The 12 GIM6010-8 motors are driven over two CAN buses, can0 (front legs) and can1 (rear legs), at 1 Mbps. This page covers the low-level CAN bus: the GIM6010-8 frame protocol, the node-ID mapping, the motor datasheet, and hardware bring-up. For the higher-level locomotion contract, gait, safety, and operator driving, see the Locomotion page.
GIM6010-8 CAN Mapping
Source: steady-win.txt, rev 0.9.
Physical layer
| Item | Value |
|---|---|
| Baud rate | 1 Mbps (odrv0 can.config.baud_rate) |
| Frame | Standard, 11-bit ID, 8-byte data |
| CAN ID | (node_id << 5) | cmd_id |
| Payload encoding | IEEE-754 float32, little-endian (e.g. 10.0 → 00 00 20 41) |
| Direction bits | None, direction implicit in cmd_id |
| Host socket API | Robot (L4T/Tegra kernel): socket(AF_CAN, SOCK_RAW, CAN_RAW=1), bind(("can0",)), the L4T headers number PF_CAN protocols from 1, unlike stock Linux (CAN_RAW=0, bind((ifindex, 0))). locomotion_harness/real_can.py probes both, so the same package works on the robot and on standard Linux (vcan). |
Command table
| CMD | Name | Dir | Payload |
|---|---|---|---|
| 0x001 | Heartbeat | M→H | Axis_Error, Axis_State, Motor_Flag, Encoder_Flag, Controller_Flag (periodic, default 100 ms) |
| 0x002 | Stop | H→M | N/A (stop axis) |
| 0x003 | Get_Error | M→H | Error_Type |
| 0x004 | RxSdo | M→H | N/A |
| 0x005 | TxSdo | M→H | N/A |
| 0x006 | Set_Axis_Node_ID | H→M | Axis_Node_ID |
| 0x007 | Set_Axis_State | H→M | Axis_Requested_State (8 = closed-loop control) |
| 0x009 | Get_Encoder_Estimates | M→H | Pos_Estimate, Or_Estimate (periodic, default 10 ms) |
| 0x00B | Set_Controller_Mode | H→M | Control_Mode, Input_Mode |
| 0x00C | Set_Input_Pos | H→M | Input_Pos, Well_FF (vel FF), Torque_FF |
| 0x00D | Set_Input_Vel | H→M | Input_Vel, Torque_FF |
| 0x00E | Set_Input_Torque | H→M | Input_Torque |
| 0x00F | Set_Limits | H→M | Velocity_Limit, Current_Limit |
| 0x010 | Start_Anticogging | H→M | N/A |
| 0x011 | Set_Traj_Vel_Limit | H→M | Traj_Vel_Limit |
| 0x012 | Set_Traj_Accel_Limits | H→M | Accel, Decel |
| 0x013 | Set_Traj_Inertia | H↔M | Traj_Inertia |
| 0x014 | Get_Iq | M→H | Iq_Setpoint, Iq_Measured |
| 0x015 | Get_Sensorless_Estimates | M→H | Pos_Estimate, Or_Estimate |
| 0x016 | Reboot | H→M | N/A |
| 0x017 | Get_Bus_Voltage_Current | M→H | Bus_Voltage, Bus_Current |
| 0x018 | Clear_Errors | H→M | N/A |
| 0x019 | Set_Linear_Count | H→M | Linear_Count |
| 0x01A | Set_Pos_Gain | H→M | Pos_Gain |
| 0x01B | Set_Vel_Gains | H→M | Vel_Gain, Vel_Integrator_Gain |
| 0x01C | Get_Torques | M→H | Torque_Setpoint, Torque |
| 0x01D | Get_Powers | M→H | Electrical_Power, Mechanical_Power |
| 0x01E | Disable_Can | H→M | N/A |
| 0x01F | Save_Configuration | H→M | N/A |
Drive sequences
Velocity control (alternative):
(node<<5|0x00B) [mode=2, input_mode=2] # velocity control, velocity ramp input
(node<<5|0x007) [axis_state=8] # enter closed-loop
(node<<5|0x00D) [target_vel, torque_ff=0] # per control tick
Position control (default, production path):
(node<<5|0x00B) [mode=3, input_mode=3] # position control, filtered position input
(node<<5|0x007) [axis_state=8]
(node<<5|0x00C) [target_pos, vel_ff, torque_ff]
Torque control (e-stop / limp): 0x00E with 0.0, or plain 0x002 Stop.
Periodic messages
Enable/disable and set rates via ODrive config. Out-of-box defaults: heartbeat 100 ms, encoder 10 ms. Contract requirements: heartbeat 100 ms (keep), encoder 10 ms (keep), iq_rate_ms=50 ms (enable, for current monitoring), bus_vi_rate_ms=100 ms (enable, for battery voltage monitoring).
What CHAMP does per control tick (200 Hz)
- Take the current velocity command and compute the 12 joint position targets.
- TX
Set_Input_Pos(0x00C) per motor: position target from the gait + IK loop, vel_ff / torque_ff optional. - RX periodic Heartbeat / Encoder / Get_Iq / Bus_VI frames, update motor states.
- Publish
/joint_states(positions from encoder feedback, velocities setpoints, effort = Iq_measured). - Publish
/foot_contacts(see Foot-Contact Inference). - Run the safety checks (see the Safety Contract) on every tick (do not wait for timer).
- Log all TX/RX frames to the CAN capture file (see CAN capture).
12-Motor Node-ID Table
Joint naming (verified in limitx_limit_description/urdf/leg.xacro + limitx_limit_sim/config/joints/joints.yaml): {leg}_{hip,upper_leg,lower_leg}_joint with legs lf, rf, lh, rh. Node IDs follow the wiring spec (limitx-wiring.drawio): joint_id = 1 + 3×leg + joint(0–2), legs FL=0, FR=1, RL=2, RR=3. CAN channel is the configured default split (front legs → can0, rear legs → can1), verify against the physical wiring on first power-up; the bridge node takes the split as parameters (front_nodes/rear_nodes) and broadcasts any unmapped node on both buses.
| # | Leg | Joint (ROS name) | node_id | CAN channel | Polarity |
|---|---|---|---|---|---|
| 1 | Left Front | lf_hip_joint | 1 | can0 | N/A |
| 2 | Left Front | lf_upper_leg_joint | 2 | can0 | N/A |
| 3 | Left Front | lf_lower_leg_joint | 3 | can0 | N/A |
| 4 | Right Front | rf_hip_joint | 4 | can0 | N/A |
| 5 | Right Front | rf_upper_leg_joint | 5 | can0 | N/A |
| 6 | Right Front | rf_lower_leg_joint | 6 | can0 | N/A |
| 7 | Left Rear | lh_hip_joint | 7 | can1 | N/A |
| 8 | Left Rear | lh_upper_leg_joint | 8 | can1 | N/A |
| 9 | Left Rear | lh_lower_leg_joint | 9 | can1 | N/A |
| 10 | Right Rear | rh_hip_joint | 10 | can1 | N/A |
| 11 | Right Rear | rh_upper_leg_joint | 11 | can1 | N/A |
| 12 | Right Rear | rh_lower_leg_joint | 12 | can1 | N/A |
Polarity is assigned and verified on first power-up (a commanded step must move the joint the expected way); the bridge records it in the CAN capture for reference.
GIM6010-8 Datasheet
| Param | Value |
|---|---|
| Rated current | 10.5 A |
| Locked-rotor current | 25 A |
| Rated torque | 5 Nm |
| Max speed | 120 rpm |
| Interface | CAN bus (1 Mbps, see CAN Mapping) |
| Control modes | position / velocity / torque (see Drive sequences) |
| Config method | USB + ODrive per motor (no over-CAN method found) |
Implication: At 120 rpm on the lower-leg joint, peak foot-tip speed is modest. Sustained 1.0 m/s body speed is likely beyond comfortable joint speeds (see Speed Cap). The 25 A locked-rotor current is well above the 10.5 A rated current, so stall protection (see Motor-level fault handling) is required.
Hardware Bring-up
- Read live node IDs off the bus (preferred): parse GIM6010-8 heartbeat frame 0x001 to extract the node ID. Do this for both can0 and can1 to confirm which CAN bus is front and which is rear.
- For any motor with no readable ID: configure it via USB + ODrive (see Datasheet). Hip joints are hard to reach, plan physical access before scheduling.
- Confirm CAN split (can0 = front / can1 = rear) and record the final Node-ID table.
- Verify CAN-bus mode is enabled on all 12 motors (not USB/ODrive mode) before CHAMP goes live.
- Only then: CHAMP live test with CAN capture logging (see CAN capture).