A less-asyncronous way of using ROS2 action clients?
Hello, I am a little while into developing my first ROS2 project as someone totally new to ROS. My last couple of posts may provide some useful context of how I arrived at this issue but essentially my 'robot' has a set of actions and services for reading sensors or operating actuators, and then I use nodes without a real defined ROS type (such as client) which I have been calling 'drivers' to then put all the needed clients and subscribers needed to perform an operation into. I like this design because by just using some additional protocol within the server to identify the clients it is fairly easy to control access so that multiple drivers(clients) can use the same service or action in a coordinated manner as not to conflict. I am not sure if there's any issues with this approach, I would be happy to hear them, but it just seemed like the best approach I could come up with on my own.
This is working just fine when it comes to service clients in the 'drivers' but I am looking for a 'less-asynchronous' way of using the action clients than what the tutorial demonstrates, using something like the spin_until_future_complete method but I have not been successful in getting the response back.
The operation would look something like this:
1) move to position 1 (action)
2) read a set of 3 sensors and post to topics (services)
3) move to position 2 (action)
4) read sensors again, etc...
so I do not need to do anything in this node while the action is happening, just wait for it to finish. Using service clients it looks something like this:
# set light level
def set_light(self, light_number, power_level):
# create service request
request = LightSet.Request()
request.light_number = light_number
request.power_level = power_level
request.driver_status = self.driver_status
# send request
future = self.set_light_client.call_async(request)
# recieve response
rclpy.spin_until_future_complete(self.set_light_node, future)
# processing
if(future.result() is not None):
result: LightSet.Response = future.result()
if(result.success):
self.get_logger().info('light {} set to {}'.format(light_number, result.confirm_level))
else:
self.get_logger().error('light setting failed')
else:
self.get_logger().error('light service call failed: {}'.format(future.exception()))
and my interpretation of using action clients in this manner has looked something like this but it does not work as I can only seem get ClientGoalHandle objects back and not the actual response
# move to target
def move_to_target(self, target):
# send goal to move x action server
# create goal
goal_msg = AxisControl.Goal()
goal_msg.driver_status = self.driver_status
goal_msg.target = target
# wait
self.move_x_client.wait_for_server()
# send request
future = self.move_x_client.send_goal_async(goal_msg)
# recieve response
rclpy.spin_until_future_complete(self.move_x_node, future)
# process response
if future.result() is not None:
# self.get_logger().info(future.result)
result: AxisControl.Result() = future.result().result
if(result.complete):
self.get_logger().info('move finished')
return True
else:
self.get_logger().info('move failed')
return False
Using the callback based approach everything seems to work OK but I am just finding it to be much more of a ...