Python callback attribute error - python

I have written a python ROS node to subscribe to two different topics and uses the ApproximateTimeSynchroniser to basically to match the messages via their timestamp and bundle these messages into one callback routine. If I use the callback to simply print out the two messages received it works fine.
However, I would like to populate a geometry_msgs/Pose message using the x and y positions from the received Pose 2D data in the callback routine.Using the code below, I have done this by making an empty Pose object in the callback routine:
#!/usr/bin/env python
import message_filters
import rospy
from laser_line_extraction.msg import LineSegmentList
from geometry_msgs.msg import Pose, Pose2D
from std_msgs.msg import Int32, Float32
rospy.init_node('simul-subscriber')
def callback(Pose2D, LineSegmentList):
pose = Pose()
pose.position.x = Pose2D.position.x
pose.position.y = Pose2D.position.y
pose.position.z = 0
#print(Pose2D, LineSegmentList, pose)
print(pose)
print(LineSegmentList)
line_segment_sub = message_filters.Subscriber('/line_segments', LineSegmentList)
pose_2D_sub = message_filters.Subscriber('/pose2D',Pose2D)
ts = message_filters.ApproximateTimeSynchronizer([line_segment_sub, pose_2D_sub], 10, 0.1, allow_headerless=True)
ts.registerCallback(callback)
rospy.spin()
Unfortunately, this gives a weird error when running this node:
[ERROR] [1535552711.709928, 1381.743000]: bad callback: <bound method Subscriber.callback of <message_filters.Subscriber object at 0x7f7af3cee9d0>>
Traceback (most recent call last):
File "/opt/ros/kinetic/lib/python2.7/dist-packages/rospy/topics.py", line 750, in _invoke_callback
cb(msg)
File "/opt/ros/kinetic/lib/python2.7/dist-packages/message_filters/__init__.py", line 75, in callback
self.signalMessage(msg)
File "/opt/ros/kinetic/lib/python2.7/dist-packages/message_filters/__init__.py", line 57, in signalMessage
cb(*(msg + args))
File "/opt/ros/kinetic/lib/python2.7/dist-packages/message_filters/__init__.py", line 287, in add
self.signalMessage(*msgs)
File "/opt/ros/kinetic/lib/python2.7/dist-packages/message_filters/__init__.py", line 57, in signalMessage
cb(*(msg + args))
File "/home/elisabeth/catkin_ws/src/hector_quadrotor/hector_quadrotor_demo/python_scripts/simul-subscriber.py", line 14, in callback
pose.position.x = Pose2D.position.x
AttributeError: 'LineSegmentList' object has no attribute 'position'
This is odd as the position attribute is only referenced for the Pose 2D and not the LineSegmentList msg. I suspect this is most a python issue than a ROS issue, any help somebody could give would be much appreciated!
I am following the example found at http://wiki.ros.org/message_filters where they took two different topics image and cameraInfo and passed them into the callback function
1 import message_filters
2 from sensor_msgs.msg import Image, CameraInfo
3
4 def callback(image, camera_info):
5 # Solve all of perception here...
6
7 image_sub = message_filters.Subscriber('image', Image)
8 info_sub = message_filters.Subscriber('camera_info', CameraInfo)
9
10 ts = message_filters.TimeSynchronizer([image_sub, info_sub], 10)
11 ts.registerCallback(callback)
12 rospy.spin()

You confuse class names with object names.
The fix to your program should be straight forward.
I've changed Pose2D to pose2d, and LineSegmentList to linesegmentlist where it was necessary and comented it with # --- Change ...:
#!/usr/bin/env python
import message_filters
import rospy
from laser_line_extraction.msg import LineSegmentList
from geometry_msgs.msg import Pose, Pose2D
from std_msgs.msg import Int32, Float32
rospy.init_node('simul-subscriber')
# --- CHANGE: using proper object names instread of class names
def callback(pose2d, linesegmentlist):
pose = Pose()
# --- CHANGE: using the object
pose.position.x = pose2d.position.x
pose.position.y = pose2d.position.y
pose.position.z = 0
#print(pose2d, linesegmentlist, pose)
print(pose)
print(linesegmentlist)
line_segment_sub = message_filters.Subscriber('/line_segments', LineSegmentList)
pose_2D_sub = message_filters.Subscriber('/pose2D',Pose2D)
ts = message_filters.ApproximateTimeSynchronizer([line_segment_sub, pose_2D_sub], 10, 0.1, allow_headerless=True)
ts.registerCallback(callback)
rospy.spin()

Related

How to know actual result of `proc_output.waitFor` if `expected_output` is wrong?

I am trying to follow a ROS2 testing tutorial which tests a topic listener to understand how ROS2 testing works. Here is a screenshot of the related code at 21:15
I have a node target_control_node which subscribes the topic turtle1/pose and then move the turtle to a new random pose.
import math
import random
import rclpy
from geometry_msgs.msg import Twist
from rclpy.node import Node
from turtlesim.msg import Pose
class TargetControlNode(Node):
def __init__(self):
super().__init__("target_control_node")
self.get_logger().info("target_control_node")
self._target_pose = None
self._cmd_vel_publisher = self.create_publisher(Twist, "turtle1/cmd_vel", 10)
self.create_subscription(Pose, "turtle1/pose", self.subscribe_target_pose, 10)
self.create_timer(1.0, self.control_loop)
def subscribe_target_pose(self, msg):
self._target_pose = msg
def control_loop(self):
if self._target_pose is None:
return
target_x = random.uniform(0.0, 10.0)
target_y = random.uniform(0.0, 10.0)
dist_x = target_x - self._target_pose.x
dist_y = target_y - self._target_pose.y
distance = math.sqrt(dist_x**2 + dist_y**2)
msg = Twist()
# position
msg.linear.x = 1.0 * distance
# orientation
goal_theta = math.atan2(dist_y, dist_x)
diff = goal_theta - self._target_pose.theta
if diff > math.pi:
diff -= 2 * math.pi
elif diff < -math.pi:
diff += 2 * math.pi
msg.angular.z = 2 * diff
self._cmd_vel_publisher.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = TargetControlNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
I am trying to write a simple test for the subscription part based on the tutorial above to understand how it works.
Here is my initial test code. Note inside I am using expected_output=str(msg), however, it is wrong, and I am not sure what to put there.
import pathlib
import random
import sys
import time
import unittest
import uuid
import launch
import launch_ros
import launch_testing
import pytest
import rclpy
import std_msgs.msg
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
#pytest.mark.rostest
def generate_test_description():
src_path = pathlib.Path(__file__).parent.parent
target_control_node = launch_ros.actions.Node(
executable=sys.executable,
arguments=[src_path.joinpath("turtle_robot/target_control_node.py").as_posix()],
additional_env={"PYTHONUNBUFFERED": "1"},
)
return (
launch.LaunchDescription(
[
target_control_node,
launch_testing.actions.ReadyToTest(),
]
),
{
"target_control_node": target_control_node,
},
)
class TestTargetControlNodeLink(unittest.TestCase):
#classmethod
def setUpClass(cls):
rclpy.init()
#classmethod
def tearDownClass(cls):
rclpy.shutdown()
def setUp(self):
self.node = rclpy.create_node("target_control_test_node")
def tearDown(self):
self.node.destroy_node()
def test_target_control_node(self, target_control_node, proc_output):
pose_pub = self.node.create_publisher(Pose, "turtle1/pose", 10)
try:
msg = Pose()
msg.x = random.uniform(0.0, 10.0)
msg.y = random.uniform(0.0, 10.0)
msg.theta = 0.0
msg.linear_velocity = 0.0
msg.angular_velocity = 0.0
pose_pub.publish(msg)
success = proc_output.waitFor(
# `str(msg)` is wrong, however, I am not sure what to put here.
expected_output=str(msg), process=target_control_node, timeout=1.0
)
assert success
finally:
self.node.destroy_publisher(pose_pub)
When I run launch_test src/turtle_robot/test/test_target_control_node.py, it only prints this without telling me what is actual output:
[INFO] [launch]: All log files can be found below /home/parallels/.ros/log/2023-01-02-16-37-27-631032-ubuntu-linux-22-04-desktop-1439830
[INFO] [launch]: Default logging verbosity is set to INFO
test_target_control_node (test_target_control_node.TestTargetControlNodeLink) ... [INFO] [python3-1]: process started with pid [1439833]
[python3-1] [INFO] [1672706247.877402445] [target_control_node]: target_control_node
FAIL
======================================================================
FAIL: test_target_control_node (test_target_control_node.TestTargetControlNodeLink)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/my-ros/src/turtle_robot/test/test_target_control_node.py", line 91, in test_target_control_node
assert success
AssertionError
----------------------------------------------------------------------
Ran 1 test in 1.061s
FAILED (failures=1)
[INFO] [python3-1]: sending signal 'SIGINT' to process[python3-1]
[python3-1] Traceback (most recent call last):
[python3-1] File "/my-ros/src/turtle_robot/turtle_robot/target_control_node.py", line 59, in <module>
[python3-1] main()
[python3-1] File "/my-ros/src/turtle_robot/turtle_robot/target_control_node.py", line 53, in main
[python3-1] rclpy.spin(node)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/__init__.py", line 222, in spin
[python3-1] executor.spin_once()
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 705, in spin_once
[python3-1] handler, entity, node = self.wait_for_ready_callbacks(timeout_sec=timeout_sec)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 691, in wait_for_ready_callbacks
[python3-1] return next(self._cb_iter)
[python3-1] File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 588, in _wait_for_ready_callbacks
[python3-1] wait_set.wait(timeout_nsec)
[python3-1] KeyboardInterrupt
[ERROR] [python3-1]: process has died [pid 1439833, exit code -2, cmd '/usr/bin/python3 /my-ros/src/turtle_robot/turtle_robot/target_control_node.py --ros-args'].
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
I checked the source code of waitFor, but still no clue.
Is there a way to print the actual output so that I can give correct expected_output? Thanks!
To answer your question(and give some more general tips): You can always print out the msg inside the node. That said, the reason you're getting an error is because msg is a ros message type, meaning it's an object. So by doing str(msg) your expected output will be something like <object of type Pose at (some_address)>; and not the actual message values. Also since it appears you're just testing a subscriber, it doesn't usually make sense to have a unit test that's expecting stdout. I would also note that you're publishing a message before actually waiting for the result, this should always fail. The reason is because by the time your subscriber is started, the message has already been published and is gone.
Finally, you shouldn't have a publisher included in any unit tests. It adds an extra dependency and since it's a prepackaged transport layer testing if it works is irrelevant. Instead to fix your problem you should simply be calling the individual methods of your node directly. Basically import the script, instantiate the object, and don't try to deal with the whole node lifecycle.
Edit based on comments
Looking at your subscriber code, you'll need to actually print something out. Unfortunately because it's not a std_msg(i.e. it has more fields than just .data) you'll need to decide how you want to go about confirming the data is right. You could simply look at one field or all of them in order. For example you might have in your test:
success = proc_output.waitFor(
expected_output=str(msg.x),
process=target_control_node, timeout=1.0)
And in your control node:
def subscribe_target_pose(self, msg):
self._target_pose = msg
print(msg.x)
That said, this IO handling method doesn't seem like the best to me. Mainly because it relies on stdout which isn't something you always want.

Calling a class method from a different file using Schedule Module in Python

I'm trying to call a method from a class that is in a different file. This is how my code looks:
main.py
###Imports
import funct as f
import schedule
import time
###More Imports
###Grabbing GPS data from user and assigning values
bboxsize = f.find(userLat, userLng, userRad)
if __name__ == '__main__':
r = f.find.radar(bboxsize) ### <---- Line 14
schedule.every(5).seconds.do(r)
while True:
schedule.run_pending()
time.sleep(1)
funct.py
###Imports
class find:
def __init__(self, userLat, userLng, userRad):
self.userLat = userLat
self.userLng = userLng
self.userRad = userRad
def bboxFormula(self):
### Lots of code that is not important to this
return bboxsize
def radar(self, bboxsize):
api = OpenSkyApi(username='###', password='###')
states = api.get_states(bbox=bboxsize)
print(f"vvvvvv| {time.ctime(time.time())} |vvvvvv")
print("------------------------------------------")
for s in states.states:
print("Callsign: %r, Latitude: %r, Longitude: %r, Altitude (meters): %r, Velocity %r)" % (s.callsign, s.latitude, s.longitude, s.geo_altitude, s.velocity))
print("------------------------------------------")
When running this I get:
Traceback (most recent call last):
File "/##/##/##/##/main.py", line 14, in <module>
r = f.find.radar(bboxsize)
TypeError: find.radar() missing 1 required positional argument: 'bboxsize'
I can run all of this in one file without classes, so I know it works. I have been messing with this for a while getting all sorts of errors with every change I make.
Is there something that I'm missing or is there no way for me to do this with the Schedule Module?
I’m just guessing here, but I think you had different code when it was all one file.
Now with two files I think you meant to write this:
find = f.find(userLat, userLng, userRad)
boxsize = find.bboxFormula()
if __name__ == '__main__':
r = lambda :find.radar(bboxsize) ### <---- Line 14
schedule.every(5).seconds.do(r)
while True:
schedule.run_pending()
time.sleep(1)

exec vs import, why errors only in one of them

I downloaded the code from here https://github.com/SpaceNetChallenge/SpaceNet_SAR_Buildings_Solutions, specifically using model 1. I downloaded the weights corresponding and created the following file to load the model and test. First, I copy the Unet part in main.py into a separate file umodel.py and the test file as follows
import torch
exec(open("./umodel.py").read())
network_data = torch.load('snapshot_fold_8_best')
print(network_data.keys())
import sys
sys.path.append("geffnet")
class Namespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
args = Namespace(extra_num = 1,
dec_ch = [32, 64, 128, 256, 1024],
stride=32,
net='b5',
bot1x1=True,
glob=True,
bn=True,
aspp=True,
ocr=True,
aux_scale=True)
def load_state_dict(model, state_dict):
missing_keys = []
# from UnetOS.umodel import Unet
exec(open("./umodel.py").read())
try:
from torch.hub import load_state_dict_from_url
except ImportError:
from torch.utils.model_zoo import load_url as load_state_dict_from_url
# from UnetOS.umodel import *
model = Unet(extra_num = args.extra_num, dec_ch = args.dec_ch, stride=args.stride, net=args.net, bot1x1 = args.bot1x1, glob=args.glob, bn=args.bn, aspp=args.aspp,
ocr=args.ocr, aux = args.aux_scale > 0).cuda()
load_state_dict(model, network_data)
My question is, why exec(open("./umodel.py").read()) works nicely but whenever I tried to import from umodel import Unet it has errors
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_10492/1282530406.py in <module>
9 # ah
10 model = Unet(extra_num = args.extra_num, dec_ch = args.dec_ch, stride=args.stride, net=args.net, bot1x1 = args.bot1x1, glob=args.glob, bn=args.bn, aspp=args.aspp,
---> 11 ocr=args.ocr, aux = args.aux_scale > 0).cuda()
12 #model = Unet()
13 #print(network_data.key())
D:\hines\Pretrained\1-zbigniewwojna\UnetOS\umodel.py in __init__(self, extra_num, dec_ch, stride, net, bot1x1, glob, bn, aspp, ocr, aux)
238 ['ir_r4_k5_s2_e6_c192_se0.25'],
239 ['ir_r1_k3_s1_e6_c320_se0.25']]
--> 240 enc = GenEfficientNet(in_chans=3, block_args=decode_arch_def(arch_def, depth_multiplier),
241 num_features=round_channels(1280, channel_multiplier, 8, None), stem_size=32,
242 channel_multiplier=channel_multiplier, act_layer=resolve_act_layer({}, 'swish'),
NameError: name 'decode_arch_def' is not defined
The main file is as follow https://github.com/SpaceNetChallenge/SpaceNet_SAR_Buildings_Solutions/blob/master/1-zbigniewwojna/main.py
From the error message, it appears that decode_arch_def is not available and looking at your imports, that has to come from from geffnet.efficientnet_builder import * (it does https://github.com/rwightman/gen-efficientnet-pytorch/blob/master/geffnet/efficientnet_builder.py)
Your exec must have worked because it followed a similar import, that brought decode_arch_def in scope - exec() executes code in the current scope, so it will work because in that scope decode_arch_def is already defined.
However, when you import, the imported module itself doesn't have the function you need in scope. You should add the required import statements to the file you're importing to bring them into scope and it should work.
For example, with a mod.py containing this:
def my_func():
print(datetime.now())
This works:
from datetime import datetime
exec(open("./mod.py").read())
my_func()
But this does not:
from datetime import datetime
import mod
mod.my_func()
To make that work, mod.py would have to be:
from datetime import datetime
def my_func():
print(datetime.now())
And the import of datetime wouldn't be needed in the main program, since it's not referenced there. Your code has a similar issue - you need to determine all the dependencies of your Unet class and import them.

How to save <class "ctypes.c_char_Array> with pickle

I'm doing research on palmprint recognition. for that I use the edcc library for the introduction of the palmprint. but i have problem in saving the encoding result from palm. I want to save the encoding result into a file, but I get an error like below
Traceback (most recent call last):
File "/home/pi/Coba/PalmDetection/PalmRecognition.py", line 18, in <module>
pickle.dump(one_palmprint_code, config_dictionary_file)
_pickle.PicklingError: Can't pickle <class 'ctypes.c_char_Array_849'>: attribute lookup c_char_Array_849 on ctypes failed
My code like this :
import os
import edcc
import cv2
import pickle
TEST_PALMPRINT_DATA_DIR = "/home/pi/Coba/PalmDetection/Data"
TEST_A_01_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "Palm1.jpeg")
#TEST_A_02_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "a_02.bmp")
TEST_B_01_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "palme.jpeg")
#TEST_B_02_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "b_02.bmp")
if __name__ == "__main__":
config = edcc.EncoderConfig(29, 5, 5, 10)
encoder = edcc.create_encoder(config)
one_palmprint_code = encoder.encode_using_file(TEST_A_01_PALMPRINT_IMAGE)
with open('encode.encode', 'wb') as config_dictionary_file:
pickle.dump(one_palmprint_code, config_dictionary_file)
another_palmprint_code = encoder.encode_using_file(TEST_B_01_PALMPRINT_IMAGE)
similarity_score = one_palmprint_code.compare_to(another_palmprint_code)
print(
"{} <-> {} similarity score:{}".format(
TEST_A_01_PALMPRINT_IMAGE, TEST_B_01_PALMPRINT_IMAGE, similarity_score
)
)
What should i do?
The edcc module must use ctypes internally, but really should hide that fact instead of returning a ctypes-wrapped object. A ctypes.c_char_Array_849 is just a C-compatible wrapper around an array of bytes. You can access the equivalent Python bytes object via the .raw property (what edcc should return instead) and write that to the file:
import ctypes
import pickle
one_palmprint_code = (ctypes.c_char * 849)()
with open('encode.encode', 'wb') as config_dictionary_file:
#pickle.dump(one_palmprint_code, config_dictionary_file) # reproduces error
config_dictionary_file.write(one_palmprint_code.raw)

Python unittesting initiate values

Sorry if this question is stupid. I created an unittest class which needs to take given inputs and outputs from outside. Thus, I guess these values should be initiated. However, I met some errors in the following code:
CODE:
import unittest
from StringIO import StringIO
##########Inputs and outputs from outside#######
a=[1,2]
b=[2,3]
out=[3,4]
####################################
def func1(a,b):
return a+b
class MyTestCase(unittest.TestCase):
def __init__(self,a,b,out):
self.a=a
self.b=b
self.out=out
def testMsed(self):
for i in range(self.tot_iter):
print i
fun = func1(self.a[i],self.b[i])
value = self.out[i]
testFailureMessage = "Test of function name: %s iteration: %i expected: %i != calculated: %i" % ("func1",i,value,fun)
self.assertEqual(round(fun,3),round(value,3),testFailureMessage)
if __name__ == '__main__':
f = MyTestCase(a,b,out)
from pprint import pprint
stream = StringIO()
runner = unittest.TextTestRunner(stream=stream, verbosity=2)
result = runner.run(unittest.makeSuite(MyTestCase(a,b,out)))
print 'Tests run', result.testsRun
However, I got the following error
Traceback (most recent call last):
File "C:testing.py", line 33, in <module>
result = runner.run(unittest.makeSuite(MyTestCase(a,b,out)))
File "C:\Python27\lib\unittest\loader.py", line 310, in makeSuite
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
File "C:\Python27\lib\unittest\loader.py", line 50, in loadTestsFromTestCase
if issubclass(testCaseClass, suite.TestSuite):
TypeError: issubclass() arg 1 must be a class
Can anyone give me some suggestions? Thanks!
The root of the problem is this line,
result = runner.run(unittest.makeSuite(MyTestCase(a,b,out)))
unittest.makeSuite expects a class, not an instance of a class. So just MyTestCase, not MyTestCase(a, b, out). This means that you can't pass parameters to your test case in the manner you are attempting to. You should probably move the code from init to a setUp function. Either access a, b, and out as globals inside setUp or take a look at this link for information regarding passing parameters to a unit test.
By the way, here is the source file within python where the problem originated. Might be informative to read.

Categories

Resources