Email me
Updates
← Self-balancing robot

Updates

Self-balancing robot

Every update on this project, newest first.

6 updates

Updates are bullet-point summaries generated with AI from my commits and build notes, then checked by me. Project articles and learning notes are written by me.

  1. AI summary

    • Verified TIM2 counts real milliseconds: 6,212 ms on the MCU against 6,208 ms on a PC stopwatch over 63 telemetry lines (ratio 1.001). Cross-checked a second way against the sensor cadence.
    • The battery ADC read between 4.96 V and 13.29 V under motor load on an 8.4 V pack, so the measurement is wrong rather than the pack. Leading suspect is ADC sampling that isn’t synchronised with the 25 kHz motor PWM. Not yet confirmed.
    • The low-battery cutoff stays disabled until the reading is understood. Averaging more samples first needs a wider accumulator than uint16_t.
  2. AI summary

    • Removed the delay_ms(5) after each loop. It added 5 ms on top of about 3.34 ms of work, so the loop ran near 120 Hz against a 200 Hz sensor. Motor jitter dropped straight away.
    • The controller, encoders and telemetry now advance only on a complete fresh DMP packet, because the loop polls 625 to 770 times a second without the delay.
    • Added an IMonotonicClock interface for the stale-IMU motor timeout. Measured cost: 616 bytes of flash and 8 bytes of BSS, under 1% of the 256 KB flash.
    • See Run the controller only on fresh samples and Separate the control logic from the hardware.
  3. AI summary

    • Confirmed the MPU6050 interrupt is on PA12, not PB12. EXTI line 12 is shared across ports and the port is chosen in GPIO_EXTILineConfig. PB12 is the PS2 controller’s chip select. Cross-checked by extracting the netlist from the board schematic PDF.
    • Withdrew a finding that the 1300 motor deadzone needed rescaling. The vendored reference uses the same 2880 timer period, so the deadzone and the PID gains carry over directly.
    • Capped the FIFO drain loop at 3 reads (about 2.5 ms) instead of 32 (about 27 ms on the bit-banged bus). Past the cap it zeroes PWM, resets the FIFO and counts the event.
    • Found that a failed IMU read leaves the last PWM command latched on the motors, with nothing expiring it. A stale-data motor cutoff has to land before the gains go up.
    • Added hysteresis to the tilt fault: it trips at 40° and clears at 10°, so the loop only restarts near upright.
  4. AI summary

    • Worked out the real control loop period from the telemetry log: about 12 ms against a nominal 5 ms, from 23-sample blocks of repeated angles overflowing a 32-packet FIFO.
    • Blocking serial output cost about 3.47 ms per 40-character line at 115200 baud, roughly 70% of a 5 ms tick. Printing one tick in twenty brought that down to about 3%.
    • Switched the UART wait from TC to TXE because it is the correct flag. Measured no speedup, since the baud rate is the limit.
    • Confirmed the feedback sign was already correct. The suspected inversion came from describing wheel rotation without recording which side of the car it was viewed from.
    • See Prove the motor mapping and feedback sign.
  5. AI summary

    • The register diagnostic now prints each i2cRead return code next to the value. On the bit-banged bus, a zero-initialised buffer made 00 indistinguishable from a failed read.
    • Tracked a dead left motor to uninitialised TIM8 config structs. Channels 1 to 3 read the unset complementary-output fields and channel 4 does not, so only the right motor (channels 3 and 4) ran. Value-initialising the structs fixed it.
    • Swapping the motor connectors could not separate hardware from firmware. Flashing Yahboom’s vendor image onto the same board did: both wheels drove.
    • See Establish a known-good hardware baseline.
  6. AI summary

    • Ported the MPU6050 DMP driver unchanged, but its FIFO stayed empty. The original called IIC_MPU6050_Init() from board code outside the driver, and without it SCL sat in its reset state.
    • Got printf telemetry working after fixing three silent gaps: the nosys.specs _write stub, newlib fully buffering stdout, and the ST-Link V2 having no UART. Telemetry comes out of the robot’s USB-C port through its CH340.
    • Traced an angle of -2147483648 in every sample to uninitialised floats in the IMU class. That value is what ARM produces when casting NaN to int. The constructor now zeroes the angles and sets the quaternion to identity.
    • Covered in more depth in Bring up UART, I2C and the MPU6050.