Contenido detallado
🔹 ROS2 vs ROS1: DDS, tiempo real, seguridad
ROS2 reescribió el middleware sobre DDS (Data Distribution Service): comunicación descentralizada (sin roscore único), QoS configurable, compatible con tiempo real y seguridad (SROS2). Los bloques son nodos que se comunican mediante tópicos (pub/sub), servicios (req/resp) y acciones (objetivos largos con feedback).
📊 Primitivas de comunicación
- Tema — flujo continuo, muchos a muchos (p. ej., imágenes, articulaciones).
- Servicio — una única llamada síncrona (ej.: «encender gripper»).
- Acción — objetivo de larga duración con feedback/cancelación (ej.: «mover a pose»).
- Parámetro — configuración en runtime por nodo.
⚡ Consejo práctico
Use tópico para flujo continuo (imágenes, articulaciones), servicio para llamadas puntuales y acción para «mover a la pose X» — donde necesitas feedback sobre el progreso y poder cancelar. Elegir la primitiva incorrecta (por ejemplo, un tópico para un único goal) genera código frágil.
DDS
Transporte descentralizado.
Nodo
Unidad de proceso.
Acción
Goal largo + feedback.
SROS2
Seguridad y autenticación.
🔹 Anatomía de un sistema: percepción, planificación, control
Un sistema ROS2 descompone el robot en nodos con una sola responsabilidad: drivers de cámara/RGB-D, nodo de percepción, planificador, controlador de articulaciones. Esta modularidad permite cambiar la "policy" sin modificar el driver del brazo —exactamente donde entra el VLA.
📊 Nodos típicos de una célula VLA
- /camera — el driver RealSense publica RGB + depth.
- /vla_node — inferencia de la policy, publica acciones.
- /robot_state_publisher + /tf2 — árbol de frames.
- /controller_manager — ros2_control ejecuta la trayectoria.
# inspecionar o sistema vivo
ros2 node list
ros2 topic list
ros2 topic hz /camera/color/image_raw # checar frequência
ros2 topic echo /joint_states --once
ros2 run rqt_graph rqt_graph # visualizar o grafo
⚡ Consejo práctico
Antes de escribir código, mapea el grafo con rqt_graph y consulta la hz de cada tema. Cámara a 60 Hz, pero /joint_states a 10 Hz se convierte en un cuello de botella silencioso en la sincronización.
Percepción
Nodo de visión/RGB-D.
Planificación
VLA o MoveIt2.
Control
ros2_control / driver.
Modularidad
Cambiar de policy de forma aislada.
🔹 Integrar un VLA: nodo de inferencia, sincronización, QoS
El VLA se convierte en un nodo rclpy que conecta la cámara y el estado, ejecuta el forward y publica el comando. La imagen y el estado deben venir del mismo instante: usa message_filters (ApproximateTimeSynchronizer) y elige la QoS cierta — BEST_EFFORT para imagen (descarta las atrasadas), RELIABLE para comandos.
import rclpy
from rclpy.node import Node
from message_filters import Subscriber, ApproximateTimeSynchronizer
from sensor_msgs.msg import Image, JointState
from rclpy.qos import QoSProfile, ReliabilityPolicy
class VLANode(Node):
def __init__(self):
super().__init__("vla_node")
qos = QoSProfile(depth=1, reliability=ReliabilityPolicy.BEST_EFFORT)
img = Subscriber(self, Image, "/camera/color/image_raw", qos_profile=qos)
st = Subscriber(self, JointState, "/joint_states")
sync = ApproximateTimeSynchronizer([img, st], queue_size=2, slop=0.02)
sync.registerCallback(self.on_obs)
def on_obs(self, img, state):
action = self.policy(preprocess(img), state.position) # forward VLA
self.pub.publish(to_cmd(action)) # ação chunked
rclpy.init(); rclpy.spin(VLANode())
✓ Hacer
- ✓Sincronizar imagen+estado por timestamp (slop pequeño).
- ✓QoS BEST_EFFORT/depth=1 para sensores de alta tasa.
- ✓Ejecuta la inferencia en un thread/grupo de callbacks separado.
✗ Evitar
- ✗Emparejar QoS incompatibles (RELIABLE vs BEST_EFFORT): no se conecta.
- ✗Bloquear el executor con un forward pesado en el callback principal.
- ✗Usar imagen y estado de instantes diferentes.
rclpy
API Python de ROS2.
message_filters
Sincronización por timestamp.
QoS
Confiabilidad/profundidad.
callback group
Concurrencia segura.
🔹 tf2 y frames: pose del modelo → comando del brazo
El VLA predice una pose en un frame, generalmente de la cámara. El brazo espera comandos en el frame de la base. tf2 mantiene el árbol de transformaciones temporizado (base_link → camera_link → tool0) y convierte poses entre frames respetando la marca de tiempo.
from tf2_ros import Buffer, TransformListener
import tf2_geometry_msgs
buf = Buffer(); TransformListener(buf, node)
# transformar a pose prevista (frame da câmera) -> frame base do braço
pose_base = buf.transform(pose_cam, "base_link", timeout=Duration(seconds=0.1))
# agora a pose está no frame que o controlador entende
📊 Errores clásicos de frame
- Mala calibración mano-ojo — una cámara↔base incorrecta desplaza todo.
- Timestamp incorrecto — transformar con TF antiguo/futuro provoca un salto.
- Convención de ejes — REP-103: x hacia adelante, z hacia arriba.
tf2
Árbol de frames.
Mano-ojo
Calibración cámara↔base.
REP-103
Convención de ejes.
Timestamp
TF temporizado.
🔹 MoveIt2 y controladores: trayectoria, límites, seguridad
MoveIt2 planifica el movimiento con verificación de colisiones, cinemática (IK/FK) y parametrización temporal respetando los límites de las articulaciones. En VLA reactivo de alta frecuencia, muchas veces ignoras el planner y envías setpoints directamente vía ros2_control — pero MoveIt2 se incorpora para una aproximación segura y la detección de colisiones.
Planificar (MoveIt2)
OMPL/Pilz genera una trayectoria libre de colisiones; útil para el movimiento de aproximación.
Controlar (ros2_control)
JointTrajectoryController ejecuta; VLA puede inyectar setpoints reactivos.
Proteger
Límites de velocidad/torque, parada de emergencia por hardware, límites del espacio de trabajo.
MoveIt2
Planificación + colisión.
ros2_control
Capa del controlador.
IK/FK
Cinemática.
Parada de emergencia
Seguridad física.
🔹 Latencia y tiempo real: del frame al torque, jitter, watchdogs
La cadena cámara → inferencia → tf2 → controlador → torque tiene un presupuesto de latencia. Peor que una latencia alta es el jitter (varianza): un bucle irregular desestabiliza el control. Usa el kernel PREEMPT_RT, prioridad de tiempo real y watchdogs que detienen el brazo si el comando se retrasa.
# watchdog: se não chegar comando em 100ms, freia
self.create_timer(0.1, self.watchdog)
def watchdog(self):
if (self.now() - self.last_cmd) > 0.1:
self.hold_position() # fail-safe
# medir latência fim-a-fim do pipeline
ros2 topic delay /arm_command
⚡ Consejo práctico
Action chunking (de M2.1) es tu aliado: predecir K pasos amortiza la inferencia y tolera el jitter de la red ROS, manteniendo suave el loop de torque aunque el VLA vuelva a inferir solo cada N ciclos.
Latencia
Frame → torque.
Jitter
Varianza del loop.
PREEMPT_RT
Kernel en tiempo real.
Watchdog
A prueba de fallas mediante timeout.
✓ Hacer
- ✓Medir la latencia de extremo a extremo y el jitter (p99), no solo el promedio.
- ✓Watchdog que frena el brazo si el comando se retrasa.
- ✓Action chunking para tolerar el jitter de la red ROS.
✗ Evitar
- ✗Ciclo de control sin kernel RT en una tarea crítica.
- ✗Enviar torque sin timeout/límite de seguridad.
- ✗Optimizar la latencia media ignorando los picos de jitter.
✅ Resumen del módulo
Próximo módulo
2.5 — Hardware: brazos, pinzas, sensores RGB-D y compute de borde Jetson, el cuerpo donde se ejecuta ROS2.