Pilotage berthing automation

Taking a small vessel alongside, and proving it.

Docking control, the forecast that says when the window is open, the awareness at the berth, and the evidence trail behind all three. One stack — and a bench that checks the software the way a bus and a boat will.

recorded run · 0.0 s gap contact angle 2.5° controller PID

The stack

Five parts under one package, plus a hardware tier that replaces the simulated one when there is a unit on the quay.

Control pilotage.control

A 3-DOF docking simulator — Fossen hull, Kt/Kq propeller, DC motor with a current limit and a battery of finite internal resistance — with a PID baseline and a nonlinear MPC that plans against the pontoon as a constraint rather than a cost.

Scored on sixteen bench scenarios and a 330-cell matrix: 3 hulls × 5 propulsion sets × 11 conditions × 2 geometries.

Forecasting pilotage.forecasting

Metocean forecast down to what the berth will actually feel — wind on the beam, set across the approach, gust structure — and then to scored go/no-go windows, per berth and per vessel class rather than per port.

A window is a claim with a time on it, and the claim is kept for scoring against what happened.

Risk pilotage.risk

The safe-window engine, an evidence store and monthly reports. Thresholds and named risks live in a configuration pack, not in code, so the numbers a decision was made against can be read without reading the program.

Every refusal is counted and named: a clipped command nobody recorded is a boat behaving differently from its own log.

Detection pilotage.detection

The port-entrance scene with an AIS cross-check, emitting the safety events an approach aborts on. Sensors are simulated here and real in the hardware tier, which emits the same event contract from a Livox unit on the quay.

One role, two implementations, one contract — the abort does not know which one it came from.

Console pilotage.console

A four-panel dashboard over the three services. No build step, no framework: it is four HTML panels and a file server, because that is the whole requirement.

Runs from the repository with one command.

The bench SIL · HIL

The plant and the controller on opposite sides of a wire, on a simulated CAN bus with arbitration, airtime and fault injection. The device under test never sees the truth and does not know what its actuators did — the boat's situation, before any hardware is bought.

Generated C held to the model within 2 ULP; every frame's quantisation inside the contract's own tolerance.

What the bench has already found

Failures a desktop cannot show, because there the measurement and the prediction are the same instant by construction.

A bus is destroyed by latency long before it drops anything

At 125 kbit/s the worst frame waits 399.73 ms against a 100 ms control period — with no frame lost at all. A bench that counts losses would call that wire healthy.

The wire takes the last five metres

Beyond five metres it costs nothing. Inside them the estimate error doubles, 6.2 → 12.2 cm, because the improvement the rangefinders are aboard to give disappears. Quantisation accounts for 0.01 cm of that; the rest is time, and compensating for the readings' own age recovers it.

Not knowing your actuators gets dearer behind a wire

With a jammed thruster the approach ends 44.4 m off if the device trusts its own command, and 23.3 m off if it reads the shaft speed already on the bus. With an exact state the same difference is under a metre.

Held by gates, not by claims

Every figure here is one a test in the repository holds, and each of those tests has been watched failing on purpose.

C port
2 ULP
Generated C against the Python model on one step. The C is never hand-edited; regeneration fails on a diff.
Bus load
33.1 %
Of a 250 kbit/s NMEA 2000 backbone, 760 frames a second, with the boat's own traffic underneath.
Worst payload age
6.15 ms
Over a 139-second approach: no late cycle in 13 921 and nothing dropped.
Regression
330
Matrix cells, plus sixty gate files over the estimator, the safety layer, the bus and the log.