Tuning parameters

From 'Engineers of Innovation Wiki'
Jump to navigation Jump to search

The tuning parameters are the live-adjustable knobs of the autopilot's foiling control system: the roll and pitch attitude loops, the ride-height loop, the rear-foil schedule, coordinated-turn banking, and the operating-mode switches. Every one of them can be read and written over the CAN bus while the boat is running, from the helm keyboard, the datalogger, or any other node — which is what makes on-water tuning possible without a laptop, a cable, or a MAVLink link.

The autopilot side of this is foil_tune.lua, a small dedicated script on the flight controller that bridges CAN to the parameter system. It is deliberately separate from the control script (hydrofoils.lua): a bug in the tuning bridge can never take down the control loop, and simply not uploading the file removes the tuning surface entirely. The two never talk to each other — parameters are the shared state. The keyboard tool (tools/foil_tune.py) and the display's foiling screen mirror the same map; the hotkeys and cursor grid are documented in FOILING_PARAMETERS.md of the eoi-can repository.

At a glance

Tuning bridge summary
Property Value
Protocol version 7 (both sides check it at connect; index 0xFE returns it)
CAN identifiers 0x260 set · 0x261 value/ack · 0x262 request — 11-bit standard IDs
Value encoding IEEE754 float32, little-endian
Parameters 50 tunable entries (indices 1–57 with gaps; 37/38 retired)
Persistence All writes volatile by default — a power-cycle reverts a bad tune; persisting to flash is an explicit flag
Safety FC-side whitelist with min/max clamps; envelope and mode entries locked while foiling
Boot behaviour One unsolicited full dump ~5 s after every FC boot, so passive listeners repopulate
Pacing Dumps and bulk restores stream at ≤ 8 frames per 50 ms; senders pace to ~40 frames/s
Bus priority The 0x2xx tuning IDs lose CAN arbitration to every safety frame (0x0100x012) — tuning can only use idle bus time
Source of truth The PT table in foil_tune.lua; mirrored in tools/foil_tune.py and this page

How a parameter is read and written

Three frame types carry everything. A set (0x260) names a parameter by its index, carries the new value as a float32, and one flag bit: persist-to-flash, which is normally left off so that everything written during a tuning session evaporates on the next power-cycle unless deliberately saved. A request (0x262) asks for one index, or for the whole table (0xFF), or for the protocol version (0xFE). Every set and every request is answered by a value frame (0x261) that carries a status byte and — crucially — the value read back from the flight controller after clamping, locking and type-casting. The reply never echoes what was merely requested: it reports what the boat is actually using. A display that simply listens to 0x261 therefore mirrors every tuning change made by anyone, in real time, without sending a single frame.

Status byte in the 0x261 reply
Code Name Meaning
0 ok Accepted (or read) as-is
1 unknown No such index (includes the retired indices 37 and 38)
2 clamped The value hit the whitelist's min/max; the reply carries the clamped value in force
3 failed The parameter system refused the write (should not happen in practice)
4 unavailable The parameter does not exist yet — HYD_* before hydrofoils.lua has booted, or TRN_* when foil_turn.lua is not uploaded at all. For the turn parameters this doubles as the "is the feature installed?" probe
5 LOCKED Refused: the entry is envelope/mode-class and the boat is under autopilot control (see below). The reply carries the unchanged value in force

Three bus behaviours are worth knowing when building anything that listens:

  • Boot dump. About five seconds after every flight-controller boot, the bridge transmits one unsolicited full dump — one 0x261 per entry, then an end marker (index 0xFF, value = entry count). A passive listener is repopulated after every power-cycle without asking, and can always re-request with 0x262/0xFF.
  • Cursor convention. When the helm keyboard's cursor settles on a cell, the tool re-requests that cell's parameter(s). The resulting 0x261 is read-only — no value changes — but tells the display where the cursor is: the latest solo value frame is "cursor here". During active tuning the acks serve the same purpose.
  • Congestion is a non-issue by construction. CAN arbitration gives numerically lower IDs priority, so the tuning traffic (0x2600x262) and the throttle beeper (0x338) always yield to the safety frames — rear foil 0x010, height sensors 0x011/0x012 — bit-for-bit, without delaying them. A full 50-parameter restore plus its 50 acks costs about 13 ms of wire time at 1 Mbit/s, and is paced anyway to protect the FC's receive buffer.

Safety model

Two independent mechanisms protect the boat from the tuning surface itself, because the bridge is reachable from any node on a shared bus and — since the helm keyboard exists — from a single keystroke:

  • Whitelist and clamps (wrong values). Only the entries in the table below exist; everything else is refused. Every accepted value is clamped FC-side to the listed min/max, so no UI bug can write an out-of-range gain. A few floors are deliberately non-zero: the rate-loop P gains cannot go below 0.02, because P = 0 means "inner loop off", which is not a tune and must not be one key-repeat away on a manned foiler. Likewise HYD_RKP cannot go below 0.15: it is the artificial tailplane that makes the boat statically stable at all (0.059 exactly neutralises the natural −59 mm stability margin), so "0" would hand back a divergent boat.
  • In-flight locks (wrong moments). Some entries are refused outright (status 5) whenever the enable pin says the autopilot is controlling the boat. These are the entries that are not tuning at all: they change what the system is doing rather than how well it does it. A wrong gain degrades gradually; these act instantly. The attitude envelope (ROLL_LIMIT_DEG, PTCH_LIM_*) defines what the boat is ever allowed to do — its whole legal range contains capsize territory, so changing it is an ashore decision. The mode knobs SCR_USER1 (flips to roll-test mode, dropping the height loop on the very next 10 Hz iteration) and SCR_USER4 (open-loop rear-foil jog, a bench calibration function) are flight-mode changes, not parameter tweaks. TRN_REV flips the steering sense — mid-turn that would drive the bank to the opposite stop. To change any of them: enable off, write, re-enable — a deliberate two-hand action.

The lock's arbiter is the enable pin, and its ambiguity fails safe: if the button configuration is not verifiably in place, the bridge assumes the boat is under control and locks — the safe default for "may I change this?" is the opposite of the safe default for "should I control the boat?". Note that TRN_ENABLE is deliberately not locked: it is the in-flight off-switch for the banking feature, and switching it off ramps the bank out at TRN_RATE rather than snapping.

Recovery from a bad (volatile) tune is always the same: enable off, power-cycle — everything not explicitly persisted reverts to the values in flash.

Parameter map

Indices are the wire contract: they are only ever appended, never renumbered, and a change bumps the protocol version. Hotkey is the helm-display key that jumps to the cell (case matters: P is the rate loop's P, p the height loop's). Fine and coarse are the increment sizes used by the keyboard's volume keys / +− and <> respectively — conventions of the tools, not of the protocol, which always carries absolute values.

Roll inner loop

The ArduPlane FBWA rate-plus-angle controller, driving the two front foils differentially (elevon mixing). Roll is the high-authority axis — about 14.5 °/s of response per degree of foil — so its gains are numerically small.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
1 P RLL_RATE_P 0.02 2 0.005 0.02 Roll rate P gain. Floor is non-zero on purpose: 0 would switch the inner loop off
2 I RLL_RATE_I 0 2 0.005 0.02 Roll rate integrator gain — holds trim against steady asymmetry (e.g. crew weight)
3 D RLL_RATE_D 0 0.5 0.001 0.005 Roll rate damping gain
4 F RLL_RATE_FF 0 3 0.01 0.05 Roll rate feed-forward — the workhorse term in ArduPlane's rate loop
5 M RLL_RATE_IMAX 0 30 0.1 0.5 Integrator clamp, in the rate PID's output units where full surface = 45. A 75 kg driver leaning 100 mm needs ~20 units at the bottom of the speed envelope, hence the ceiling of 30
6 C RLL2SRV_TCONST 0.1 2 0.05 0.1 Angle→rate time constant (s): how aggressively the angle loop chases a roll target
7 R RLL2SRV_RMAX 0 180 5 15 Maximum commanded roll rate (°/s)
8 L ROLL_LIMIT_DEG 5 20 1 5 yes Bank-angle envelope (°). 45° of roll on a foiling boat is a capsize, not a limit — locked in flight because it is the airframe envelope, not a gain
9 T RLL_RATE_FLTT 0 100 1 5 Rate-target low-pass filter (Hz)
10 E RLL_RATE_FLTE 0 100 1 5 Rate-error low-pass filter (Hz)
11 G RLL_RATE_FLTD 0 100 1 5 D-term low-pass filter (Hz) — the first knob against high-frequency oscillation
12 S RLL_RATE_SMAX 0 200 5 20 Output slew-rate limit

Pitch inner loop

The same controller structure for pitch, driving the front foils in common mode plus the rear foil (elevator, forwarded over CAN as 0x010). Pitch is the low-authority axis — about 2.42 °/s per degree of foil, six times weaker than roll — so its gains run roughly six times higher for the same loop bandwidth; the wider P/I ceilings exist because the model-derived pitch P (≈5.65) exceeded the old limit of 2.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
16 P PTCH_RATE_P 0.02 8 0.02 0.1 Pitch rate P gain (non-zero floor, as roll)
17 I PTCH_RATE_I 0 8 0.02 0.1 Pitch rate integrator gain
18 D PTCH_RATE_D 0 0.5 0.001 0.005 Pitch rate damping gain
19 F PTCH_RATE_FF 0 4 0.01 0.05 Pitch rate feed-forward
20 M PTCH_RATE_IMAX 0 40 0.1 0.5 Integrator clamp, output units (full surface = 45)
21 C PTCH2SRV_TCONST 0.1 2 0.05 0.1 Angle→rate time constant (s)
22 R PTCH2SRV_RMAX_UP 0 180 5 15 Maximum nose-up pitch rate (°/s). On the display this is one cell with RMAX_DN: the two are edited together and kept equal
23 R PTCH2SRV_RMAX_DN 0 180 5 15 Maximum nose-down pitch rate (°/s); paired with RMAX_UP as above
24 X PTCH2SRV_RLL 0 1.5 0.01 0.05 Roll→pitch cross-feed (coordinated-turn pitch compensation). The boat's seed sets 0 — a displacement hull does not pitch in a bank the way an aircraft does; the floor was lowered from ArduPlane's customary 0.7 for exactly that reason
25 L PTCH_LIM_MAX_DEG 1 10 1 5 yes Pitch envelope, nose-up (°). One display cell with LIM_MIN, mirrored: + widens the envelope. Locked in flight; the old ceiling of 30° was, in the audit's words, a backflip demand
26 L PTCH_LIM_MIN_DEG −10 −1 1 5 yes Pitch envelope, nose-down (°); mirrored pair of LIM_MAX
27 T PTCH_RATE_FLTT 0 100 1 5 Rate-target filter (Hz)
28 E PTCH_RATE_FLTE 0 100 1 5 Rate-error filter (Hz)
29 G PTCH_RATE_FLTD 0 100 1 5 D-term filter (Hz)
30 S PTCH_RATE_SMAX 0 200 5 20 Output slew-rate limit

Speed scaling

One parameter stands apart because it silently retunes everything above: ArduPlane multiplies every surface gain on both axes by SCALING_SPEED / airspeed (clamped to roughly [0.5, 2]). It must be set to the boat's real foiling cruise speed, or none of the seeded gains mean what they say — a boat doing 8 m/s with the firmware default of 15 would run nearly double gains.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
31 Q SCALING_SPEED 4 15 0.5 1 Gain-scaling reference speed (m/s). Rescales every roll and pitch surface gain at once

Height outer loop

The ride-height controller in hydrofoils.lua: it turns the EKF's height-above-water estimate into a pitch-target bias for the inner loop. These parameters are created when that script boots, so they answer unavailable (status 4) for the first seconds after power-on.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
32 p HYD_KP 0 2000 10 50 Height-error → pitch-demand proportional gain
33 k HYD_KI 0 500 5 20 Height integrator gain — trims out steady offsets (loading, speed)
34 d HYD_KD 0 2000 10 50 Vertical-velocity damping gain, acting on the EKF's fused climb rate (phase-consistent with the P term, and blind to wave-surface motion by design)
35 h HYD_IMAX 0 500 10 50 Height integrator clamp
36 t HYD_TARGET 0 1 0.01 0.05 Ride-height setpoint (m) — the most-used control on the water
37 retired Was HYD_HSRC (height-source select, removed when control went EKF-exclusive). Answers unknown; the index is reserved forever
38 retired Was HYD_HDIV (EKF-vs-geometric divergence gate, removed — EKF3's own innovation gating covers it). Reserved forever
39 b HYD_ARM 0 3.8 0.05 0.2 Ride-height reference point: metres from the sensors back to the point whose height is controlled (2.4 = the front foils, 1.4 m forward of the FC). Vehicle geometry, not a gain — only change it if the foil station is re-measured. The ceiling is the true 3.8 m sensor arm
52 g HYD_CMDMAX 0.5 5 0.1 0.5 The height loop's own nose-up demand clamp (°) — its authority, distinct from the airframe envelope PTCH_LIM_* which stays locked. One display cell with CMDMIN, mirrored
53 g HYD_CMDMIN −8 −0.5 0.1 0.5 Nose-down authority (°): sets how hard the loop can push the bow down, i.e. the fly-up overshoot — the first knob to reach for after watching a fly-up

Rear-foil schedule

The rear foil's static trim and speed schedule, also in hydrofoils.lua. This group is what makes the boat statically stable in pitch, so it has the map's most protective floor.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
54 K HYD_RKP 0.15 1.2 0.02 0.1 The artificial tailplane gain. K = 0.059 exactly neutralises the natural −59 mm static margin, so the floor is 0.15, not 0: the tuner must never be able to hand back a divergent boat
55 W HYD_RSCALE 0.5 1.2 0.02 0.1 Rear decalage scale — the rear foil runs shallower than the fronts; the floor keeps it from being scheduled to nothing
56 Y HYD_RSCHED 0 1200 5 25 Speed-schedule strength (deg·m²/s²)
57 V HYD_FRNTFF 0 0.5 0.01 0.05 Fraction of the rear schedule fed forward to the front-foil trims

Coordinated-turn banking

Served by a third script, foil_turn.lua, which banks the boat into turns from the steering input. If that script is not uploaded, all six answer unavailable — which is itself the "is the feature installed?" check.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
40 N TRN_ENABLE 0 1 1 1 Feature on/off. Deliberately not locked: this is the in-flight kill switch, and disabling ramps the bank out at TRN_RATE instead of snapping
41 U TRN_ON 5 60 1 5 Steering input (%) at which banking starts
42 A TRN_FULL 10 100 1 5 Steering input (%) giving full bank
43 Z TRN_MAX 0 20 0.5 2 Maximum bank angle (°); 0 = never bank
44 H TRN_RATE 1 20 0.5 2 Bank slew rate (°/s)
45 J TRN_REV 0 1 1 1 yes Steering-sense reverse. Locked in flight: flipping it mid-turn would drive the bank to the opposite stop

Mode and test knobs

The scripting user parameters that hydrofoils.lua reads live. Two of them are flight-mode changes and therefore locked while foiling; the two roll-test demands stay live because sweeping them is the test.

Idx Hotkey Parameter Min Max Fine Coarse Locked Description
48 y SCR_USER1 0 1 1 1 yes Operating mode: 0 = height control (full cascade), 1 = roll-test/wheelie (height loop bypassed, fixed nose-up pitch). Takes effect on the next 10 Hz iteration — a mode change, not a tweak, hence the lock
49 q SCR_USER2 −10 10 0.5 1 Roll-test pitch target (°); 0 uses the built-in 3° default
50 f SCR_USER3 −20 20 1 5 Roll-step demand (°) for step-response tuning; 0 = level. Watch the achieved attitude come back on telemetry 0x250
51 B SCR_USER4 0 2100 10 50 yes Rear-foil jog (PWM µs, 0 = off): drives the rear actuator open-loop for bench calibration. Only honoured while the boat is disabled — and locked while foiling for the same reason

Special indices

Index Meaning
0xFE Protocol-version query: the reply's value is the version (currently 7). Any tool should check this before trusting its own copy of this map
0xFF As a request: dump the whole table (one 0x261 per entry, paced). As a reply: the end-of-dump marker, value = number of entries sent
13–15, 46–47 Unused, free for future entries in their groups
37, 38 Retired (HYD_HSRC, HYD_HDIV) — reserved forever, never reuse

Config slots, undo and factory reset

The helm keyboard adds a layer on top of the protocol; none of this exists on the wire beyond the ordinary set/ack frames it generates. Digit keys 19 are configuration slots: tap to restore a slot (every stored parameter is re-sent over CAN, volatile, so the acks repopulate every listener), hold ≈1.2 s to store the current acked values into it. Slots are deliberately volatile — held in the tool's memory, wiped on restart, never written to flash — so a slot is a scratchpad for A/B comparisons on the water, not a configuration store; the flash write remains its own explicit action. ~ undoes the last change (one step back per press — a slot restore or factory reset counts as a single step), and 0 held down performs a factory reset to the values read at connect. Restoring while foiling is safe by construction: the locked entries simply refuse (status 5) and everything else lands.

Keeping this page true

The authoritative copy of this map is the PT whitelist in foil_tune.lua on the flight controller, mirrored by PARAMS in tools/foil_tune.py and by the display's FOILING_PARAMETERS.md/.csv. Any change to indices, ranges or locks bumps PROTO_VERSION on both sides of the wire — if the version on this page and the one the boat reports at index 0xFE disagree, trust the boat and update this page.