Software to control a Remotely Operated Underwater Vehicle (ROV) with 8 thrusters in V configuration. It consists of two programs: a base-station control program and a program that runs on the ROV's Raspberry Pi.
-
Base Station (
base_station/):- Runs on a laptop or desktop computer.
- Takes input from a keyboard or game controller (Xbox, PS).
- Calculates the thruster forces for 6-DOF movement (surge, sway, heave, roll, pitch, yaw).
- Sends control commands to the ROV over UDP at 20 Hz.
- Receives and displays telemetry and video from the ROV.
-
ROV/Pi (
pi/):- Runs on a Raspberry Pi mounted on the ROV, connected to a Pixhawk running ArduSub over USB (
/dev/ttyACM0). - Forwards the commands to the 8 thrusters as MAVLink RC overrides (Pixhawk outputs in RC passthrough mode).
- Reads attitude (roll, pitch, yaw) and the external Bar30 pressure sensor (
SCALED_PRESSURE2) from the Pixhawk. - Streams video from a RealSense camera and a USB camera back to the base station.
- Watchdog: if no command arrives for 1 s, all thrusters are set to neutral.
- Runs on a Raspberry Pi mounted on the ROV, connected to a Pixhawk running ArduSub over USB (
Base station ROV (Raspberry Pi)
┌─────────────────────────────┐ UDP 5006 ┌──────────────────────────────┐
│ input → PID/KF → allocation ├─────────────►│ command_receiver ─► Pixhawk │
│ (30 Hz loop) │ PWM @ 20 Hz │ watchdog (neutral after 1 s) │
│ │ UDP 5005 │ │
│ telemetry_listener │◄─────────────┤ sensor_sender (20 Hz) │
│ │ TCP 5555 │ │
│ video_receiver (imagezmq) │◄─────────────┤ video_stream_loop │
└─────────────────────────────┘ └──────────────────────────────┘
- Input: The base station captures the desired movement from the controller.
- Control: Optional PID controllers hold a target depth, roll, pitch, or yaw. A Kalman filter can smooth the depth measurement. Both run only on fresh telemetry samples and are reset if telemetry is lost.
- Kinematics: The desired forces and torques are allocated to the 8 thrusters (pseudo-inverse of the allocation matrix), with lateral and vertical groups saturated independently.
- Communication:
- The base station sends thruster PWM commands to the Pi.
- The Pi sends sensor telemetry (attitude, pressure) back to the base station.
- The Pi streams video from its cameras to the base station.
- On exit, the base station sends neutral and the Pi disarms.
| Path | Contents |
|---|---|
base_station/ |
Base-station program (main.py), configuration, controllers, PID, Kalman filter, thrust allocation |
pi/ |
ROV-side program (main.py) |
tests/ |
Automated test suite (runs in CI, no hardware needed) |
hardware_tests/ |
Manual bring-up scripts for thrusters, IMU, pressure sensor, cameras and network (need the real hardware) |
deploy/, scripts/ |
systemd unit and deploy script for the Pi |
.github/workflows/ |
CI and release pipelines |
Install the dependencies:
pip install -r requirements-base-station.txt # on the laptop
scripts/deploy_pi.sh --install-deps # from the laptop: copy to the Pi and install there- On the ROV's Raspberry Pi:
python3 pi/main.py(orsudo systemctl start rov-piafter deploying). - On the base station:
python3 base_station/main.py. Quit with the window close button orqin a video window; thrusters are sent neutral on exit.
Safety: the Pi program arms the vehicle when it starts. Keep the propellers clear before starting it. The systemd service is deliberately not enabled at boot.
- Network: The Pi's hostname defaults to
auv.localand the base station's tomba.local. Override them with theROV_PI_HOST(base station) andROV_BASE_HOST(Pi) environment variables. On the Pi, the service also reads them fromrov.envin the install directory. - Controller: The input device (Keyboard, Xbox, PS) is selected in
base_station/config.py. Usehardware_tests/controller_test.pyto check axis/button indices. - Tuning: PID gains, sensor offsets, and thruster inversion/enable flags are in
base_station/config.py. - Hardware: The logical-to-physical Pixhawk pin mapping (
THRUSTER_MAP) is inpi/main.py.
| Keys | Motion |
|---|---|
| W / S | Surge |
| A / D | Sway |
| Space / Left Shift | Heave |
| Q / E | Yaw |
| I / K | Pitch |
| J / L | Roll |
pip install -r requirements-dev.txt
pre-commit install # format + lint on every commit
python -m pytest # run the test suite
ruff format . && ruff check . # format and lint by handThe test suite is white-box: it has 100% line and branch coverage of every Python file in the repo, and CI fails if that drops (python -m pytest --cov). It covers:
- the PID, Kalman filter, thrust allocation (including golden values from the original implementation) and controller input;
- both programs' threads over real localhost sockets, and both main loops end to end on a virtual clock (closed-loop control, telemetry loss and recovery, shutdown);
- every hardware script against fakes of the Pixhawk, pigpio, serial IMU, Bar30, cameras and network, including the safety paths: Ctrl+C mid-spin must leave every thruster neutral and the vehicle disarmed;
- thread safety: a fake MAVLink connection fails the test if the Pixhawk is ever accessed from two threads at once.
- CI (
.github/workflows/ci.yml) runs on every push and pull request: ruff format check, ruff lint, shellcheck, and the test suite with 100% coverage enforced on Python 3.10 to 3.13. - Release (
.github/workflows/release.yml): pushing a tag such asv1.0.0runs CI and publishes a GitHub release with separate base-station and Pi bundles. - Deploy to the ROV:
scripts/deploy_pi.sh [--install-deps] [--restart]syncs the Pi code over SSH, installs dependencies into a virtualenv and installs therov-pisystemd unit. - Dependabot keeps GitHub Actions and Python dependencies up to date.
