[Rosject] ROS2 Basics Python Real Robot Project Part 2

Hi, I am currently trying to do the Part 2 of ROS2 Basics Python Real Robot Project .
I am trying to move forward & detect laser in a loop until the distance to wall is less them 0.3m.
Here’s my code…

    def __init__(self):
        ......
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        self.subscriber = self.create_subscription(
            LaserScan, '/scan', self.update_scan, QoSProfile(depth=10, reliability=ReliabilityPolicy.BEST_EFFORT))
    ......
    def update_scan(self, msg):  # LaserScan
        self.get_logger().info(f'XXXXXX {self.laser_f}')
        self.laser_f = min(msg.ranges[170:189])
    def CustomService_callback(self, request, response):
        # The callback function recives the self class parameter,
        # received along with two parameters called request and response
        # - receive the data by request
        # - return a result as response
        # create a Twist message
        msg = Twist()
        self.get_logger().info(f'----A {self.laser_f}')
        if self.laser_f > 0.3:
            self.get_logger().info('GO~~~!!')
            msg.linear.x = 0.3
            msg.angular.z = 0.0
            self.publisher_.publish(msg)
        while self.laser_f > 0.3:
            self.get_logger().info(f"----B {self.laser_f}")
            pass
        self.get_logger().info('Found~~~!!')
        msg.linear.x = 0.0
        msg.angular.z = 0.0
        self.publisher_.publish(msg)

        # response state
        response.wallfound = True
        # response.message = "Found wall"

        # return the response parameter
        return response

and launch the service with the command

ros2 service call /find_wall custom_interfaces/FindWall '{}'

My question is that the subscriber never triggered if the loop start in CustomService_callback (while self.laser_f > 0.3)
What should I do to return the service response after completing a series of motor controling and laster detecting

[find_wall_entry-1] [INFO] [1644569403.775852341] [findwall_server]: XXXXXX 0.7479755282402039
[find_wall_entry-1] [INFO] [1644569404.093707290] [findwall_server]: XXXXXX 0.774036705493927
[find_wall_entry-1] [INFO] [1644569404.391379378] [findwall_server]: ----A 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.394096942] [findwall_server]: GO~~~!!
[find_wall_entry-1] [INFO] [1644569404.396846474] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.399629648] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.401853411] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.403750555] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.405304375] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.406490269] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.407652039] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.408416130] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.409121474] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.409691427] [findwall_server]: ----B 0.7601828575134277

Hi @smith.lai , welcome to the community!

I don’t quite understand your issue. What is the problem? Is the subscriber to scan not working?
It looks like in the output the program did enter the condition you made of

Hi @roalgoal,
Thank you for the reply.
I suppose the result should be like

[find_wall_entry-1] [INFO] [1644569403.775852341] [findwall_server]: XXXXXX 0.7479755282402039
[find_wall_entry-1] [INFO] [1644569404.093707290] [findwall_server]: XXXXXX 0.774036705493927
[find_wall_entry-1] [INFO] [1644569404.391379378] [findwall_server]: ----A 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.394096942] [findwall_server]: GO~~~!!
[find_wall_entry-1] [INFO] [1644569404.396846474] [findwall_server]: ----B 0.7601828575134277
[find_wall_entry-1] [INFO] [1644569404.399629648] [findwall_server]: ----XXXXXX  0.676018285751
[find_wall_entry-1] [INFO] [1644569404.401853411] [findwall_server]: ----B 0.676018285751
[find_wall_entry-1] [INFO] [1644569404.399629648] [findwall_server]: ----XXXXXX  0.56760182857
[find_wall_entry-1] [INFO] [1644569404.405304375] [findwall_server]: ----B 0.56760182857
......
.......

Howerver the self.subscriber (i.e. update_scan() or “XXXXXX”) stop updating the self.laser_f data after the loop start

        while self.laser_f > 0.3:
            self.get_logger().info(f"----B {self.laser_f}")
            pass

The requirement of Part II is to receive a msg, do something, then make a response.
I think I have to make a loop to make sure everything is done before return a response, but it seems the loop blocks the self.subscriber.

This is the problem I’m having.

This is probably because you haven’t created your service client as asynchronous.

Please check

4.3 Synchronous vs. Asynchronous Service clients in ROS2

in the course to make sure you are implementing an asynchronous service.

Just to let you know you can access the courses notebooks through rosjects by clicking on the green notebook icon on the bottom menu bar.

Hi, @roalgoal:
Thank you for the reply.

I am currently at the Part II step1:


Step 1: Create the ROS service server

When the service is called, the robot must do the following behavior:

  1. Identify which laser ray is the shortest. Assume that that is the one pointing to a wall.
  2. Rotate the robot until the front of the robot is facing the wall. This can be done by rotating the robot until ray 0 is the smaller one.
  3. Move the robot forward until ray 0 is shorter than 0.3m.
  4. Now, rotate the robot again until ray number 270 of the laser range is pointing to the wall.
  5. At this point, assume that the robot is situated to start moving along the wall.
  6. Return the service message with a True.

How to test the service

  • Launch the service server node
  • Use the terminal to test the service with a service call command
ros2 service call /find_wall std_srvs/Empty '{}'

Step1 is only service server part, no client code is involved, and Service server is triggerred from basic terminal command.

I want to move robot and receive laser scanner info at the same time to adjust its location (or rotation) until it matches the condition. That’s why I need a loop to control this before response.

Did I misunderstand the project requirement?

I will appreciate your prompt reply.

I now understand where you are in the project, sorry about that.

So I assume your server makes the robot rotate but it doesn’t allow to stay subscribed to the laser.

Can you share the whole script? It is difficult to understand only with snippets.

Also, make sure you are setting up your service and topic subscribers callbacks as a ReentrantCallbackGroup() and then in your main you can setup a multi threaded executor with 3 threads:

executor = MultiThreadedExecutor(num_threads=3)

You can check this post for more info:

Thank you! I think what I need is how to apply the multithread in ROS2.
Here is my code after your suggestion.

class FindwallService(Node):

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

        # call the class constructor to initialize the node as service_stop
        super().__init__('findwall_server')
        # create the service server object
        # defines the type, name and callback function
        self.group1 = ReentrantCallbackGroup() 
        self.group2 = ReentrantCallbackGroup()
        self.group3 = ReentrantCallbackGroup()
        self.srv = self.create_service(
            Empty, 'find_wall', self.CustomService_callback, callback_group=self.group1)
        # 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, callback_group=self.group2)
        self.subscriber = self.create_subscription(
            LaserScan, '/scan', self.update_scan, QoSProfile(depth=10, reliability=ReliabilityPolicy.BEST_EFFORT), callback_group=self.group3)
        self.laser_f = 0

    def update_scan(self, msg):  # LaserScan
        self.get_logger().info(f'XXXXXX {self.laser_f}')
        self.laser_f = min(msg.ranges[170:189])

    def CustomService_callback(self, request, response):
        # The callback function recives the self class parameter,
        # received along with two parameters called request and response
        # - receive the data by request
        # - return a result as response
        # create a Twist message
        msg = Twist()
        self.get_logger().info(f'----A {self.laser_f}')
        if self.laser_f > 0.3:
            self.get_logger().info('GO~~~!!')
            msg.linear.x = 0.3
            msg.angular.z = 0.0
            self.publisher_.publish(msg)
        while self.laser_f > 0.3:
            self.get_logger().info(f"----B {self.laser_f}")
            time.sleep(1)
            pass
        self.get_logger().info('Found~~~!!')
        msg.linear.x = 0.0
        msg.angular.z = 0.0
        self.publisher_.publish(msg)

        # response state
        # response.wallfound = True
        # response.message = "Found wall"

        # return the response parameter
        return response


def main(args=None):
    # initialize the ROS communication
    rclpy.init(args=args)

    # # declare the node constructor
    # service = FindwallService()
    # # pause the program execution, waits for a request to kill the node (ctrl+c)
    # rclpy.spin(service)
    # # shutdown the ROS communication
    # rclpy.shutdown()
    try:
        # declare the node constructor
        findwall_service = FindwallService()

        executor = MultiThreadedExecutor(num_threads=3)
        executor.add_node(findwall_service)

        try:
            # pause the program execution, waits for a request to kill the node (ctrl+c)
            executor.spin()
        finally:
            executor.shutdown()
            findwall_service.destroy_node()

    finally:
        # shutdown the ROS communication
        rclpy.shutdown()


if __name__ == '__main__':
    main()
1 Like