Internals
How the firmware works inside. You don’t need this unless you’re modifying the firmware itself.
Threads
Each thread runs at a fixed rate on a specific CPU core. Higher priority threads can interrupt lower priority ones.
Thread |
Frequency |
Priority |
CPU |
Description |
Status |
|---|---|---|---|---|---|
|
100 Hz |
99 |
7 |
Safety watchdog, heartbeat check |
Enabled |
|
1000 Hz |
95 |
1 |
CAN RX drain for all buses |
Enabled |
|
100 Hz |
94 |
6 |
CAN bus 0 motor I/O (L_Leg) |
Enabled |
|
100 Hz |
94 |
5 |
CAN bus 1 motor I/O (R_Leg) |
Enabled |
|
500 Hz |
92 |
4 |
BNO085 read (built-in fusion) |
Enabled |
|
50 Hz |
90 |
3 |
ONNX neural network inference |
Enabled |
|
100 Hz |
86 |
5 |
CAN bus 2 motor I/O (L_Arm) |
Disabled |
|
100 Hz |
85 |
5 |
CAN bus 3 motor I/O (R_Arm) |
Disabled |
|
100 Hz |
84 |
6 |
CAN bus 4 motor I/O (Torso) |
Disabled |
|
100 Hz |
83 |
6 |
CAN bus 5 motor I/O |
Disabled |
|
100 Hz |
80 |
2 |
Merge CAN data into state snapshot |
Enabled |
|
100 Hz |
50 |
2 |
UDP command reception (port 8888) |
Enabled |
|
100 Hz |
45 |
2 |
Protobuf telemetry streaming |
Enabled |
|
10 Hz |
30 |
3 |
Battery management |
Disabled |
Priority is Linux SCHED_FIFO (1-99). Higher number = more important = can interrupt lower numbers.
CPU Layout
CPU 1: can_rx (High-frequency CAN receive)
CPU 2: aggregator, netrx, nettx (Data aggregation + Network)
CPU 3: policy, power (Control)
CPU 4: imu (IMU sensor)
CPU 5: can1, can2, can3 (CAN buses 1-3)
CPU 6: can0, can4, can5 (CAN buses 0, 4-5)
CPU 7: estop (Safety-critical)
Why this layout:
CPU 7: E-stop gets its own core so nothing can starve safety
CPU 1: CAN RX at 1kHz needs a dedicated core for consistent timing
CPU 4: IMU at 500Hz has its own core for low-latency sensor reads
CPU 5-6: CAN motor threads spread across two cores
CPU 2-3: Network, aggregator, and policy share cores (lower frequency)
What Each Thread Does
estop - Watches for problems. If it doesn’t see commands for 100ms, or a motor overheats, or the IMU dies, it kills power to the motors.
can_rx - Runs at 1kHz to drain incoming CAN frames from all buses. This dedicated high-frequency thread ensures motor feedback is captured promptly, preventing packet loss in userspace. Without this, the 100Hz CAN threads might miss feedback frames that arrive between their polling intervals.
imu - Reads the BNO085 at 500Hz, writes orientation to g_app.imu.
can0-5 - Each one talks to motors on one CAN bus. Sends commands, reads back position/velocity/current/temperature.
aggregator - Combines motor data from all CAN threads into one snapshot.
policy - Runs the ONNX neural network. Reads sensor state, outputs motor commands.
netrx - Listens on UDP 8888 for your commands. Parses the protobuf, puts it in a queue for the policy.
nettx - Sends telemetry back to you at 100Hz.
Real-Time Stuff
At startup, the firmware locks all memory (mlockall) so there are no page faults during operation. Each thread is pinned to a specific CPU core so it doesn’t get migrated around.
Thread Lifecycle
flowchart TD
init["SCHEDULER_init()"] --> create
subgraph create["SCHEDULER_create_thread()"]
c1["Allocate thread slot"]
c2["Configure timer with frequency"]
c3["Set priority"]
end
create --> start
subgraph start["SCHEDULER_start_thread()"]
s1["Start periodic timer"]
s2["Set CPU affinity"]
s3["Thread enters RUNNING state"]
end
start --> running
running["Thread Running<br/>(periodic loop)"] --> callback["callback()"]
callback --> running
running --> stop["SCHEDULER_stop_thread()"]
stop --> stopped["Thread Stopped"]
style create fill:#e3f2fd
style start fill:#e8f5e9
style running fill:#fff3e0
Data Flow
flowchart TD
subgraph inputs["Input Threads"]
imu["IMU thread<br/>500 Hz<br/>Reads sensor"]
netrx["netrx_thread<br/>100 Hz<br/>Receives UDP"]
can_in["CAN threads<br/>100 Hz<br/>Talks to motors"]
end
imu --> imu_data["g_app.imu<br/>(atomic seqlock)"]
netrx --> netcmd_queue["netcmd_to_policy<br/>(SPSC queue)"]
can_in --> can_queue["can_to_aggregator<br/>(SPSC queues)"]
imu_data --> policy
netcmd_queue --> policy["policy_thread<br/>50 Hz<br/>Runs ONNX model"]
policy --> cmd_snap["cmd_snapshot<br/>(seqlock)<br/>IoCmd_S: motor targets"]
cmd_snap --> can_out["CAN threads<br/>(read cmds, write state)"]
can_queue --> aggregator
can_out --> aggregator["aggregator_thread<br/>Combines state"]
aggregator --> state_snap["state_snapshot<br/>(seqlock)<br/>IoState_S: joints, IMU, errors"]
state_snap --> nettx["nettx_thread<br/>100 Hz<br/>Sends telemetry"]
style inputs fill:#e3f2fd
style policy fill:#fff3e0
style aggregator fill:#e8f5e9
Internal Structs
IoState_S
Sensor data going to the policy. Written by aggregator, read by policy.
Field |
Type |
Unit |
Description |
|---|---|---|---|
|
float |
rad |
Joint positions |
|
float |
rad/s |
Joint velocities |
|
float |
A |
Motor phase currents |
|
float |
°C |
Motor temperatures |
|
float |
- |
Orientation quaternion [w,x,y,z] |
|
float |
rad/s |
Angular velocity in body frame |
|
float |
- |
Gravity vector (normalized) |
|
float |
- |
Locomotion phase [0-1] |
|
float |
V |
Pack voltage |
|
bool |
- |
Motor power state |
|
bool |
- |
E-stop active |
|
uint64 |
μs |
Capture timestamp |
IoCmd_S
Motor commands from policy to CAN threads.
Field |
Type |
Unit |
Description |
|---|---|---|---|
|
float |
rad |
Target positions |
|
float |
rad/s |
Target velocities (feedforward) |
|
float |
Nm |
Target torques (feedforward) |
|
float |
- |
Position gains |
|
float |
- |
Velocity gains |
|
bool |
- |
Enable motor power |
|
bool |
- |
Trigger E-stop |
NetCmd_S
Commands from network to policy (parsed from your RobotCommand).
Field |
Type |
Unit |
Description |
|---|---|---|---|
|
float |
m/s, rad/s |
Velocity command [vx, vy, vyaw] |
|
ControlMode_E |
enum |
Requested control mode |
|
bool |
- |
Enable motion |
|
bool |
- |
Remote E-stop |
|
uint64 |
μs |
Command timestamp |
Important Constants
Name |
Value |
What it means |
|---|---|---|
|
100000 |
100ms without commands = E-stop |
|
8888 |
Where to send commands |
|
6 |
Number of CAN interfaces |
Source Files
File |
What it does |
|---|---|
|
Thread setup in |
|
Global state struct |
|
IoState_S, IoCmd_S, NetCmd_S |