Un Action Client Mínimo
El Código¶
Revisa el código (incluyendo las anotaciones) y luego toma una copia de él.
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient # (1)!
from tuos_interfaces.action import CameraSweep # (2)!
class CameraSweepActionClient(Node):
def __init__(self):
super().__init__("camera_sweep_action_client") # (3)!
self.actionclient = ActionClient(
node=self,
action_type=CameraSweep,
action_name="camera_sweep"
) # (4)!
self.declare_parameters(
namespace='',
parameters=[
('goal_images', 0),
('goal_angle', 0)
]
) # (5)!
def send_goal(self): # (6)!
images = self.get_parameter(
'goal_images'
).get_parameter_value().integer_value
angle = self.get_parameter(
'goal_angle'
).get_parameter_value().integer_value
goal = CameraSweep.Goal() # (7)!
goal.sweep_angle = float(angle)
goal.image_count = images
self.actionclient.wait_for_server() # (8)!
# send the goal to the action server:
return self.actionclient.send_goal_async(goal)
def main(args=None): # (9)!
rclpy.init(args=args)
action_client = CameraSweepActionClient()
future = action_client.send_goal()
rclpy.spin_until_future_complete(action_client, future)
if __name__ == '__main__':
main()
-
Como ya sabes, para desarrollar nodes de ROS usando Python necesitamos importar la librería cliente
rclpyy la claseNodeen la que basar nuestro node. Además, aquí también importamos una claseActionClient. -
Sabemos que el Action Server
/camera_sweepusa la interfaceactionCameraSweepdel paqueterigsa_interfaces, así que la importamos aquí también (la cual usamos para hacer una llamada al servidor). -
Práctica estándar al inicializar nodes de ROS: debemos darles un nombre.
-
Aquí instanciamos un objeto de la clase
ActionClient. Al hacer esto definimos elnodeal que se agrega el action client (en nuestro casoself, es decir, nuestra claseCameraSweepActionClient). Luego también definimos el tipo de interface utilizado por el servidor (CameraSweep) y el nombre del action que queremos llamar (action_name="camera_sweep"). -
Aquí estamos declarando dos parámetros de ROS:
goal_imagesygoal_angle.Los usaremos para establecer goals para el action server en tiempo de ejecución.
Por defecto, estos valores están configurados en
0, ¡así que si no establecemos explícitamente valores para estos dos parámetros, permanecerán en0!Question
¿Cómo establecemos valores para los parámetros en tiempo de ejecución (es decir, cuando ejecutamos este node usando
ros2 run)? -
Aquí definimos un método de clase para construir y enviar un goal al servidor.
-
Como sabemos de antes, un
CameraSweep.Goal()contiene dos parámetros a los que podemos asignar valores:sweep_angleyimage_count.Como se indicó arriba, los valores asignados a estos provienen de dos parámetros de ROS:
goal_angleygoal_images.Recuerda
Por defecto, ambos parámetros tendrán un valor de
0a menos que les asignemos explícitamente un valor (ver arriba).¿Cómo asignamos valores a estos parámetros en tiempo de ejecución? Recuerda cómo lo hicimos en la Parte 4.
-
El goal se envía al servidor usando el método
send_goal_async(), que devuelve un future: es decir, algo que sucederá en el futuro y en lo que podemos esperar. Este future se devuelve una vez que los parámetros del goal han sido aceptados por el servidor, no cuando el action server ha completado realmente su tarea. -
En nuestro método
maininicializamosrclpyy nuestra claseCameraSweepActionClient(nada nuevo aquí), pero luego llamamos al métodosend_goal()de nuestra clase (como se discutió arriba), que devuelve un future. Entonces podemos usar el métodorclpy.spin_until_future_complete()para activar nuestro node solo hasta que este objeto future haya terminado.
Dependencias del Paquete¶
El action client tiene dos dependencias clave, por lo que necesitamos modificar el archivo package.xml (debajo de la línea <exec_depend>rclpy</exec_depend>) para incluirlas: