I have this code which receives data from node (subscribe) and then inside this node I do some mathematical operation, then I want to publish the data to a certain topic.
#!/usr/bin/env python
import rospy
from std_msgs.msg import String, Float32
from math import cos,sin
import sys
import os
import logging
from types import *
import time
class exact_pose():
def callback_car_yaw(self, data):
self.car_yaw = data
def callback_line_pose(self,data):
self.line_pose = data
def init(self):
self.car_yaw = None
self.line_pose = None
def subscriber(self):
self.car_yaw_sub = rospy.Subscriber('~car/yaw',Float32, self.callback_car_yaw)
self.line_pose_sub = rospy.Subscriber('~line_pose',Float32, self.callback_line_detect_xL15)
def publisher(self):
self.exact_pose_pub = rospy.Publisher('exact_pose',Float32, queue_size=10)
rospy.Rate(1)
rospy.spin()
#sloving for exact pose
def calculation(self,):
while not rospy.is_shutdown():
self.exact_pose = self.line_pose_sub * cos(self.car_yaw_sub) + self.line_pose_sub * sin(self.car_yaw_sub)
info1 = Float32()
info1.data = self.exact_pose
self.exact_pose_pub.publish(info1)
rospy.Rate.sleep()
rospy.spin()
if __name__ == '__main__':
rospy.init_node('exact_pose', anonymous=True)
try:
ne = exact_pose()
except rospy.ROSInterruptException: pass
so what the code does, subscribe to a topic from a previous node, then do some mathematical operation for these data inside the topics, then publish the last result on separate Node
When requiring asynchronous communication, like in subscribers, multiple threads running in parallel will have to be involved. ROS uses callbacks in order to hide multi-threading required for subscribers from its users. When a new message arrives these callbacks will be added to a callback queue (whose maximum size can be defined for each publisher/subscriber) and then be processed. The way this works differs between C++ and Python:
ros::spin()
once or repeatedly callingros::spinOnce()
. The processing of these callback queue happens sequentially by means of a spinner thread but you can deploy multiple spinner threads to increase the throughput as well as use different node handles that process different subscribers. A more detailed explanation can be found here.rospy.spin()
does is block the main thread from returning and thus keep the subscriber threads alive. You can also replace it with awhile
loop as explained here.In ROS2 the concept is similar but it is termed executors and further extends it with so called callback groups.
This means that you actually have to register the subscribers and publisher with their callbacks inside a initialisation routine and then you keep your node alive for the desired time, either with a
while
loop orrospy.spin()
.There is though no hard guarantee that all messages are actually processed. If your throughput is not sufficient (if your callbacks are executed too slowly) you will fill up the queue and eventually start dropping messages. If that is not what you need then have a look at the other communication methods - ROS services and actions. The other communication methods are though a bit more complicated.
Your code has several errors. You are lucky that Python is translated and not compiled and it never entered the relevant code parts..
subscriber
,publisher
andcalculation
are never called inside your code, meaning the publisher and subscriber are never registered.ros.Rate.sleep()
androspy.spin()
have nothing to do inside thepublisher
anccalculation
routines. They are used to put the thread into an endless loop as long as ROS is running. This happens once inside a program only and that should be the main loop for clarity!exact_pose
has actually to use the local values/class members inside the class likeself.line_pos
and not the subscribers likeself.line_pose_sub
. The subscribers are only executed when they receive new data. You can't force a subscriber to check for new data the way you seem to try it!while
andros.spin()
does not work in combination. In C++ there isros::spinOnce()
but there is no equivalent in Python. You therefore need to usewhile
androspy.Rate.sleep(d)
instead!rospy.Rate.sleep
requires a duration as first input argument and can't be left void.I tried to restructure it and the following code works:
Put this code inside your code into your
.py
-file, open a new console and source the workspace withsource devel/setup.bash
, then start theroscore
, open another console, source the workspace again and launch your Python noderosrun <package> <node>.py
. Finally launch another console, source the workspace again and output the topic published by the Python noderostopic echo /exact_pose
. If no other node is running this should output you0.0
every second.