Skip to content

B5: Comportamientos y Servicios

Servicios disponibles

ros2 service list | grep sport
# /sport/stand_up
# /sport/stand_down
# /sport/hello
# /sport/stretch
# /sport/dance1
# /sport/dance2
# /sport/recovery_stand
# /sport/balance_stand
# /sport/dock
# /sport/undock

Todos son de tipo std_srvs/Trigger — no tienen argumentos, solo devuelven éxito/fallo.

Cliente de servicio

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_srvs.srv import Trigger

class BehaviorClient(Node):
    def __init__(self):
        super().__init__('behavior_client')

    def call(self, behavior: str) -> bool:
        client = self.create_client(Trigger, f'/sport/{behavior}')
        if not client.wait_for_service(timeout_sec=3.0):
            self.get_logger().error(f'Servicio /sport/{behavior} no disponible')
            return False
        future = client.call_async(Trigger.Request())
        rclpy.spin_until_future_complete(self, future)
        result = future.result()
        self.get_logger().info(f'{behavior}: {result.message}')
        return result.success

def main():
    rclpy.init()
    node = BehaviorClient()
    node.call('hello')      # saludar
    node.call('dance1')     # bailar
    node.call('stand_up')   # ponerse de pie
    rclpy.shutdown()

Tarea

Crea un nodo que active la secuencia: hello → esperar 2 s → dance1 → recovery_stand.


Creando tu propio servidor de servicio

Un cliente llama servicios; un servidor los atiende. El mismo tipo Trigger puede usarse en ambos lados.

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from nav_msgs.msg import Odometry
from std_srvs.srv import Trigger
import math

class RobotStatusServer(Node):
    def __init__(self):
        super().__init__('robot_status_server')
        self._x = 0.0; self._y = 0.0; self._th = 0.0
        self.create_subscription(Odometry, '/odom', self._odom_cb, 10)
        # Registrar el servidor — callback se llama al recibir cada petición
        self.create_service(Trigger, '/robot/status', self._status_cb)
        self.get_logger().info('Servidor /robot/status listo')

    def _odom_cb(self, msg):
        self._x  = msg.pose.pose.position.x
        self._y  = msg.pose.pose.position.y
        q = msg.pose.pose.orientation
        self._th = math.atan2(2*(q.w*q.z + q.x*q.y), 1 - 2*(q.y**2 + q.z**2))

    def _status_cb(self, request, response):
        response.success = True
        response.message = (
            f"x={self._x:.2f} y={self._y:.2f} "
            f"hdg={math.degrees(self._th):.1f}°"
        )
        return response

def main():
    rclpy.init()
    rclpy.spin(RobotStatusServer())
    rclpy.shutdown()
# Terminal 1 — iniciar el servidor
ros2 run booster_b5_behaviors b5_status_server.py

# Terminal 2 — llamarlo desde la línea de comandos
ros2 service call /robot/status std_srvs/srv/Trigger {}

# O desde otro nodo Python
node.call('status')   # usando el BehaviorClient de la primera parte

Diferencia clave: cliente vs. servidor

Cliente Servidor
Crea con create_client(Tipo, nombre) create_service(Tipo, nombre, callback)
Inicia la comunicación Sí — llama call_async() No — espera peticiones
Necesita girar Sí — spin_until_future_complete Sí — rclpy.spin()
Callback recibe resultado (request, response)

Tarea extendida

Crea un nodo que sirva /robot/status y además suscriba /battery_state. Incluye el porcentaje de batería en la respuesta del servicio.