ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

How to publish topics at different rates using while loop?

asked 2021-02-17 07:41:28 -0600

Redhwan gravatar image

updated 2021-02-18 00:36:27 -0600

I'd like to fix the hz for one topic control to control of the robot at 30 HZ.

I am using object detection in my work, the hz of the video's topic /usb_cam/image_raw is between 15 and 25.

Note: I'd like to using while loop in my work.

When I run this code, the hz above 100 for control topic, it is not 30 as I wanted. I am using the command : rostopic hz /control to measure of hz.

How to fix it at 30 hz?

Please help me.

Thanks in advince!

Full the code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
import rospy
import cv2
from std_msgs.msg import String, Float32MultiArray, Bool, Float32
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image, PointCloud2

class Detector:
    def __init__(self):
        self.pubtracking = rospy.Publisher('control', Float32MultiArray, queue_size=1)
        rospy.Timer(rospy.Duration(1 / 30), self.control)

        self._bridge = CvBridge()
        # rospy.Subscriber("/camera/rgb/image_color", Image, self.image_callback)
        # rospy.Subscriber("/camera/rgb/image_raw", Image, self.image_callback)
        rospy.Subscriber("/usb_cam/image_raw", Image, self.image_callback)
        # rospy.Subscriber('/camera/depth/points', PointCloud2, self.pc_callback)
        # rospy.Subscriber('stopback', Bool, self.shutdownlistener)
        self._current_image = None
        self._current_pc = None
        # self.array = 0
        self.error_v = 0
        self.error_w = 0


    def image_callback(self, image):
        self._current_image = image
    def run(self):
        global error_v, error_w, array
        while not rospy.is_shutdown():
            if self._current_image is not None:
                try:
                    scene = self._bridge.imgmsg_to_cv2(self._current_image, "bgr8")
                    self.error_v = 2
                    self.error_w = 1
                    # hello_str = [error_v, error_w]
                    # array = Float32MultiArray(data=hello_str)
                    self.control()

                    cv2.imshow("image", scene)
                    key = cv2.waitKey(1)
                except CvBridgeError as e:
                    print(e)

    def control(self, event=None):
        # error_v = 2
        # error_w = 1
        hello_str = [self.error_v, self.error_w]
        print hello_str
        array = Float32MultiArray(data=hello_str)
        self.pubtracking.publish(array)
        # rospy.Timer(rospy.Duration(1 / 30), self.control)
        # rospy.spin()





if __name__ == '__main__':
    rospy.init_node('dodo_detector_ros', log_level=rospy.INFO)
    try:
        tr = Detector()
        tr.run()
        # rospy.spin()
        # tr.control()
    except KeyboardInterrupt:
        rospy.loginfo('Shutting down')

I need it works like this code but using while loop, I don't know if it is possible or not in ROS?

Here I put hs = 50

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
import rospy
import cv2
from std_msgs.msg import String, Float32MultiArray, Bool, Float32
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image, PointCloud2

class Detector:
    def __init__(self):
        self.pubtracking = rospy.Publisher('control', Float32MultiArray, queue_size=1)
        rospy.Timer(rospy.Duration(1 / 50), self.control)

        self._bridge = CvBridge()
        # rospy.Subscriber("/camera/rgb/image_color", Image, self.image_callback)
        # rospy.Subscriber("/camera/rgb/image_raw", Image, self.image_callback)
        rospy.Subscriber("/usb_cam/image_raw", Image, self.image_callback)
        # rospy.Subscriber('/camera/depth/points', PointCloud2, self.pc_callback)
        # rospy.Subscriber('stopback', Bool, self.shutdownlistener)
        self._current_image = None
        self._current_pc = None
        # self.array = 0
        self.error_v = 0
        self.error_w = 0


    def image_callback(self, image):
        self._current_image = image
    # def run(self):
        global error_v, error_w, array
        # rate = rospy.Rate(30)
        # while not rospy.is_shutdown():
        if self._current_image is not None ...
(more)
edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
0

answered 2021-02-18 00:56:47 -0600

Redhwan gravatar image

It works fine now

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
import rospy
import cv2
from std_msgs.msg import String, Float32MultiArray, Bool, Float32
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image, PointCloud2

class Detector:
    def __init__(self):
        self.pubtracking = rospy.Publisher('control', Float32MultiArray, queue_size=1)
        rospy.Timer(rospy.Duration(1 / 50), self.control)

        self._bridge = CvBridge()
        # rospy.Subscriber("/camera/rgb/image_color", Image, self.image_callback)
        # rospy.Subscriber("/camera/rgb/image_raw", Image, self.image_callback)
        rospy.Subscriber("/usb_cam/image_raw", Image, self.image_callback)
        # rospy.Subscriber('/camera/depth/points', PointCloud2, self.pc_callback)
        # rospy.Subscriber('stopback', Bool, self.shutdownlistener)
        self._current_image = None
        self._current_pc = None
        # self.array = 0
        self.error_v = 0
        self.error_w = 0


    def image_callback(self, image):
        self._current_image = image
    def run(self):
        global error_v, error_w, array
        rate = rospy.Rate(30)
        while not rospy.is_shutdown():
            if self._current_image is not None:
                try:
                    scene = self._bridge.imgmsg_to_cv2(self._current_image, "bgr8")
                    self.error_v = 2
                    self.error_w = 1
                    # hello_str = [error_v, error_w]
                    # array = Float32MultiArray(data=hello_str)
                    # self.control()

                    cv2.imshow("image", scene)
                    key = cv2.waitKey(1)
                    rate.sleep()
                except CvBridgeError as e:
                    print(e)

    def control(self, event=None):
        # error_v = 2
        # error_w = 1
        hello_str = [self.error_v, self.error_w]
        print hello_str
        array = Float32MultiArray(data=hello_str)
        self.pubtracking.publish(array)
        # rospy.Timer(rospy.Duration(1 / 30), self.control)
        # rospy.spin()





if __name__ == '__main__':
    rospy.init_node('dodo_detector_ros', log_level=rospy.INFO)
    try:
        tr = Detector()
        tr.run()
        # rospy.spin()
        # tr.control()
    except KeyboardInterrupt:
        rospy.loginfo('Shutting down')
edit flag offensive delete link more

Comments

I'm glad you got it working, but it should be noted that you're no longer using the while loop to control the control publisher timing; it's being handled by the rospy.Timer.

tryan gravatar image tryan  ( 2021-02-18 08:34:49 -0600 )edit

First of all, I'd like to thank you a lot. Your help inspired me to solve it, although I had tried before and gave up. In fact, I have many rostopic publishers are dependent on hz of while loop, and I had wanted to make only rostopic control will be independent.

Redhwan gravatar image Redhwan  ( 2021-02-18 21:08:20 -0600 )edit
0

answered 2021-02-17 08:29:18 -0600

tryan gravatar image

Your question title made me think you wanted to publish multiple topics at various rates, in which case timers (roscpp, rospy) are probably better than a while loop. Since you specified using the loop, we'll move on with that.

Are you trying to publish one topic (/control) at one specific rate (30 Hz)? If so, you need to add something to make the loop take the specified time, which won't be exact--especially with waitKey() in there. As is, your code specifies a timer that triggers control() at your desired rate, but you also call control() from inside the while loop. To use the while loop for timing, remove the rospy.Timer() and use rospy.Rate(). From the tutorial:

rate = rospy.Rate(10) # 10hz
while not rospy.is_shutdown():
    hello_str = "hello world %s" % rospy.get_time()
    rospy.loginfo(hello_str)
    pub.publish(hello_str)
    rate.sleep()

Since your loop is in run(), the sleep should go at the end of that loop:

def run(self):
    global error_v, error_w, array
    rate = rospy.Rate(30)
    while not rospy.is_shutdown():
        if self._current_image is not None:
            try:
                scene = self._bridge.imgmsg_to_cv2(self._current_image, "bgr8")
                self.error_v = 2
                self.error_w = 1
                # hello_str = [error_v, error_w]
                # array = Float32MultiArray(data=hello_str)
                self.control()

                cv2.imshow("image", scene)
                key = cv2.waitKey(1)
            except CvBridgeError as e:
                print(e)
        rate.sleep()

Again, this will be best effort timing--not exact. The sleep will try to vary its length to take up the rest of the desired loop period.

edit flag offensive delete link more

Comments

I did as you suggested, but it is not work.

Redhwan gravatar image Redhwan  ( 2021-02-17 18:41:45 -0600 )edit

You'll have to elaborate. Did you get an error/warning, or did it behave unexpectedly?

tryan gravatar image tryan  ( 2021-02-17 20:00:44 -0600 )edit

It works fine if you put rate.sleep() under key = cv2.waitKey(1)

Redhwan gravatar image Redhwan  ( 2021-02-18 00:55:52 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2021-02-17 07:41:28 -0600

Seen: 738 times

Last updated: Feb 18 '21