Original Approach API Reference
This page documents the API for the original state-space observer implementation preserved in src/ with the defunct_ prefix. This code was not used in the final system but represents the initial sensor fusion approach combining IMU, encoders, and RK4 numerical integration.
Note
This implementation was replaced by the displacement-based odometry approach documented in Tasks. See Analysis for the technical rationale behind this decision, including IMU communication issues and the pivot to encoder-only estimation.
IMU Driver
The BNO055 IMU driver provides 9-axis absolute orientation sensing with software I2C implementation. This driver includes automatic calibration management, bus recovery, and retry logic for robust operation.
SoftI2C Class
- class SoftI2C(scl_pin, sda_pin, delay_us=50)
Software I2C implementation with improved timeout handling and bus recovery.
- Parameters:
scl_pin (str) – SCL pin name (e.g., ‘B13’)
sda_pin (str) – SDA pin name (e.g., ‘B14’)
delay_us (int) – Bit timing delay in microseconds (default: 50)
- mem_read(nbytes, addr, reg)
Read multiple bytes from a device register with automatic bus recovery.
- Parameters:
nbytes (int) – Number of bytes to read
addr (int) – I2C device address (7-bit)
reg (int) – Register address to read from
- Returns:
Data read from register
- Return type:
bytes
- Raises:
OSError – If communication fails after bus recovery attempt
- mem_write(data, addr, reg)
Write multiple bytes to a device register with automatic bus recovery.
- Parameters:
data (bytes) – Data to write
addr (int) – I2C device address (7-bit)
reg (int) – Register address to write to
- Raises:
OSError – If communication fails after bus recovery attempt
BNO055 Class
- class BNO055(scl_pin, sda_pin, addr=0x28, autodetect=True, retries=10, retry_delay_ms=20)
Driver for BNO055 9-axis absolute orientation sensor using software I2C.
- Parameters:
scl_pin (str) – SCL pin name (e.g., ‘B13’)
sda_pin (str) – SDA pin name (e.g., ‘B14’)
addr (int) – I2C address (0x28 or 0x29, default: 0x28)
autodetect (bool) – Try both addresses if chip ID doesn’t match (default: True)
retries (int) – Number of retry attempts for I2C operations (default: 10)
retry_delay_ms (int) – Delay between retries in milliseconds (default: 20)
- Raises:
RuntimeError – If BNO055 not detected or wrong chip ID
- set_mode(fusion_mode)
Change the BNO055 operating mode.
- Parameters:
fusion_mode (int) – Operating mode constant (MODE_NDOF, MODE_IMU, etc.)
- get_calibration_status()
Get calibration status for all sensor subsystems.
- Returns:
Calibration status with keys ‘sys’, ‘gyro’, ‘accel’, ‘mag’. Each value ranges from 0 (uncalibrated) to 3 (fully calibrated).
- Return type:
dict
Example:
>>> status = imu.get_calibration_status() >>> print(status) {'sys': 3, 'gyro': 3, 'accel': 3, 'mag': 3}
- read_calibration_blob()
Read calibration coefficients from BNO055 as binary blob.
Temporarily switches to CONFIG mode, reads 22-byte calibration data, then restores previous operating mode.
- Returns:
22-byte calibration blob
- Return type:
bytes
- write_calibration_blob(blob)
Write calibration coefficients to BNO055 from binary blob.
Temporarily switches to CONFIG mode, writes 22-byte calibration data, then restores previous operating mode.
- Parameters:
blob (bytes) – Exactly 22 bytes of calibration data
- Raises:
ValueError – If blob is not exactly 22 bytes
- read_euler()
Read Euler angles from BNO055.
- Returns:
(heading, roll, pitch) in degrees
- Return type:
tuple
heading: 0-360° (yaw, compass heading)
roll: -180 to +180° (rotation about X-axis)
pitch: -90 to +90° (rotation about Y-axis)
- get_heading()
Get heading (yaw) angle in degrees.
Convenience function that extracts only the heading from Euler angles.
- Returns:
Heading in degrees (0-360°, 0 = North in NDOF mode)
- Return type:
float
- read_angular_velocity()
Read angular velocity (gyro rates) from BNO055.
- Returns:
(gx, gy, gz) angular velocities in degrees per second
- Return type:
tuple
gx: Roll rate (rotation about X-axis)
gy: Pitch rate (rotation about Y-axis)
gz: Yaw rate (rotation about Z-axis)
- get_yaw_rate()
Get yaw rate (angular velocity about Z-axis) in degrees per second.
Convenience function that extracts only the yaw rate from angular velocities.
- Returns:
Yaw rate in degrees per second (positive = counter-clockwise)
- Return type:
float
IMU Hardware Notes
Sensor: BNO055 9-axis absolute orientation sensor
Interface: Software I2C on pins B13 (SCL) and B14 (SDA)
Reset Pin: B15 (active low)
I2C Address: 0x28 (default) or 0x29 (alternate)
Operating Mode: NDOF (9-DOF fusion with magnetometer)
Heading Range: 0-360 degrees (0 = North in NDOF mode)
Angular Rates: Degrees per second
Calibration Status: 0 (uncalibrated) to 3 (fully calibrated)
Software I2C Delay: 150 µs (tuned for reliability)
Calibration Manager
Manages BNO055 IMU calibration persistence with file-based storage. Handles saving and loading calibration coefficients to/from the filesystem for automatic calibration restoration on startup.
CalibrationManager Class
- class CalibrationManager(imu)
Manages BNO055 IMU calibration persistence with file-based storage.
- Parameters:
imu (BNO055) – BNO055 IMU driver instance
Class Attributes:
CALIB_FILE = "calibration.txt": Filename for calibration storageCALIB_BLOB_LEN = 22: Expected calibration blob length in bytes
- calib_blob_to_hex(blob)
Convert calibration blob (bytes) to hexadecimal string representation.
- Parameters:
blob (bytes) – Raw calibration data bytes from IMU
- Returns:
Hexadecimal string representation of calibration data
- Return type:
str
- hex_to_calib_blob(hex_str)
Convert hexadecimal string back to calibration blob (bytes).
- Parameters:
hex_str (str) – Hexadecimal string from calibration file
- Returns:
Calibration data as bytes
- Return type:
bytes
- save_calibration()
Read calibration coefficients from IMU and save to persistent storage.
Reads the 22-byte calibration blob from the BNO055 sensor and writes it to
calibration.txtas a hexadecimal string.- Returns:
True if calibration saved successfully, False otherwise
- Return type:
bool
Example:
>>> calib_mgr.save_calibration() ✓ Calibration saved to calibration.txt Data: A1B2C3D4E5F6... True
- load_calibration()
Load calibration coefficients from file and write to IMU sensor.
Reads calibration data from
calibration.txt, validates the format and length, then writes the 22-byte calibration blob to the BNO055 sensor.- Returns:
True if calibration loaded successfully, False otherwise
- Return type:
bool
Validation:
Checks if file exists in current directory
Validates blob length equals 22 bytes
Handles conversion from hex string to bytes
- calibration_exists()
Check if calibration file exists in the current directory.
- Returns:
True if
calibration.txtexists, False otherwise- Return type:
bool
- delete_calibration()
Delete stored calibration file (for testing purposes).
Removes
calibration.txtfile from the filesystem if it exists. Used to force recalibration during testing and development.- Returns:
True if file deleted successfully, False if file not found
- Return type:
bool
Calibration Workflow
Typical usage pattern:
Initialization: Create
CalibrationManagerwith BNO055 instanceCheck for existing calibration: Use
calibration_exists()Load or calibrate:
If calibration exists: Call
load_calibration()to restore coefficientsIf no calibration: Perform manual calibration, then call
save_calibration()
Verification: Check IMU calibration status reaches
3/3for all subsystems
File Format:
The calibration file stores 22 bytes as a hexadecimal string:
A1B2C3D4E5F607080910111213141516171819202122
Calibration Notes:
Calibration blob contains coefficients for accelerometer, gyroscope, and magnetometer
Must be performed in NDOF mode for full 9-axis calibration
Calibration persists across power cycles when saved to file
System calibration status should reach
3/3before saving
IMU Handler Task
Provides cooperative multitasking functions for managing BNO055 IMU initialization, calibration persistence, and continuous sensor data reading. The calibration task implements a state machine for automatic calibration management, while the monitor task provides continuous heading and yaw rate updates with heading unwrapping.
Calibration Task Function
- calibration_task_fun(shares)
Cooperative task function implementing IMU calibration state machine.
Manages the complete IMU initialization and calibration workflow using a 6-state finite state machine. Automatically detects existing calibration files, loads calibration if available, or guides the user through manual calibration if needed.
- Parameters:
shares (tuple) – Task shares (currently unused, reserved for future use)
- Yields:
Current state number (0-5)
- Ytype:
int
State Machine:
S_INIT (0): Initialize BNO055 hardware and reset pin
S_CHECK_FILE (1): Check for existing calibration.txt file
S_LOAD_CALIB (2): Load calibration from file
S_MANUAL_CALIB (3): Manual calibration mode (rotate IMU)
S_SAVE_CALIB (4): Save calibration to file
S_DONE (5): Calibration complete, idle state
Hardware Setup:
Reset pin B15: Pulsed low then high to reset BNO055
I2C pins B13/B14: Software I2C communication
I2C address: 0x28 (default)
Calibration Requirements:
System calibration status must reach 3/3
IMU must be rotated in all directions during manual calibration
Calibration data saved to calibration.txt for persistence
Example Console Output:
================================================== === BNO055 Initialization === ================================================== [OK] BNO055 hardware initialized === Checking for Calibration File === [INFO] calibration.txt not found Manual calibration required (rotate IMU in all directions) === Manual Calibration Mode === Waiting for full calibration (Sys: 3/3)... Rotate the IMU slowly in all directions... [OK] Fully calibrated! (Sys: 3/3) === Saving Calibration === ✓ Calibration saved to calibration.txt ================================================== === Calibration Complete - Starting Main Loop === ==================================================
IMU Monitor Task Function
- imu_monitor_task_fun(shares)
Cooperative task function for continuous IMU data monitoring with heading unwrapping.
Alternately reads heading and yaw rate from the BNO055 IMU, updating shared variables for navigation and control tasks. Implements heading unwrapping to produce continuous heading values that do not wrap at 0/360 degrees.
- Parameters:
shares (tuple) – Task shared variables
Shared Variables:
shares[0](Share): IMU object share (BNO055 instance)shares[1](Share): Continuous heading output (float, degrees)shares[2](Share): Yaw rate output (float, deg/s)
- Yields:
None (yields control to scheduler after each sensor read)
Heading Unwrapping Algorithm:
The function maintains a continuous heading by detecting and correcting wraparound at the 0/360 degree boundary:
Read raw heading from IMU (0-360 degrees)
Calculate delta from previous reading
Detect wraparound:
If delta > 180: Crossed from 359 to 0 (subtract 360)
If delta < -180: Crossed from 0 to 359 (add 360)
Add corrected delta to continuous heading
Update previous heading for next iteration
Example Unwrapping:
# Heading crosses from 359° to 1° # Raw IMU readings: 359, 360, 0, 1, 2 # Continuous output: 359, 360, 360, 361, 362
Timing:
Alternates between heading and yaw rate reads each yield
Typical period: 70ms (14.3 Hz update rate)
Notes:
Continuous heading can exceed 360° or go negative
Initial heading matches first IMU reading
Yaw rate is read directly without processing
IMU object must be calibrated before this task starts
IMU Handler Workflow
System Startup Sequence:
Hardware Reset: Pin B15 pulsed low/high to reset BNO055
I2C Initialization: Software I2C established on B13/B14
Calibration Check: Look for calibration.txt file
Load or Calibrate:
If file exists: Load calibration coefficients automatically
If no file: Enter manual calibration mode
Manual Calibration (if needed):
User rotates IMU in all directions
System monitors calibration status (0-3 for each subsystem)
Waits for Sys: 3/3 before proceeding
Save Calibration: Write 22-byte blob to calibration.txt
Start Monitoring: Launch imu_monitor_task_fun for continuous data
Task Integration:
# Typical task setup in main program
imu_share = task_share.Share('O', name='imu')
heading_share = task_share.Share('f', name='heading')
yaw_rate_share = task_share.Share('f', name='yaw_rate')
monitor_task = cotask.Task(
imu_monitor_task_fun,
name="imu_monitor",
priority=3,
period=70,
shares=(imu_share, heading_share, yaw_rate_share)
)
State Observer
State observer task for robot localization using sensor fusion and RK4 integration. This module implements a cooperative multitasking observer function that fuses IMU heading measurements with encoder velocity data to estimate the robot’s global position (X, Y), heading, and arc length traveled.
Observer Task Function
Cooperative task function implementing state observer with RK4 integration.
Fuses IMU heading measurements with encoder velocity data to estimate the robot’s global position, heading, and distance traveled. Uses 4th-order Runge-Kutta numerical integration to solve the robot’s kinematic equations, providing accurate state estimation for navigation.
- Parameters:
shares (tuple) – Task shared variables for sensor inputs and state outputs
- Yields:
None (yields control to scheduler after each update cycle)
Shared Variables (14 inputs/outputs):
meas_heading_share(Share): IMU heading measurement (degrees)meas_yaw_rate_share(Share): IMU yaw rate measurement (deg/s)left_enc_pos(Share): Left encoder position (ticks)right_enc_pos(Share): Right encoder position (ticks)left_enc_speed(Share): Left encoder velocity (ticks/us)right_enc_speed(Share): Right encoder velocity (ticks/us)obs_heading_share(Share): Observer heading output (degrees)obs_yaw_rate_share(Share): Observer yaw rate output (deg/s)left_set_point(Share): Left motor control effortright_set_point(Share): Right motor control effortobserver_calibration_flg(Share): Calibration trigger flag (0 or 1)big_X_share(Share): Observer X position output (mm)big_Y_share(Share): Observer Y position output (mm)initial_heading_share(Share): Initial heading reference (degrees)end_flg(Share): End flag for debugging output
State Vector
The observer maintains a 6-state vector:
where:
X: Global X position (meters, converted to mm for output)
Y: Global Y position (meters, converted to mm for output)
Theta: Heading angle (radians, converted to degrees for output)
s: Arc length traveled (meters)
Omega_L: Left wheel velocity (m/s)
Omega_R: Right wheel velocity (m/s)
Kinematic Model
The observer uses differential drive kinematics with IMU heading feedback:
where:
\(v_L, v_R\): Left and right wheel linear velocities (m/s)
\(w\): Track width (0.141 m)
\(\theta_{IMU}\): IMU heading measurement (radians)
Calibration Sequence
When observer_calibration_flg == 1, the observer performs initialization:
Read Initial Heading: Get IMU heading at startup
Calculate Offset: Compute correction to align IMU with robot frame (+X = 0°)
Initialize Position: Set starting position to (100, 800) mm
Enable Observer: Set calibration flag to 0 and begin estimation
Example:
IMU heading at startup: -79.63°
IMU heading offset (to correct to +X = 0°): 79.63° (1.3895 rad)
[FIRST_RUN] Observer initialized at (100, 800) mm pointing +X (0°)
IMU Heading Filtering
Low-pass filter applied to reduce heading jitter:
heading_filtered = alpha * heading_new + (1 - alpha) * heading_prev
# alpha = 0.3 (30% new, 70% previous)
This helps smooth out rapid heading oscillations from IMU noise.
RK4 Integration Parameters
Integration Horizon: 0 to 100ms
Step Size: 10ms
Solver: 4th-order Runge-Kutta (RK4_solver)
Update Rate: 100ms (observer task period)
Tuning Parameters
Velocity calibration factor to match physical measurements.
Increase if observer underestimates distance traveled
Decrease if observer overestimates distance traveled
IMU heading low-pass filter coefficient.
Lower (e.g., 0.1) = smoother but slower response
Higher (e.g., 0.5) = faster response but more noise
Hardware Configuration
IMU: BNO055 9-DOF sensor for heading measurements
Encoders: Quadrature encoders on both wheels (1437.1 ticks/rev)
Wheel Diameter: 70mm (35mm radius)
Track Width: 141mm
Wheel Circumference: 0.2199 m
Coordinate Frame
X Axis: Forward direction of robot at initialization
Y Axis: Right direction of robot at initialization
Theta: Counter-clockwise positive from +X axis
Origin: Initial position (100, 800) mm at startup
Example Console Output
IMU heading at startup: -79.63°
IMU heading offset (to correct to +X = 0°): 79.63° (1.3895 rad)
[FIRST_RUN] Observer initialized at (100, 800) mm pointing +X (0°)
X = 567.8 mm, Y = 723.1 mm, Heading = 45.62° (corrected), s = 523.4 mm | v=12.3cm/s
Observer Notes
Calibration Required: Observer must be calibrated before navigation begins
Heading Source: IMU heading is used directly for X, Y position calculation
Integration Source: Encoder velocities drive heading integration via RK4
Output Format: Position in mm, heading in degrees
Update Period: 100ms observer task period ensures real-time performance
Angle Normalization Function
Normalize angle to [-180, 180] degree range.
- Parameters:
angle_deg (float) – Angle in degrees (can be any value)
- Returns:
Normalized angle in degrees within [-180, 180]
- Return type:
float
Example:
>>> normalize_angle_deg(270.0) -90.0 >>> normalize_angle_deg(-190.0) 170.0
RK4 Solver
Lightweight 4th-order Runge-Kutta (RK4) ODE solver for MicroPython + ulab. This module provides numerical integration for systems of ordinary differential equations, designed for embedded control and estimation tasks.
RK4 Solver Function
Integrate a system of ODEs using 4th-order Runge-Kutta method.
The ODE function must have the signature:
x_dot, y = fcn(t, x)
where:
x_dotis the state derivative (same shape as x)yis the output vector at time t
- Parameters:
fcn (callable) – ODE function handle f(t, x) -> (x_dot, y)
x_0 (ulab.numpy.ndarray) – Initial state vector (column array, shape (n_states, 1))
tspan (list | tuple) – Time span [t0, tf] for integration
tstep (float) – Integration step size
- Returns:
(tout, yout) where tout is a 1D array of time samples and yout is a 2D array of outputs, shape (len(tout), n_outputs)
- Return type:
tuple(ulab.numpy.ndarray, ulab.numpy.ndarray)
RK4 Algorithm
The classic 4th-order Runge-Kutta method computes four intermediate slopes per step:
where \(h\) is the step size (tstep).
Usage Example
from ulab import numpy as np
from defunct_rk4_solver import RK4_solver
# Define system: x_dot = -0.5 * x (exponential decay)
def system_fcn(t, x):
x_dot = np.array([[-0.5 * x[0, 0]]])
y = x # Output = state
return x_dot, y
# Initial condition: x(0) = 1.0
x_0 = np.array([[1.0]])
# Integrate from t=0 to t=10 with step size 0.1
tout, yout = RK4_solver(system_fcn, x_0, [0, 10], 0.1)
# yout contains the solution at each time step
print(f"Final state: {yout[-1, 0]:.4f}") # Should be ≈ 0.0067
Observer Integration Example
The observer task uses RK4_solver to integrate robot kinematics:
# Wrapper function for RK4 solver
def kinematics_wrapper(t, x_col):
'''Wrapper to convert column vector to/from kinematics_fun format'''
# Use corrected heading for position integration
xd, y = kinematics_fun(t, x_col, omega_L_ms, omega_R_ms, heading_corrected_rad)
return xd, y
# Run RK4 integration for 100ms with 10ms steps
tout, yout = RK4_solver(kinematics_wrapper, x, [0, 0.1], 0.01)
# Extract final state from the last row
x = np.array([[yout[-1, i]] for i in range(6)])
RK4 Performance Notes
Memory Efficient: Designed for ulab’s subset of NumPy (MicroPython)
Fixed Step Size: Uses constant time step (no adaptive step sizing)
Accuracy: 4th-order method, local truncation error \(O(h^5)\)
Speed: Optimized for embedded systems with limited resources
Typical Usage: 10ms steps over 100ms horizon for observer task
Main (Original)
Main program for the original IMU-based obstacle course navigation robot. This implementation uses BNO055 IMU for heading estimation combined with encoder-based state observation, implementing cooperative multitasking with IMU calibration, observer-based state estimation, and navigation control.
System Architecture
The main program coordinates seven cooperative tasks using the cotask scheduler:
IMU Calibration Task (Priority 7, One-time) - Runs at startup to initialize and calibrate BNO055 IMU - Loads calibration from file if available - Performs manual calibration if needed - Period: 50ms (runs until calibration complete)
IMU Monitor Task (Priority 3, Period 70ms) - Continuously reads heading and yaw rate from IMU - Implements heading unwrapping for continuous values - Provides sensor data to observer and navigation tasks
Observer Task (Priority 5, Period 100ms) - Fuses IMU heading with encoder velocity data - Uses RK4 numerical integration for state estimation - Outputs global X, Y position and heading estimates
Velocity Control Task (Priority 2, Period 12ms) - Updates motor PWM based on control effort - Fastest task for responsive motor control - Applies setpoints from closed-loop controllers
Closed Loop Control Task (Priority 4, Period 20ms) - PI velocity control for both motors - Implements line following with IR sensor array - Coordinates heading control and bias adjustments
Navigation Task (Priority 6, Period 100ms) - 5-state finite state machine for course navigation - Position-based state transitions using observer estimates - Coordinates line following, turns, and straight segments
Serial Communication Task (Priority 1, Period 200ms) - Handles terminal commands and status output - Lowest priority for non-critical communication
Hardware Configuration
Motors and Encoders:
# Left motor on pins PC1 (direction), PA0 (PWM), timer 5 channel 1
left_motor = motor_driver(Pin.cpu.C1, Pin.cpu.A0, Timer(5, freq=20_000), 1)
# Right motor on pins PA10 (direction), PC7 (PWM), timer 3 channel 2
right_motor = motor_driver(Pin.cpu.A10, Pin.cpu.C7, Timer(3, freq=20_000), 2)
# Left encoder on pins PB6, PB7 (timer 4)
left_encoder = Encoder(Pin.cpu.B6, Pin.cpu.B7, 4)
# Right encoder on pins PC6, PC7 (timer 8)
right_encoder = Encoder(Pin.cpu.C6, Pin.cpu.C7, 8)
IR Sensor Array:
# 7 IR reflectance sensors for line detection
ir_pins = [Pin.cpu.A4, Pin.cpu.A5, Pin.cpu.A6, Pin.cpu.A7,
Pin.cpu.B0, Pin.cpu.B1, Pin.cpu.C4]
ir_sensors = [pyb.ADC(pin) for pin in ir_pins]
IMU Sensor:
# BNO055 9-DOF IMU on software I2C
# SCL: Pin B13, SDA: Pin B14, Reset: Pin B15
# I2C Address: 0x28 (default)
Robot Physical Parameters:
Wheel Diameter: 70mm (radius = 35mm)
Track Width: 141mm
Encoder Resolution: 1437.1 ticks/revolution
Wheel Circumference: 0.2199 m
Task Timing and Priorities
The task scheduler executes tasks based on priority (higher number = higher priority) and period:
Task Name |
Priority |
Period (ms) |
Function |
|---|---|---|---|
IMU Calibration |
7 |
50 |
One-time startup calibration |
Navigation |
6 |
100 |
Position-based state machine |
Observer |
5 |
100 |
State estimation with RK4 |
Closed Loop Control |
4 |
20 |
PI velocity control |
IMU Monitor |
3 |
70 |
Heading/yaw rate reading |
Velocity Control |
2 |
12 |
Motor PWM update |
Serial Communication |
1 |
200 |
Terminal I/O |
Startup Sequence
The main program follows this initialization sequence:
Hardware Initialization: - Configure motor drivers with PWM timers - Initialize encoders with position and velocity tracking - Set up IR sensor array for line detection - Configure IMU reset pin (B15)
Shared Variable Creation: - Create all 28 shared variables for inter-task communication - Initialize controller objects (PI for velocity control)
Task Creation: - Create 7 cooperative tasks with cotask.Task() - Assign priorities and periods for each task - Add all tasks to cotask.task_list
Scheduler Execution: - Run cotask.task_list.pri_sched() in infinite loop - Tasks yield control based on period and priority - Handle KeyboardInterrupt for graceful shutdown
Control Flow Example
A typical control cycle showing task coordination:
Time 0ms:
├─ Velocity Control (12ms task)
│ └─ Apply motor PWM based on left/right_set_point
Time 20ms:
├─ Closed Loop Control (20ms task)
│ ├─ Read IR sensors for line position
│ ├─ Calculate line following error with bias
│ ├─ Run PI velocity controllers
│ └─ Update left/right_set_point
Time 70ms:
├─ IMU Monitor (70ms task)
│ ├─ Read heading from BNO055
│ ├─ Apply heading unwrapping
│ └─ Update meas_heading_share
Time 100ms:
├─ Observer (100ms task)
│ ├─ Read IMU heading and encoder velocities
│ ├─ Run RK4 integration (10ms steps over 100ms)
│ └─ Update big_X_share, big_Y_share, obs_heading_share
│
├─ Navigation (100ms task)
│ ├─ Read X, Y position from observer
│ ├─ Check position thresholds for state transitions
│ ├─ Update line_follow_flg, bias_share based on state
│ └─ Set desired_angle_share for heading control
Terminal Commands
Connecting to Board:
# List available USB serial devices (macOS/Linux)
ls /dev/tty.*
# Connect using screen (115200 baud)
screen /dev/tty.usbmodem205F379C39472 115200
# Exit screen session
# Press: Ctrl-A, then K, then Y
Board Commands:
# Soft reset the board
# Press: Ctrl-D
# Hard interrupt (KeyboardInterrupt)
# Press: Ctrl-C
Memory Management
The program includes memory management for MicroPython:
import gc
gc.collect() # Run garbage collection periodically
This helps prevent memory fragmentation during long-running operation.
Differences from Final Implementation
This original approach was replaced by the displacement-based odometry system. Key differences:
Original (defunct) Approach:
Uses BNO055 IMU for absolute heading measurement
Observer fuses IMU heading with encoder data via RK4 integration
Global X, Y position estimation from sensor fusion
Position-based navigation with coordinate thresholds
More complex state estimation with 6-state observer
Final Approach:
Pure encoder-based odometry (no IMU)
Displacement calculation from encoder ticks
Simpler state estimation without sensor fusion
Distance-based navigation (no global coordinates)
Reduced complexity and improved reliability
See Analysis for the technical rationale behind this architectural change, including IMU communication issues that motivated the switch to encoder-only estimation.