The robot uses a decomposed LQR controller for self-balancing, implemented as four cascaded proportional controllers operating on pitch angle, pitch rate, wheel displacement, and wheel speed. A separate cascade controls yaw heading via differential wheel torque. Leg height and roll are managed by the STS3032 servo pair, and a jump state machine uses servo speed bursts to achieve ground-clearance hops.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/MuShibo/Micro-Wheeled_leg-Robot/llms.txt
Use this file to discover all available pages before exploring further.
LQR Pitch Balance Loop
The balance torqueLQR_u is derived from the classical LQR formula decomposed into four individually-tunable terms:
k1–k4) is implemented as the P-term of a PIDController instance so that it can be live-tuned via the Commander serial interface without recompiling.
State Variable Acquisition
Normal Balance Computation
0.1 * lpf_joyy(wrobot.joyy)) introduces a forward/back velocity bias by shifting the speed setpoint, which causes the balance loop to naturally tilt the robot in the commanded direction.
Wheel Liftoff Detection
If the wheel angular velocity spikes — indicating the wheels have left the ground (during a jump or forceful push) — the distance and speed terms are suppressed to prevent integrator wind-up and runaway torque:Distance Zero-Point Reset Logic
Three situations trigger a forced reset ofdistance_zeropoint to prevent the displacement term from fighting intentional motion:
| Trigger | Condition |
|---|---|
| Joystick active | wrobot.joyy != 0 |
| Joystick just zeroed and robot nearly stopped | wrobot_move_stop_flag == 1 and abs(LQR_speed) < 0.5 |
| Robot pushed quickly | abs(LQR_speed) > 15 |
Small-Torque Dead-Band Compensation
At very small output values the motor produces no meaningful torque due to stiction and back-EMF non-linearity. When all of the following are true,pid_lqr_u (a PI controller) integrates away the residual error:
Adaptive Zero-Point Self-Calibration
angle_zeropoint drifts slowly toward the true centre of mass by integrating the filtered distance_control signal. A persistent non-zero displacement indicates the nominal angle_zeropoint is offset from the real balance point:
angle_zeropoint = -2.25 degrees. The adaptation rate is governed by pid_zeropoint.P = 0.002.
Adaptive pid_speed Gain
The effective leg length changes significantly withwrobot.height. A shorter stance makes the inverted-pendulum dynamics faster; the speed gain is reduced at higher leg positions to prevent oscillation:
Yaw Control Loop
Yaw heading is controlled by accumulating the IMU’s incremental yaw angle and adding the joystick X contribution, then applying a differential torque correction.Yaw Angle Accumulation
The MPU6050 reports yaw angle modulo ±π.yaw_angle_addup() unwraps the modular angle into a monotonic running total:
Joystick Yaw Input Integration
After accumulation, the joystick X axis rotates the heading setpoint:wrobot.joyx turns the robot right.
Yaw PID and Differential Torque
YAW_output is then injected differentially into the wheel targets:
YAW_output to motor 1 and subtracting it from motor 2 spins the robot in place while the balance loop maintains pitch stability.
Leg Height and Roll Control
Both legs are driven by FEETECH STS3032 bus servos. The servo position formula maps the abstractwrobot.height parameter (range 32–80) to raw encoder counts around the mechanical centre (2048 counts).
Servo Position Formula
+12 / −12 offset corrects for the mechanical asymmetry between the two servo arms. The factor 8.4 counts/unit converts the height parameter to servo encoder counts.
Output Clamping
Positions are hard-clamped to keep the servos within their effective travel range:| Servo | Min | Max |
|---|---|---|
Position[0] (ID 1) | 2110 | 2510 |
Position[1] (ID 2) | 1586 | 1986 |
Normal Operation Speed / Acceleration
Jump Sequence
The jump is triggered by detecting the direction transition fromJUMP (5) → STOP (4) while jump_flag == 0. The sequence is managed by a counter (jump_flag) that increments each loop iteration.
Trigger — Detect JUMP→STOP Transition
When
wrobot.dir_last == 5 (JUMP) and wrobot.dir == 4 (STOP), the servos are commanded to maximum extension at maximum speed (ACC = 0, Speed = 0 means servo uses its own maximum):Extend Phase (jump_flag 1–29)
jump_flag increments each loop. The servos hold their extended position. The LQR liftoff detection suppresses wheel distance/speed terms during this phase.Retract Phase (jump_flag 30–34)
Servos are commanded back to mid-height (height = 40 equivalent) at maximum speed, then
jump_flag is forced to 40 to skip over the re-trigger window:Recovery Phase (jump_flag 40–200)
jump_flag continues incrementing. LQR liftoff suppression remains active (jump_flag != 0). Normal leg_loop() height control is suspended; the robot recovers balance.Fall Detection and Recovery
The firmware detects a fallen state by monitoring the IMU pitch angle and implements a timed recovery to prevent the motors from fighting against a fallen robot.| Threshold | Value | Description |
|---|---|---|
| Fall trigger | |LQR_angle| > 25° | Robot is declared fallen; motors are zeroed |
| Recovery condition | |LQR_angle| < 10° | Must hold for 200 consecutive loop cycles |
| Recovery delay | 200 loop cycles | ~200 ms at typical loop rates before control resumes |
The recovery counter only increments when the angle is already below 10°. If the robot is picked up and held at an intermediate angle (10°–25°), the counter does not advance — the robot will not re-enable until it is placed upright.