Rosject of ROS2 Basics in 5 Days(Python) course

Hello community,

I did not find the category for the ROS2 Basics Python, so I just post the question here.
I am currently working on the rosject of ROS2 Basics Python, and I encounter some problems in Topic 2 Services.

The requirement is to build a service that finds the nearest wall. So first I use a subscriber to get laser scan values. Then when the service is called, I publish a message to rotate the robot to the wall side. However, it seems like the while loop that I use to check if the robot faces the wall blocks the subscriber thread. Therefore the laser value is not updated and the robot will keep rotating.

I know maybe I should use something like unblocking wait in the while loop. But I am not sure if it is the right way to do it.

Below is the code I have at the moment. And I am wondering do you provide the solution for this rosject?

Please help. Thanks!

# import the Empty module from std_servs service interface
from std_srvs.srv import Empty
# import the Twist module from geometry_msgs messages interface
from geometry_msgs.msg import Twist
# import the ROS2 python client libraries
import rclpy
from rclpy.node import Node
from custom_interfaces.srv import Wall
from sensor_msgs.msg import LaserScan
from rclpy.qos import ReliabilityPolicy, QoSProfile
from time import sleep



class Service(Node):

    def __init__(self):
        # Here we have the class constructor

        # call the class constructor to initialize the node as service_moving
        super().__init__('find_wall_service')
        # create the service server object
        # defines the type, name and callback function
        self.srv = self.create_service(Wall, 'findWall', self.Empty_callback)
        # create the publisher object
        # in this case the publisher will publish on /cmd_vel topic with a queue size of 10 messages.
        # use the Twist module
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        self.subscriber = self.create_subscription(LaserScan, '/scan', self.scan_update, QoSProfile(depth=10, reliability=ReliabilityPolicy.BEST_EFFORT))
        # prevent unused variable warning
        self.subscriber

        self.laser_front = 0
        self.laser_right = 0
        self.nearest_idx = 0


    def scan_update(self, msg):
        self.laser_right = msg.ranges[90]
        self.laser_front = msg.ranges[180]
        self.nearest_idx = msg.ranges.index(min(msg.ranges))
        # The logger stops printing the message after the service is called, so I assumed this part is not running
        self.get_logger().info(f'smallest index = {self.nearest_idx}')
        

    def Empty_callback(self, request, response):
        msg = Twist()

        if self.nearest_idx < 170:
            msg.angular.z = -0.3
            self.get_logger().info('Turn right to find wall')
        elif self.nearest_idx > 190:
            msg.angular.z = 0.3
            self.get_logger().info('Turn left to find wall')

        self.publisher_.publish(msg)

        while True:
            if self.nearest_idx > 170 and self.nearest_idx < 190:
                msg.angular.z = 0.0
                self.publisher_.publish(msg)
                self.get_logger().info('Wall found')
                break
            # it seems like the sleep is blocking the thread
            sleep(0.1)
        
        return response


def main(args=None):
    # initialize the ROS communication
    rclpy.init(args=args)
    # declare the node constructor
    moving_service = Service()
    # pause the program execution, waits for a request to kill the node (ctrl+c)
    rclpy.spin(moving_service)
    # shutdown the ROS communication
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Hi,

You are not doing nothing wrong. This is something that happens in ROS2. Multi threading is not built in by default as in ROS1, which generates this behavior.

Each node class only has ONE thread by default, which generates a blocking situation when you have more than one callback running at the same time.

To fix this you have to use multi threading . Here I leave you an example of how it could be done:

multithreading_ros2_example.py

1 Like

Thank you for the trick! You save my day :grinning:
It works perfectly now.