ROS topic is not published yet

5.5k Views Asked by At

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

1

There are 1 best solutions below

0
On BEST ANSWER

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:

  • In C++ the callback queue is associated to a particular node and this queue has to be processed by calling the blocking ros::spin() once or repeatedly calling ros::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.
  • In Python each subscriber is running in a different thread and therefore there the only thing that rospy.spin() does is block the main thread from returning and thus keep the subscriber threads alive. You can also replace it with a while 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 or rospy.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..

  • Functions like subscriber, publisher and calculation are never called inside your code, meaning the publisher and subscriber are never registered.
  • ros.Rate.sleep() and rospy.spin() have nothing to do inside the publisher anc calculation 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 like self.line_pos and not the subscribers like self.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 and ros.spin() does not work in combination. In C++ there is ros::spinOnce() but there is no equivalent in Python. You therefore need to use while and rospy.Rate.sleep(d) instead!
  • rospy.Rate.sleep requires a duration as first input argument and can't be left void.
  • You include a lot of libraries your code actually does not need.

I tried to restructure it and the following code works:

#!/usr/bin/env python

import rospy
from std_msgs.msg import Float32
from math import cos,sin

class exact_pose():
  def __init__(self):
    # Set default values
    self.car_yaw = 0.0
    self.line_pose = 0.0

    # Initialise node
    rospy.init_node("emergency_procedure", anonymous=True)
    r = rospy.Rate(1)

    # Register topics
    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_pose)
    self.exact_pose_pub = rospy.Publisher('exact_pose',Float32, queue_size=10)

    # Repeat the following code in a loop
    while not rospy.is_shutdown():
      # Perform some calculation and publish to topic
      self.exact_pose = self.line_pose * cos(self.car_yaw) + self.line_pose * sin(self.car_yaw)
      info1 = Float32()
      info1.data = self.exact_pose
      self.exact_pose_pub.publish(info1)
      # Sleep for some time
      rospy.Rate.sleep(r)
    return;

  def callback_car_yaw(self, data):
    # Copy data to class
    self.car_yaw = data
    return;

  def callback_line_pose(self,data):
    # Copy data to class
    self.line_pose = data
    return;

if __name__ == '__main__':
  try:
    # Create instance of class when node is called
    e = exact_pose()
  except rospy.ROSInterruptException:
    pass

Put this code inside your code into your .py-file, open a new console and source the workspace with source devel/setup.bash, then start the roscore, open another console, source the workspace again and launch your Python node rosrun <package> <node>.py. Finally launch another console, source the workspace again and output the topic published by the Python node rostopic echo /exact_pose. If no other node is running this should output you 0.0 every second.