I am working on the ros project part 2, and my server does not start.
I have in my customer shell :“waiting for service to become available”, and nothing happends.
Here is my server
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
from findwall.srv import Findwall
class Service(Node):
def __init__(self):
# Define the class constructor intialize the node
super().__init__('find_wall')
# Define the server object
self.service = self.create_service(
Findwall, 'findwall', self.find_wall_callback)
# Define the publisher object
self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
# Define the subscriber object
self.subscriber = self.create_subscription(
LaserScan, 'scan', self.laser_callback, 10)
# Initialisation of the laser_270_degree and laser_360_degree values at 0
self.laser_270_degree = 0
self.laser_360_degree = 0
# Initialisation of the variable that will take the Twist message
self.move = Twist()
def laser_callback(self, sensor_msgs):
self.laser_270_degree = sensor_msgs.ranges[270]
self.laser_360_degree = sensor_msgs.ranges[360]
def find_wall_callback(self, request, response):
# The callback function receives the self-class parameter,
# received along with two parameters called request and response
# - receive the data by request
# - return a result as a response
# Print the degug function at the info level the information related to the laser
self.get_logger().info('I receive on the right side: "%s"' %
str(self.laser_360_degree))
# Stop the robot
self.move.linear.x = 0.0
# Check if a wall is around
if(str(self.laser_360_degree) == 'inf'):
# Robot turns to find a wall
self.move.angular.z = 0.3
else:
# Stop turning because wall found
self.move.angular.z = 0.0
# Check the distance between the wall and the robot
if(self.laser_360_degree != 0.3):
# Robot moves
self.move.linear.x = 0.5
else:
# Robot stops
self.move.linear.x = 0.0
# Rotate the robot to be parallel to the wall
if(self.laser_270_degree != 0.3):
# Rotate the robot
self.move.angular.z = 0.3
else:
# Stop the rotation of the robot
self.move.angular.z = 0.0
# Return an answer
response.wallfound = True
# Publish the robot pose in the pose topic /cmd_vel
self.publisher.publish(self.move)
return response
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
Find_wall_move = Service()
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin(Find_wall_move)
# shutdown the ROS communication
rclpy.shutdown()
if __name__ == '__main__':
main()
And I call the server with:
ros2 service call /find_wall std_srvs/Empty ‘{}’
I took the liberty to fix your issue title and category. Also added some tags.
That is the wrong format. You should instead use:
ros2 service call /find_wall custom_interfaces/srv/FindWall "{}"
After you have made the custom interface package, you do not use std_srvs anymore.
You can make use of the autocomplete feature in the command line.
After typing ros2 service call /find_wall, hit [TAB] key twice. It will give you options to choose from.
there you can choose the interface type and then hit [TAB] twice again to give you message structure options. Then you can send the message to your service server.
To check if find_wall service server is running:
user:~$ ros2 service list | grep find_wall
/find_wall
/find_wall_service_server_node/describe_parameters
/find_wall_service_server_node/get_parameter_types
/find_wall_service_server_node/get_parameters
/find_wall_service_server_node/list_parameters
/find_wall_service_server_node/set_parameters
/find_wall_service_server_node/set_parameters_atomicall
To call the service:
user:~$ ros2 service call # hit TAB key twice here to list all services
--rate /find_wall_service_server_node/set_parameters_atomically
-r /ros_bridge/describe_parameters
/find_wall /ros_bridge/get_parameter_types
/find_wall_service_server_node/describe_parameters /ros_bridge/get_parameters
/find_wall_service_server_node/get_parameter_types /ros_bridge/list_parameters
/find_wall_service_server_node/get_parameters /ros_bridge/set_parameters
/find_wall_service_server_node/list_parameters /ros_bridge/set_parameters_atomically
/find_wall_service_server_node/set_parameters
user:~$ ros2 service call /find_wall # hit TAB key twice here to list options
--rate -r custom_interfaces/srv/FindWall
user:~$ ros2 service call /find_wall custom_interfaces/srv/FindWall # hit TAB key twice here to list options
--rate -r {}\
user:~$ ros2 service call /find_wall custom_interfaces/srv/FindWall "{}" # this will call the service
I took a quick glance into your code, I have a vague feeling that you will run into callback issues. Let me know if you face any errors.
I hope this clarifies your doubt(s). Let me know if you are still unclear.
Thank you for your answer.
Yes, the ros project textbook/announcement does not give the correct command. This confused me and explains why I couldn’t find anything on the internet about it.
I will try to debug my code.
If I have any problems, I will contact you. Thanks for your help.
I changed my code, but it is exercute only one time when I call it with the ros2 service call.
I would like to know how to call it more than one time? I though about a while loop but the laser reading is not update.
How can I have access to the right laser scan values?
Thanks for your help.
Claire
Here is my code
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
from findwall.srv import Findwall
class Service(Node):
def __init__(self):
# Define the class constructor intialize the node
super().__init__('find_wall')
# Define the server object
self.service = self.create_service(
Findwall, 'findwall', self.find_wall_callback)
# Define the publisher object
self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
# Define the subscriber object
self.subscriber = self.create_subscription(
LaserScan, 'scan', self.laser_callback, 10)
# Initialisation of the laser_270_degree and laser_360_degree values at 0
self.laser_270_degree = 0
self.laser_360_degree = 0
# Initialisation of the variable that will take the Twist message
self.move = Twist()
print(0)
def laser_callback(self, sensor_msgs):
self.laser_270_degree = sensor_msgs.ranges[270]
self.laser_360_degree = sensor_msgs.ranges[359]
print(1)
def find_wall_callback(self, request, response):
while response.wallfound == False:
self.get_logger().info('I receive on the right side: "%s"' %
str(self.laser_360_degree))
# Check if a wall is around
if(self.laser_360_degree >= 0.6):
# Robot turns to find a wall
self.move.angular.z = 0.3
else:
# Stop turning because wall found
self.move.angular.z = 0.0
if(self.laser_360_degree > 0.3):
# Robot moves
self.move.linear.x = 0.1
else:
# Robot stops
self.move.linear.x = 0.0
# Rotate the robot to be parallel to the wall
if(self.laser_270_degree > 0.3):
# Rotate the robot
self.move.angular.z = 0.3
else:
# Stop the rotation of the robot
self.move.angular.z = 0.0
response.wallfound == True
# Publish the robot pose in the pose topic /cmd_vel
self.publisher.publish(self.move)
return response
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
Find_wall_move = Service()
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin(Find_wall_move)
# shutdown the ROS communication
rclpy.shutdown()
if __name__ == '__main__':
main()
Exactly what I thought! I knew this would happen in your code.
That’s why I said this in my previous post:
The solution to your problem is quite simple.
You need to use MultiThreadedExecutor with ReentrantCallbackGroup for both your scan_callback and the service_callback.
Your main() function will now transform into this version:
Thank you for your answer.
May I ask you why I need to use MultiThreaded here?
Even after reading the course about the server and the callback, I would never have thought of it.
My server code and the codes in the server unit are quite similar. Why in the server unit, threads are not used? Why here I need them?
I believe the concept of “Executors and Callbacks” comes after the Services chapter in the current course notes. So if you are strictly following the instructions in the course notes and working on Part 2 of your rosject, you may have not have much exposure to Executors and Callbacks.
So, according to the service callback design, you need laser scan callback to be running in parallel along with service callback.
In a program that does not explicitly state the use of MultiThreadedExecutor, SingleThreadedExecutor is used by default. SingleThreadedExecutor cannot efficiently handle parallel process callbacks. Therefore you need MultiThreadedExecutor.
You cannot use MutuallyExclusiveCallbackGroup either - because they execute callbacks in a sequential way, one after another. Therefore, you need ReentrantCallbackGroup to execute service callback and laser scan callback in parallel mode, not one after another, but together.
Also, if you do not implement MultiThreadedExecutor, your service will be called only once. You cannot call it again, even if the service server is online and running.
I hope this clarifies your doubt(s). Let me know if anything is still unclear.
The service callback will be executed only once when the client calls this service. Unlike scan callback, this service callback will not execute multiple times.
I think the code you have provided inside the service callback executes once and exits. You can actually add loops inside your service callback. It will not cause any problem - since service callback executes only once after being called.
Also from your output, I see a lot of 0’s but alone one single 1.
And you have print(1) in your code inside scan callback as well as in your service callback.
I am not sure which function printed the 1 in the output. I am guessing it is the service call.
Try to make more debug print statement to identify what is happening. I think the scan callback is not executing. Comment out the service declaration and service callback. Check if laser subscriber callback is functioning alone by itself.
Then add the service callback and check your output.
Remember to compile your package every time before execution. In ROS2, you must compile after any change you make in python code also.
Thank you for your answer.
I tried to add a loop in my service callback with the lasercallback but it failed.
How can I call my laser callback in the loop of my service callback?
I did not tell you to add the laser callback into your service callback. I think you have mistaken me.
I told you to check if the callbacks are working properly, individually by themselves.
Comment out the service declaration and service callback. Now execute your program and check if your scan callback works.
Now, comment out the laser callback and create a dummy service callback with some print messages. Execute your program again to check if your service callback works.
Finally merge both the callbacks once you confirm that both callbacks are working properly. Execute and check if the program works with both the callbacks.
Now modify your code to use the laser scan readings in your service callback to make the robot find a wall.
Thank you for your answer. I don’t think I understood the reason for a server.
Can you explain to me how a server works?
Will my server code be executed only once before a single request is sent and received?
Or will my server code be executed in the same way as a topic code?
That is actually explained in the course notes itself. Under the “Services” and “Actions” chapters.
Services and Actions are concepts of communication in ROS just like Topics.
You can think of servers as programs that will help the robot do a special / separate task that the robot does not frequently use in its main program.
Let’s assume that you have a humanoid robot. Among all the different tasks it can do, let’s say one of it’s tasks is to pick up any trash from the floor and drop it into the nearby trashcan.
The main program of the robot would be obstacle avoidance and room exploration. As the robot explores the room, when suddenly it comes in contact with a trash - say snacks wrapper - the robot must pick it up and drop it in the nearest trashcan. So as the robot explores the room and when it finds the trash, it will call the service that would help the robot pick up the trash. Then there would be an action call that would enable the robot to drop the trash into the trashcan and return back to its original position, where it originally picked up the trash.
Again, the above is just an example. The uses for services and actions are plenty.
Yes, you cannot make multiple calls to the same server from different nodes / programs. That will not work. However, you can call a service multiple times from the same or different node, once the service completes.
Topics are more like data pipelines. You can use many subscribers to a single publisher - or - multiple publishers to a single subscriber. Topics do not have any usage limits, other than the possibility of the data throughput becoming slower if more connections are made to the same topic.
Services and Actions only work for a short period - only when needed by the main program.
I hope this explanation is clear. Let me know if anything is still unclear.