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

Selecting the model

The launch files pick the xacro only from arm_type:

arm_type valueXacro loaded
v1.0, v10, v1_0, openarm_v1.0, openarm_v10, openarm_v1_0openarm_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_file launch argument exists but is ignored.
  • Both launch files default to v2.0. Always pass arm_type:=v10 for 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)

ArmArm jointsGripper joint
Left (ROS default can1; zeus can0)openarm_left_joint1 … openarm_left_joint7openarm_left_finger_joint1
Right (ROS default can0; zeus can1)openarm_right_joint1 … openarm_right_joint7openarm_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):

JointLeft 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…+20016.7540DM8009
joint2−3.316 … +0.175−0.175 … +3.316−190…+10 / −10…+19016.7540DM8009
joint3−1.571 … +1.571same±905.4527DM4340
joint40.0 … 2.443same0…1405.4527DM4340
joint5−1.571 … +1.571same±9020.947DM4310
joint6−0.785 … +0.785same±4520.947DM4310
joint7−1.571 … +1.571same±9020.947DM4310
finger_joint10.0 (closed) … 0.044 m (open)sameDM4310 (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.

JointWhat it isLeft arm: + moves…Right arm: + moves…
joint1shoulder, forward/backhand backwardhand forward
joint2shoulder, sidewayshand inward (toward the body)hand outward
joint3upper-arm twistclockwise seen from aboveclockwise seen from above
joint4elbowforearm forward/upforearm forward/up
joint5forearm twistclockwise seen from aboveclockwise seen from above
joint6wrist, sidewayshand outwardhand inward
joint7wrist, forward/backhand backwardhand forward
finger_joint1gripper0 = closed, 0.044 = opensame

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 at xyz = 0 ±0.031 0.698 with rpy = ∓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_publisher publishes /tf from /joint_states. If /joint_states is missing (no joint_state_broadcaster), RViz shows “No transform” for every moving link.