Part I: The ROS 2 stack · §3 of 43
Selecting the model
The launch files pick the xacro only from arm_type:
arm_type value | Xacro loaded |
|---|---|
v1.0, v10, v1_0, openarm_v1.0, openarm_v10, openarm_v1_0 | openarm_description/assets/robot/openarm_v1.0/urdf/openarm_v10.urdf.xacro |
v2.0, v20, … (the default!) | …/openarm_v2.0/urdf/openarm_v20.urdf.xacro |
- The
description_filelaunch argument exists but is ignored. - Both launch files default to v2.0. Always pass
arm_type:=v10for your arms.
Verify what is loaded:
ros2 param get /robot_state_publisher robot_description | grep -o -m1 "openarm_v1.0/urdf/openarm_v10.urdf.xacro"Joint names (bimanual)
| Arm | Arm joints | Gripper joint |
|---|---|---|
Left (ROS default can1; zeus can0) | openarm_left_joint1 … openarm_left_joint7 | openarm_left_finger_joint1 |
Right (ROS default can0; zeus can1) | openarm_right_joint1 … openarm_right_joint7 | openarm_right_finger_joint1 |
There are no un-prefixed openarm_joint1…7 names in the bimanual setup. That’s why commands copied from single-arm docs hang with “Waiting for an action server…“.
v1.0 joint limits (as actually loaded, per arm)
The limits in config/arm/joint_limits.yaml are not the ones the robot uses for joints 1 and 2: urdf/arm/openarm_arm.xacro shifts and mirrors them per arm (joint1: −2.094 rad on the left arm; joint2: ±π/2 plus mirroring). The real limits, read from the expanded URDF (xacro openarm_v10.urdf.xacro arm_type:=v10 bimanual:=true, openarm_description c103041):
| Joint | Left arm (rad) | Right arm (rad) | Left / right (°) | Max vel (rad/s) | Max effort (Nm) | Motor |
|---|---|---|---|---|---|---|
| joint1 | −3.491 … +1.396 | −1.396 … +3.491 | −200…+80 / −80…+200 | 16.75 | 40 | DM8009 |
| joint2 | −3.316 … +0.175 | −0.175 … +3.316 | −190…+10 / −10…+190 | 16.75 | 40 | DM8009 |
| joint3 | −1.571 … +1.571 | same | ±90 | 5.45 | 27 | DM4340 |
| joint4 | 0.0 … 2.443 | same | 0…140 | 5.45 | 27 | DM4340 |
| joint5 | −1.571 … +1.571 | same | ±90 | 20.94 | 7 | DM4310 |
| joint6 | −0.785 … +0.785 | same | ±45 | 20.94 | 7 | DM4310 |
| joint7 | −1.571 … +1.571 | same | ±90 | 20.94 | 7 | DM4310 |
| finger_joint1 | 0.0 (closed) … 0.044 m (open) | same | DM4310 (gripper) |
(The raw joint_limits.yaml gives joint1 −1.396…+3.491 and joint2 ±1.745 for both arms. That’s what guide versions V1–V3 listed, and it’s wrong for the left joint1 and both joint2s.)
The gripper joint is prismatic, in metres: 0.0 = closed, 0.044 = fully open. MoveIt’s named states: closed = 0, half closed/half_closed = 0.022.
Nothing enforces these limits on raw commands
Neither the trajectory controller nor the mock hardware checks them. Check every hand-written target against this table, for the right arm. Joint2 has only 10° of room inward (toward the body) on each arm, and joint4 (elbow) can’t go negative. The zeus helper scripts (§8.7) check them for you.
Which way each joint moves (direction map)
Computed from the expanded URDF at the zero pose (arms hanging), and confirmed on the real robot for all 14 arm joints (log 2026-10-10). Forward = the direction the robot faces (the way the elbows bend). Left/right = the robot’s own left/right: stand behind the robot, facing the way it faces. Valid for small moves from the hanging pose; once other joints have moved, each axis moves with the arm.
| Joint | What it is | Left arm: + moves… | Right arm: + moves… |
|---|---|---|---|
| joint1 | shoulder, forward/back | hand backward | hand forward |
| joint2 | shoulder, sideways | hand inward (toward the body) | hand outward |
| joint3 | upper-arm twist | clockwise seen from above | clockwise seen from above |
| joint4 | elbow | forearm forward/up | forearm forward/up |
| joint5 | forearm twist | clockwise seen from above | clockwise seen from above |
| joint6 | wrist, sideways | hand outward | hand inward |
| joint7 | wrist, forward/back | hand backward | hand forward |
| finger_joint1 | gripper | 0 = closed, 0.044 = open | same |
The arms are mirror images: joints 1, 2, 6 and 7 mean the opposite on the two arms. Joints 3 and 5 turn the same way in the room, which is mirrored in body terms. Joint4 is the same on both. ”+” is never “forward” by definition; it’s set by each joint’s axis in the model.
Robot frame (world): x forward, y = robot’s left, z up. At zero the hands are at (0, ±0.153, 0.159) m and the shoulders at z ≈ 0.70 m.
Frames
world→body_link0(the torso/pillar) →openarm_left_link0/openarm_right_link0, mounted atxyz = 0 ±0.031 0.698withrpy = ∓1.5708 0 0.- The arms are mirrored, so the same joint angle moves the left and right arms in mirrored directions for some joints (see the direction map above). Test each arm separately.
robot_state_publisherpublishes/tffrom/joint_states. If/joint_statesis missing (nojoint_state_broadcaster), RViz shows “No transform” for every moving link.