robot description · canonical spec

Robot Model & Joint Map

The confirmed joint ordering, the joint_id → leg mapping, and the full robot model description. One numbering, used everywhere — firmware, telemetry, URDF, sim, dashboard, docs.

drag to orbit · scroll to zoom
LOADING LIMIT

Status

The joint ordering, joint names, and joint_id → leg/joint mapping on this page are CONFIRMED and canonical (ratified 2026-09-04). Every consumer uses exactly this table — there is no alternative numbering.

Geometric constants marked pending (link lengths, limits, masses) are current best estimates pending final vendor data. The joint mapping itself is final.

Joint ordering rules confirmed

Twelve joints, IDs 1–12. The ID is a deterministic index derived from a fixed two-level rule:

Derivation:

joint_id = 1 + 3 × leg_index + joint_index
  leg_index:    fl = 0,  fr = 1,  rl = 2,  rr = 3
  joint_index:  hip = 0, knee = 1, ankle = 2

Checks: fl_hip = 1 · fr_ankle = 1 + 3 + 2 = 6 · rl_knee = 1 + 6 + 1 = 8 · rr_ankle = 1 + 9 + 2 = 12. ✓

Master joint map confirmed

joint_idJoint nameLegJointMotion
1fl_hipfront-lefthipthigh swing, sagittal plane
2fl_kneefront-leftkneethigh → shank bend
3fl_anklefront-leftankleshank → foot pitch
4fr_hipfront-righthipthigh swing, sagittal plane
5fr_kneefront-rightkneethigh → shank bend
6fr_anklefront-rightankleshank → foot pitch
7rl_hiprear-lefthipthigh swing, sagittal plane
8rl_kneerear-leftkneethigh → shank bend
9rl_anklerear-leftankleshank → foot pitch
10rr_hiprear-righthipthigh swing, sagittal plane
11rr_kneerear-rightkneethigh → shank bend
12rr_anklerear-rightankleshank → foot pitch

Name grammar confirmed

Joint names are always {leg}_{joint}:

All 12 names are derivable from the ordering rules above; the joint name and the joint_id always agree per the master map. Any code, message, or log that carries a joint_id (1–12) is decoded with that table, and any that carries a name is spelled exactly as shown (lowercase, underscore).

Body frame canonical

AxisDirection
+xforward (nose)
+yright (left side is −y)
+zup

Hip origins & foot rest positions verified

Hip joint origin in base_link (m) — directly above the foot rest position:

LegHip xHip yFoot z (rest)
FL0.130−0.105−0.185 m (−185 mm)
FR0.130+0.105−0.185 m
RL0.240−0.105−0.185 m
RR0.240+0.105−0.185 m

Foot rest positions, body frame (mm): FL (130, −105, −185) · FR (130, +105, −185) · RL (240, −105, −185) · RR (240, +105, −185).

Per-joint kinematics

ParameterValueStatus
L1 (hip → knee, thigh)0.100 mpending — provisional split of the 185 mm rest reach
L2 (knee → ankle, shank)0.085 mpending — L1 + L2 = 0.185 m matches documented foot drop
Ankle → footfixed joint, not actuatedconfirmed — chain is hip → knee → ankle → foot
Joint axes(0, 1, 0) all legscanonical (body frame above)

Limits / effort / velocity per joint type (same for all four legs):

JointLimit (rad)Limit (°)Max effortMax velocity
hip±1.20±68.8°10 N·m10 rad/s
knee±1.57±89.9°10 N·m10 rad/s
ankle±0.60±34.4°5 N·m10 rad/s

All limit values are provisional estimates pending final vendor data (rated motor torque is 5 N·m per the GIM6010-8 datasheet in Robot).

Telemetry wire mapping canonical

Commands (JointTarget) and samples (JointSample) both address joints by the joint_id from the master map above. There is no second numbering.

FieldTypeMeaning
id / joint_iduint1–12, decoded per the master map (fl_hiprr_ankle)
pos / pos_radfloattarget/actual angle, rad, zero pose per body frame
vel / vel_radsfloattarget/actual angular velocity, rad/s
kp, kdfloatPD gains
max_tqfloatper-joint torque clamp, N·m
tq_nnm, cur_a, temp_c, flags, fault_codesample-side telemetry (50 Hz)

URDF structure canonical

Files: limit.urdf (expanded), limit.urdf.xacro (parameterized source), generate.py (regenerator: python3 generate.py).

URDF jointTypeParent → Child
{leg}_hiprevolutebase_link{leg}_hip
{leg}_kneerevolute{leg}_hip{leg}_thigh
{leg}_anklerevolute{leg}_thigh{leg}_shank
{leg}_footfixed{leg}_shank{leg}_foot

Note: each revolute joint shares its name with its child link (e.g. joint fl_hip drives link fl_hip). The ankle actuator is joint {leg}_ankle; the foot itself is a passive link carried by fixed joint {leg}_foot.

/joint_states names (ROS): fl_hip, fl_knee, fl_ankle, fr_hip, fr_knee, fr_ankle, rl_hip, rl_knee, rl_ankle, rr_hip, rr_knee, rr_ankle.

Gait pairing (trot)

Trot drives the two diagonal leg pairs in alternation:

Single source of truth

This page is the canonical specification, mirrored 1:1 in the URDF repository README (limit_urdf/README.md). Downstream artifacts must stay in lockstep:

ArtifactUses this spec for
generate.py / limit.urdf / limit.urdf.xacrolink & joint names, order, geometry
limit_msgs (JointTarget / JointSample)joint_id 1–12 semantics
sim_node.cppsynthetic joint numbering
Dashboard (dash.js)joint_id → leg/axis rendering
limit_docs/ sitepublic documentation

Change process: propose the change here first (with date + rationale), then update all downstream artifacts in the same commit. A mapping change breaks every consumer simultaneously — never edit one artifact alone.

Usage

# regenerate URDF after any parameter edit
python3 generate.py

# ROS 2
ros2 launch robot_state_publisher robot_state_publisher.launch.xml \
  robot_description:="$(command cat limit.urdf)"
# joint_state_publisher_gui feeds /joint_states (fl_hip, fl_knee, …)

rviz: RobotModel first; swap box/cylinder visuals for per-link meshes when per-link GLTF exports exist (the current AssemblyAnimated.gltf is one skinned mesh and cannot be split per URDF link).

Verified facts & sources