Part I: The ROS 2 stack · §1 of 43

The ROS 2 stack on zeus. Orange boxes run inside one controller_manager (ros2_control_node, 750 Hz). Dotted arrows carry data.

flowchart TB
  clients["your code · ros2 CLI · rclpy<br/>MoveIt (via move_group)"]
  jtc["trajectory controllers<br/>left arm · right arm · 2 grippers"]:::cm
  lhw["left hardware interface"]:::cm
  rhw["right hardware interface"]:::cm
  jsb["joint_state_broadcaster"]:::cm
  rviz["robot_state_publisher<br/>→ /tf → RViz"]
  larm["left arm<br/>7 Damiao + gripper"]:::hw
  rarm["right arm<br/>7 Damiao + gripper"]:::hw
  clients -.->|FollowJointTrajectory| jtc
  jtc -.->|position| lhw
  jtc -.->|position| rhw
  lhw ==>|can0| larm
  rhw ==>|can1| rarm
  lhw -.->|state| jsb
  rhw -.->|state| jsb
  jsb -.->|/joint_states| rviz
  classDef cm fill:#c2410c1f,stroke:#c2410c
  classDef hw fill:#0f766e1f,stroke:#0f766e

Key facts:

  • One controller manager runs both arms. Each arm is its own ros2_control hardware component on its own CAN bus.
  • Everything is joint-space position control. The trajectory controllers write position commands. On real hardware these become Damiao MIT-mode commands (kp, kd, q_des, dq_des, tau_ff), so the motor itself runs the PD loop.
  • Sim and real use the same controllers, topics and actions. Only the hardware plugin changes (use_fake_hardware:=true/false). Anything you develop against the mock works unchanged on the robot. The real robot just has physics, gravity and the ability to hurt someone.