This repository contains the IMU firmware for Polaris.
This project's toolchain is based on PlatformIO, configured for the RP2040.
A dev shell is provided in flake.nix. If you use
direnv, it will load automatically when you enter the
directory. Otherwise:
nix developInstall Nix using the Determinate installer:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
mkdir -p ~/.config/nix/
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.confThen install direnv through Nix (the apt package is too old to support use flake):
nix profile add nixpkgs#direnv
echo '# Direnv shell hook'
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
exec bash
direnv allowInstall Nix using the Determinate installer:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
mkdir -p ~/.config/nix/
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.confThen install direnv through Nix:
nix profile add nixpkgs#direnv
echo '# Direnv shell hook'
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
exec zsh
direnv allowThe recommended path is WSL2 with Ubuntu 24.04. Install WSL2 from PowerShell:
wsl --install -d Ubuntu-24.04Once in WSL, follow the Linux instructions above.
For flashing, the RP2040 needs to be forwarded from Windows into WSL using usbipd-win:
usbipd list # find the RP2040 bus ID
usbipd bind --busid <id>
usbipd attach --wsl --busid <id>Skip this section if you've already configured git and your GitHub credentials.
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git config --global core.autocrlf false # Very important you don't miss this on WSL!Linux:
sudo apt update && sudo apt install ghmacOS:
brew install ghThen authenticate:
gh auth loginmake test # host Unity tests
make build # build firmware
make upload # flash the RP2040
make format # clang-format all sources in-placeRun make with no arguments to list all targets.
A Python harness at sim/ runs the C++ EKF offline for visualization and
tuning; the same filter that ships on the RP2040 runs in the sim, so tuning
values transfer faithfully.
make tui drops into an arrow-key picker; choose a view, then a scenario.
The timeseries view plots truth, measurements, the EKF estimate, and the open-loop gyro integration per channel, with residuals and a +/-1 sigma band below. Three runs show the fusion holding heading where raw integration cannot.
Held through a GNSS outage: fixes drop from 25 to 50 s; the open-loop gyro integration peels away while the EKF, having learned the gyro bias, stays near truth and re-anchors the instant fixes resume:
...and at heel: the same outage at 20 deg heel with a body-Y gyro bias, the lever behind heel-dependent heading drift. The 3-axis bias state observes and removes it, where the old vertical-only filter would ramp:
Across the envelope: peak heading error over a 30 s outage for every boat
attitude and turn-on bias (indoor mag). The sailing rows stay within a few
degrees; only near-vertical bench trim, where the ZYX heading kinematics are
singular, goes hot (uv run python examples/drift_sweep.py):
The mounting view renders the calibration geometry from tuning.toml, and simulate/pose show the 3D boat (truth, EKF estimate, raw IMU) as a GIF or a static filmstrip.
make tui # interactive picker
make tui SCENARIO=wave_tack VIEW=timeseries # skip the picker
make sim-test # pytest suite
make sim-format # ruff format + checkNoise, EKF, and calibration flags are available on the direct CLI; see
cd sim && uv run python -m plrs_sim sim --help.
Pre-push hooks are tracked in hooks/ and mirror the CI checks (clang-format
- native tests). If you use direnv, they are installed automatically when you enter the directory. Without direnv, install them once manually:
ln -sf ../../hooks/pre-push .git/hooks/pre-pushSee CONTRIBUTING.md.
The goal is heading accuracy of ≤2° on Polaris by fusing IMU and GNSS measurements through a Kalman filter (likely an Extended Kalman Filter given time constraints).
TinyEKF: Lightweight C/C++ Extended Kalman Filter.
Filter tuning lives in tuning.toml, shared by the firmware and the sim. See docs/tuning.md for theory, datasheet-derived starting values, and the record-and-replay workflow.
MCU: Raspberry Pi RP2040
MEMS IMU (accelerometer, gyroscope, magnetometer): Xsens MTi-3-5A-T
GNSS kit (dual antenna): Septentrio mosaic-go H
| Milestone | Issues |
|---|---|
| Python Logger | Serial capture script, log format, replay utility |
| Python EKF Sim | pybind11 bindings, synthetic data generator, sim runner, plotter |
| HIL Testing | On-device test suite, PIO Remote agent, CI integration |


