Actions won't work in sequence
skemp117 opened this issue · comments
Hello,
I am trying to use actions to move the robot. I only need to perform simple tasks. Turn, drive a few feet, turn. They don't need to be done quickly or with great position. Why can I not string actions together like this? For some reason I cannot run one after the other, they always hang. This is extremely frustrating, and there is very little support online for anything other than the most basic action (which both of the action clients I have work perfectly on their own). I don't really know ROS2 extremely well, but I have been programming for several years. I understand the basics of multi-threading, blocking operations, and asyncio. As far as I understand, rclpy.spin is a blocking operation (without a multi threaded executor) which is fine because I want to wait for the action client to finish before it moves on. Why is this not working?
#!/usr/bin/env python3
import rclpy
from irobot_logger.action_undock import UndockingActionClient
from irobot_logger.action_drive_distance import DriveDistanceActionClient
def main(args=None):
rclpy.init(args=args)
try:
# Initialize action clients
undock_action_client = UndockingActionClient()
future = undock_action_client.send_goal("{}")
rclpy.spin_once(undock_action_client) # i've tried spin, spin_once, and spin_until_future_complete
action_client = DriveDistanceActionClient()
dist = 1.0
speed = 0.5
future = action_client.send_goal(dist, speed)
rclpy.spin_once(action_client)
except KeyboardInterrupt:
pass
rclpy.shutdown()
if __name__ == '__main__':
main()
Here is the drive action client
import rclpy
from rclpy.action import ActionClient
from rclpy.node import Node
from irobot_create_msgs.action import DriveDistance
class DriveDistanceActionClient(Node):
'''
An example of an action client that will cause the iRobot
Create3 to drive a specific distance. Subclass of Node.
'''
def __init__(self):
'''
Purpose
-------
initialized by calling the Node constructor, naming our node
'drive_distance_action_client'
'''
super().__init__('drive_distance_action_client')
self._action_client = ActionClient(
self, DriveDistance, 'drive_distance')
def send_goal(self, distance=0.5, max_translation_speed=0.15):
'''
Purpose
-------
This method waits for the action server to be available, then sends a
goal to the server. It returns a future that we can later wait on.
'''
goal_msg = DriveDistance.Goal()
goal_msg.distance = distance
goal_msg.max_translation_speed = max_translation_speed
self._action_client.wait_for_server()
self._send_goal_future = self._action_client.send_goal_async(goal_msg)
self._send_goal_future.add_done_callback(self.goal_response_callback)
return self._send_goal_future
def goal_response_callback(self, future):
'''
Purpose
-------
A callback that is executed when the future is complete.
The future is completed when an action server accepts or rejects the goal request.
'''
goal_handle = future.result()
if not goal_handle.accepted:
self.get_logger().info('Goal rejected :(')
return
self.get_logger().info('Goal accepted :)')
self._get_result_future = goal_handle.get_result_async()
self._get_result_future.add_done_callback(self.get_result_callback)
def get_result_callback(self, future):
'''
Purpose
-------
Similar to sending the goal, we will get a future that will complete when the result is ready.
'''
result = future.result().result
self.get_logger().info('Result: {0}'.format(result))