Tuning parameters: Difference between revisions
Created page with "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..." |
No edit summary |
||
| Line 10: | Line 10: | ||
! Property !! Value | ! Property !! Value | ||
|- | |- | ||
| Protocol version || ''' | | Protocol version || '''9''' (both sides check it at connect; index <code>0xFE</code> returns it) | ||
|- | |- | ||
| CAN identifiers || <code>0x260</code> set · <code>0x261</code> value/ack · <code>0x262</code> request — 11-bit standard IDs | | CAN identifiers || <code>0x260</code> set · <code>0x261</code> value/ack · <code>0x262</code> request — 11-bit standard IDs | ||
| Line 16: | Line 16: | ||
| Value encoding || IEEE754 float32, little-endian | | Value encoding || IEEE754 float32, little-endian | ||
|- | |- | ||
| Parameters || 50 tunable entries (indices | | Parameters || 50 tunable entries (indices 1–59 with gaps; 37/38/58 retired, 59 off the grid) | ||
|- | |- | ||
| Persistence || All writes '''volatile''' by default — a power-cycle reverts a bad tune; persisting to flash is an explicit flag | | Persistence || All writes '''volatile''' by default — a power-cycle reverts a bad tune; persisting to flash is an explicit flag | ||
| Line 28: | Line 28: | ||
| Bus priority || The <code>0x2xx</code> tuning IDs lose CAN arbitration to every safety frame (<code>0x010</code>–<code>0x012</code>) — tuning can only use idle bus time | | Bus priority || The <code>0x2xx</code> tuning IDs lose CAN arbitration to every safety frame (<code>0x010</code>–<code>0x012</code>) — tuning can only use idle bus time | ||
|- | |- | ||
| Source of truth || The <code>PT</code> table in <code>foil_tune.lua</code>; mirrored in <code>tools/foil_tune.py</code> | | Source of truth || The <code>PT</code> table in <code>foil_tune.lua</code>; mirrored in <code>tools/foil_tune.py</code>, which generates this page (<code>tools/export_wiki.py</code>) | ||
|} | |} | ||
| Line 57: | Line 57: | ||
* '''Boot dump.''' About five seconds after every flight-controller boot, the bridge transmits one unsolicited full dump — one <code>0x261</code> per entry, then an end marker (index <code>0xFF</code>, value = entry count). A passive listener is repopulated after every power-cycle without asking, and can always re-request with <code>0x262</code>/<code>0xFF</code>. | * '''Boot dump.''' About five seconds after every flight-controller boot, the bridge transmits one unsolicited full dump — one <code>0x261</code> per entry, then an end marker (index <code>0xFF</code>, value = entry count). A passive listener is repopulated after every power-cycle without asking, and can always re-request with <code>0x262</code>/<code>0xFF</code>. | ||
* '''Cursor convention.''' When the helm keyboard's cursor settles on a cell, the tool ''re-requests'' that cell's parameter(s). The resulting <code>0x261</code> 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. | * '''Cursor convention.''' When the helm keyboard's cursor settles on a cell, the tool ''re-requests'' that cell's parameter(s). The resulting <code>0x261</code> 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 (<code>0x260</code>–<code>0x262</code>) and the [[Throttle|throttle]] beeper (<code>0x338</code>) always yield to the safety frames — rear foil <code>0x010</code>, height sensors <code>0x011</code>/<code>0x012</code> — bit-for-bit, without delaying them. A full 50-parameter restore plus its | * '''Congestion is a non-issue by construction.''' CAN arbitration gives numerically ''lower'' IDs priority, so the tuning traffic (<code>0x260</code>–<code>0x262</code>) and the [[Throttle|throttle]] beeper (<code>0x338</code>) always yield to the safety frames — rear foil <code>0x010</code>, height sensors <code>0x011</code>/<code>0x012</code> — bit-for-bit, without delaying them. A full 50-parameter restore plus its 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 == | == Safety model == | ||
| Line 63: | Line 63: | ||
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: | 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 <code>P = 0</code> means "inner loop off", which is not a tune and must not be one key-repeat away on a manned foiler. | * '''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 <code>P = 0</code> means "inner loop off", which is not a tune and must not be one key-repeat away on a manned foiler. <code>HYD_RKP</code> was floored at 0.15 on the same reasoning until 2026-08-24; it now reaches 0, because the term is computed in the Lua script and so never provided the passive stability the floor was protecting, while the front elevons are closed by FBWA ~20× faster than the boat's divergent pitch root. Its default is still 0.4. | ||
* '''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''' (<code>ROLL_LIMIT_DEG</code>, <code>PTCH_LIM_*</code>) 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''' <code>SCR_USER1</code> (flips to roll-test mode, dropping the height loop on the very next 10 Hz iteration) and <code>SCR_USER4</code> (open-loop rear-foil jog, a bench calibration function) are flight-mode changes, not parameter tweaks. <code>TRN_REV</code> 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. | * '''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''' (<code>ROLL_LIMIT_DEG</code>, <code>PTCH_LIM_*</code>) 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''' <code>SCR_USER1</code> (flips to roll-test mode, dropping the height loop on the very next 10 Hz iteration) and <code>SCR_USER4</code> (open-loop rear-foil jog, a bench calibration function) are flight-mode changes, not parameter tweaks. <code>TRN_REV</code> 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. | ||
| Line 82: | Line 82: | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 1 || <code>P</code> || <code>RLL_RATE_P</code> || 0.02 || 2 || 0.005 || 0.02 || || Roll rate P gain. Floor is non-zero on purpose: 0 would switch the inner loop off | | 1 || <code>P</code> || <code>RLL_RATE_P</code> || 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 || <code>I</code> || <code>RLL_RATE_I</code> || 0 || 2 || 0.005 || 0.02 || || Roll rate integrator gain — holds trim against steady asymmetry (e.g. crew weight) | | 2 || <code>I</code> || <code>RLL_RATE_I</code> || 0 || 2 || 0.005 || 0.02 || || Roll rate integrator gain — holds trim against steady asymmetry (e.g. crew weight) | ||
|- | |- | ||
| 3 || <code>D</code> || <code>RLL_RATE_D</code> || 0 || 0.5 || 0.001 || 0.005 || || Roll rate damping gain | | 3 || <code>D</code> || <code>RLL_RATE_D</code> || 0 || 0.5 || 0.001 || 0.005 || || Roll rate damping gain | ||
|- | |- | ||
| 4 || <code>F</code> || <code>RLL_RATE_FF</code> || 0 || 3 || 0.01 || 0.05 || || Roll rate feed-forward — the workhorse term in ArduPlane's rate loop | | 4 || <code>F</code> || <code>RLL_RATE_FF</code> || 0 || 3 || 0.01 || 0.05 || || Roll rate feed-forward — the workhorse term in ArduPlane's rate loop | ||
|- | |- | ||
| 5 || <code>M</code> || <code>RLL_RATE_IMAX</code> || 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 | | 5 || <code>M</code> || <code>RLL_RATE_IMAX</code> || 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 || <code>C</code> || <code>RLL2SRV_TCONST</code> || 0.1 || 2 || 0.05 || 0.1 || || Angle→rate time constant (s): how aggressively the angle loop chases a roll target | | 6 || <code>C</code> || <code>RLL2SRV_TCONST</code> || 0.1 || 2 || 0.05 || 0.1 || || Angle→rate time constant (s): how aggressively the angle loop chases a roll target | ||
|- | |- | ||
| 7 || <code>R</code> || <code>RLL2SRV_RMAX</code> || 0 || 180 || 5 || 15 || || Maximum commanded roll rate (°/s) | | 7 || <code>R</code> || <code>RLL2SRV_RMAX</code> || 0 || 180 || 5 || 15 || || Maximum commanded roll rate (°/s) | ||
|- | |- | ||
| 8 || <code>L</code> || <code>ROLL_LIMIT_DEG</code> || 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 | | 8 || <code>L</code> || <code>ROLL_LIMIT_DEG</code> || 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 || <code>T</code> || <code>RLL_RATE_FLTT</code> || 0 || 100 || 1 || 5 || || Rate-target low-pass filter (Hz) | | 9 || <code>T</code> || <code>RLL_RATE_FLTT</code> || 0 || 100 || 1 || 5 || || Rate-target low-pass filter (Hz) | ||
|- | |- | ||
| 10 || <code>E</code> || <code>RLL_RATE_FLTE</code> || 0 || 100 || 1 || 5 || || Rate-error low-pass filter (Hz) | | 10 || <code>E</code> || <code>RLL_RATE_FLTE</code> || 0 || 100 || 1 || 5 || || Rate-error low-pass filter (Hz) | ||
|- | |- | ||
| 11 || <code>G</code> || <code>RLL_RATE_FLTD</code> || 0 || 100 || 1 || 5 || || D-term low-pass filter (Hz) — the first knob against high-frequency oscillation | | 11 || <code>G</code> || <code>RLL_RATE_FLTD</code> || 0 || 100 || 1 || 5 || || D-term low-pass filter (Hz) — the first knob against high-frequency oscillation | ||
|- | |- | ||
| 12 || <code>S</code> || <code>RLL_RATE_SMAX</code> || 0 || 200 || 5 || 20 || || Output slew-rate limit | | 12 || <code>S</code> || <code>RLL_RATE_SMAX</code> || 0 || 200 || 5 || 20 || || Output slew-rate limit | ||
|} | |} | ||
| Line 115: | Line 115: | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 16 || <code>P</code> || <code>PTCH_RATE_P</code> || 0.02 || 8 || 0.02 || 0.1 || || Pitch rate P gain (non-zero floor, as roll) | | 16 || <code>P</code> || <code>PTCH_RATE_P</code> || 0.02 || 8 || 0.02 || 0.1 || || Pitch rate P gain (non-zero floor, as roll) | ||
|- | |- | ||
| 17 || <code>I</code> || <code>PTCH_RATE_I</code> || 0 || 8 || 0.02 || 0.1 || || Pitch rate integrator gain | | 17 || <code>I</code> || <code>PTCH_RATE_I</code> || 0 || 8 || 0.02 || 0.1 || || Pitch rate integrator gain | ||
|- | |- | ||
| 18 || <code>D</code> || <code>PTCH_RATE_D</code> || 0 || 0.5 || 0.001 || 0.005 || || Pitch rate damping gain | | 18 || <code>D</code> || <code>PTCH_RATE_D</code> || 0 || 0.5 || 0.001 || 0.005 || || Pitch rate damping gain | ||
|- | |- | ||
| 19 || <code>F</code> || <code>PTCH_RATE_FF</code> || 0 || 4 || 0.01 || 0.05 || || Pitch rate feed-forward | | 19 || <code>F</code> || <code>PTCH_RATE_FF</code> || 0 || 4 || 0.01 || 0.05 || || Pitch rate feed-forward | ||
|- | |- | ||
| 20 || <code>M</code> || <code>PTCH_RATE_IMAX</code> || 0 || 40 || 0.1 || 0.5 || || Integrator clamp, output units (full surface = 45) | | 20 || <code>M</code> || <code>PTCH_RATE_IMAX</code> || 0 || 40 || 0.1 || 0.5 || || Integrator clamp, output units (full surface = 45) | ||
|- | |- | ||
| 21 || <code>C</code> || <code>PTCH2SRV_TCONST</code> || 0.1 || 2 || 0.05 || 0.1 || || Angle→rate time constant (s) | | 21 || <code>C</code> || <code>PTCH2SRV_TCONST</code> || 0.1 || 2 || 0.05 || 0.1 || || Angle→rate time constant (s) | ||
|- | |- | ||
| 22 || <code>R</code> || <code>PTCH2SRV_RMAX_UP</code> || 0 || 180 || 5 || 15 || || Maximum nose-up pitch rate (°/s). On the display this is one cell with <code>RMAX_DN</code>: the two are edited together and kept equal | | 22 || <code>R</code> || <code>PTCH2SRV_RMAX_UP</code> || 0 || 180 || 5 || 15 || || Maximum nose-up pitch rate (°/s). On the display this is one cell with <code>RMAX_DN</code>: the two are edited together and kept equal | ||
|- | |- | ||
| 23 || <code>R</code> || <code>PTCH2SRV_RMAX_DN</code> || 0 || 180 || 5 || 15 || || Maximum nose-down pitch rate (°/s); paired with <code>RMAX_UP</code> as above | | 23 || <code>R</code> || <code>PTCH2SRV_RMAX_DN</code> || 0 || 180 || 5 || 15 || || Maximum nose-down pitch rate (°/s); paired with <code>RMAX_UP</code> as above | ||
|- | |- | ||
| 24 || <code>X</code> || <code>PTCH2SRV_RLL</code> || 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 | | 24 || <code>X</code> || <code>PTCH2SRV_RLL</code> || 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 || <code>L</code> || <code>PTCH_LIM_MAX_DEG</code> || 1 || 10 || 1 || 5 || '''yes''' || Pitch envelope, nose-up (°). One display cell with <code>LIM_MIN</code>, mirrored: + widens the envelope. Locked in flight; the old ceiling of 30° was, in the audit's words, a backflip demand | | 25 || <code>L</code> || <code>PTCH_LIM_MAX_DEG</code> || 1 || 10 || 1 || 5 || '''yes''' || Pitch envelope, nose-up (°). One display cell with <code>LIM_MIN</code>, mirrored: + widens the envelope. Locked in flight; the old ceiling of 30° was, in the audit's words, a backflip demand | ||
| Line 137: | Line 137: | ||
| 26 || <code>L</code> || <code>PTCH_LIM_MIN_DEG</code> || −10 || −1 || 1 || 5 || '''yes''' || Pitch envelope, nose-down (°); mirrored pair of <code>LIM_MAX</code> | | 26 || <code>L</code> || <code>PTCH_LIM_MIN_DEG</code> || −10 || −1 || 1 || 5 || '''yes''' || Pitch envelope, nose-down (°); mirrored pair of <code>LIM_MAX</code> | ||
|- | |- | ||
| 27 || <code>T</code> || <code>PTCH_RATE_FLTT</code> || 0 || 100 || 1 || 5 || || Rate-target filter (Hz) | | 27 || <code>T</code> || <code>PTCH_RATE_FLTT</code> || 0 || 100 || 1 || 5 || || Rate-target filter (Hz) | ||
|- | |- | ||
| 28 || <code>E</code> || <code>PTCH_RATE_FLTE</code> || 0 || 100 || 1 || 5 || || Rate-error filter (Hz) | | 28 || <code>E</code> || <code>PTCH_RATE_FLTE</code> || 0 || 100 || 1 || 5 || || Rate-error filter (Hz) | ||
|- | |- | ||
| 29 || <code>G</code> || <code>PTCH_RATE_FLTD</code> || 0 || 100 || 1 || 5 || || D-term filter (Hz) | | 29 || <code>G</code> || <code>PTCH_RATE_FLTD</code> || 0 || 100 || 1 || 5 || || D-term filter (Hz) | ||
|- | |- | ||
| 30 || <code>S</code> || <code>PTCH_RATE_SMAX</code> || 0 || 200 || 5 || 20 || || Output slew-rate limit | | 30 || <code>S</code> || <code>PTCH_RATE_SMAX</code> || 0 || 200 || 5 || 20 || || Output slew-rate limit | ||
|} | |} | ||
| Line 150: | Line 150: | ||
One parameter stands apart because it silently retunes ''everything above'': ArduPlane multiplies every surface gain on both axes by <code>SCALING_SPEED / airspeed</code> (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. | One parameter stands apart because it silently retunes ''everything above'': ArduPlane multiplies every surface gain on both axes by <code>SCALING_SPEED / airspeed</code> (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. | ||
{| class="wikitable" | {| class="wikitable sortable" | ||
|- | |- | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 31 || <code>Q</code> || <code>SCALING_SPEED</code> || 4 || 15 || 0.5 || 1 || || Gain-scaling reference speed (m/s). Rescales every roll and pitch surface gain at once | | 31 || <code>Q</code> || <code>SCALING_SPEED</code> || 4 || 15 || 0.5 || 1 || || Gain-scaling reference speed (m/s). Rescales every roll and pitch surface gain at once | ||
|} | |} | ||
| Line 165: | Line 165: | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 32 || <code>p</code> || <code>HYD_KP</code> || 0 || 2000 || 10 || 50 || || Height-error → pitch-demand proportional gain | | 32 || <code>p</code> || <code>HYD_KP</code> || 0 || 2000 || 10 || 50 || || Height-error → pitch-demand proportional gain | ||
|- | |- | ||
| 33 || <code>k</code> || <code>HYD_KI</code> || 0 || 500 || 5 || 20 || || Height integrator gain — trims out steady offsets (loading, speed) | | 33 || <code>k</code> || <code>HYD_KI</code> || 0 || 500 || 5 || 20 || || Height integrator gain — trims out steady offsets (loading, speed) | ||
|- | |- | ||
| 34 || <code>d</code> || <code>HYD_KD</code> || 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) | | 34 || <code>d</code> || <code>HYD_KD</code> || 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 || <code>h</code> || <code>HYD_IMAX</code> || 0 || 500 || 10 || 50 || || Height integrator clamp | | 35 || <code>h</code> || <code>HYD_IMAX</code> || 0 || 500 || 10 || 50 || || Height integrator clamp | ||
|- | |- | ||
| 36 || <code>t</code> || <code>HYD_TARGET</code> || 0 || 1 || 0.01 || 0.05 || || Ride-height setpoint (m) — the most-used control on the water | | 36 || <code>t</code> || <code>HYD_TARGET</code> || 0 || 1 || 0.01 || 0.05 || || Ride-height setpoint (m) — the most-used control on the water | ||
|- | |- | ||
| 37 || — || ''retired'' || || || || || || Was <code>HYD_HSRC</code> (height-source select, removed when control went EKF-exclusive). Answers ''unknown''; the index is reserved forever | | 37 || — || ''retired'' || || || || || || Was <code>HYD_HSRC</code> (height-source select, removed when control went EKF-exclusive). Answers ''unknown''; the index is reserved forever | ||
|- | |- | ||
| 38 || — || ''retired'' || || || || || || Was <code>HYD_HDIV</code> (EKF-vs-geometric divergence gate, removed — EKF3's own innovation gating covers it). Reserved forever | | 38 || — || ''retired'' || || || || || || Was <code>HYD_HDIV</code> (EKF-vs-geometric divergence gate, removed — EKF3's own innovation gating covers it). Reserved forever | ||
|- | |- | ||
| 39 || <code>b</code> || <code>HYD_ARM</code> || 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 | | 39 || <code>b</code> || <code>HYD_ARM</code> || 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 || <code>g</code> || <code>HYD_CMDMAX</code> || 0.5 || 5 || 0.1 || 0.5 || || The height loop's ''own'' nose-up demand clamp (°) — its authority, distinct from the airframe envelope <code>PTCH_LIM_*</code> which stays locked. One display cell with <code>CMDMIN</code>, mirrored | | 52 || <code>g</code> || <code>HYD_CMDMAX</code> || 0.5 || 5 || 0.1 || 0.5 || || The height loop's ''own'' nose-up demand clamp (°) — its authority, distinct from the airframe envelope <code>PTCH_LIM_*</code> which stays locked. One display cell with <code>CMDMIN</code>, mirrored | ||
|- | |- | ||
| 53 || <code>g</code> || <code>HYD_CMDMIN</code> || −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 | | 53 || <code>g</code> || <code>HYD_CMDMIN</code> || −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 | ||
|} | |} | ||
| Line 194: | Line 194: | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 54 || <code>K</code> || <code>HYD_RKP</code> || 0 | | 54 || <code>K</code> || <code>HYD_RKP</code> || 0 || 1.2 || 0.02 || 0.1 || || The rear foil's '''pitch-error''' gain. Floored at 0 since 2026-08-24 (was 0.15). K = 0.059 neutralises the natural −59 mm static margin, but the term runs in the Lua script and so was never the passive stability it was described as; the front elevons are closed by FBWA at TCONST 0.5 s, ~20× faster than the 9.9 s divergent root. SITL measures it as harmful in normal operation (ride-height sd 29.6 → 18.1 mm in chop as 0.4 → 0, monotone). Default 0 since tune rev 22: the rear is a 2 °/s stepper, and through it the term is a second, laggy pitch loop that also dithers the stepper on pitch noise | ||
|- | |||
| 55 || <code>W</code> || <code>HYD_RSCALE</code> || 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 || <code>Y</code> || <code>HYD_RSCHED</code> || 0 || 1200 || 5 || 25 || || Speed-schedule strength (deg·m²/s²) | |||
|- | |- | ||
| | | 57 || <code>V</code> || <code>HYD_FRNTFF</code> || 0 || 0.5 || 0.01 || 0.05 || || Fraction of the rear schedule fed forward to the front-foil trims | ||
|- | |- | ||
| | | 58 || — || ''retired'' || || || || || || Was <code>HYD_IGATE</code> (integrator-gating bitmask, added to measure what the two integrator gates were worth and removed once measured). Reserved forever | ||
|- | |- | ||
| | | 59 || — || ''off the grid'' || || || || || || Is <code>HYD_RTKI</code>, since tune rev 22 the rate at which the height integrator's standing pitch demand is moved onto the rear foil (1/s; seeded 0.05 = a 20 s hand-over, 0 = off). It keeps the height loop centred when the speed schedule is wrong — a heavier driver, a current, a wrong RSCHED. On no cell of the grid on purpose: a rate to leave alone, not a knob for the water. Before rev 22 the same index was the abandoned front-deflection follow-up gain. The index is spoken for and must never be reused | ||
|} | |} | ||
| Line 211: | Line 215: | ||
! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ! Idx !! Hotkey !! Parameter !! Min !! Max !! Fine !! Coarse !! Locked !! Description | ||
|- | |- | ||
| 40 || <code>N</code> || <code>TRN_ENABLE</code> || 0 || 1 || 1 || 1 || || Feature on/off. Deliberately ''not'' locked: this is the in-flight kill switch, and disabling ramps the bank out at <code>TRN_RATE</code> instead of snapping | | 40 || <code>N</code> || <code>TRN_ENABLE</code> || 0 || 1 || 1 || 1 || || Feature on/off. Deliberately ''not'' locked: this is the in-flight kill switch, and disabling ramps the bank out at <code>TRN_RATE</code> instead of snapping | ||
|- | |- | ||
| 41 || <code>U</code> || <code>TRN_ON</code> || 5 || 60 || 1 || 5 || || Steering input (%) at which banking starts | | 41 || <code>U</code> || <code>TRN_ON</code> || 5 || 60 || 1 || 5 || || Steering input (%) at which banking starts | ||
|- | |- | ||
| 42 || <code>A</code> || <code>TRN_FULL</code> || 10 || 100 || 1 || 5 || || Steering input (%) giving full bank | | 42 || <code>A</code> || <code>TRN_FULL</code> || 10 || 100 || 1 || 5 || || Steering input (%) giving full bank | ||
|- | |- | ||
| 43 || <code>Z</code> || <code>TRN_MAX</code> || 0 || 20 || 0.5 || 2 || || Maximum bank angle (°); 0 = never bank | | 43 || <code>Z</code> || <code>TRN_MAX</code> || 0 || 20 || 0.5 || 2 || || Maximum bank angle (°); 0 = never bank | ||
|- | |- | ||
| 44 || <code>H</code> || <code>TRN_RATE</code> || 1 || 20 || 0.5 || 2 || || Bank slew rate (°/s) | | 44 || <code>H</code> || <code>TRN_RATE</code> || 1 || 20 || 0.5 || 2 || || Bank slew rate (°/s) | ||
|- | |- | ||
| 45 || <code>J</code> || <code>TRN_REV</code> || 0 || 1 || 1 || 1 || '''yes''' || Steering-sense reverse. Locked in flight: flipping it mid-turn would drive the bank to the opposite stop | | 45 || <code>J</code> || <code>TRN_REV</code> || 0 || 1 || 1 || 1 || '''yes''' || Steering-sense reverse. Locked in flight: flipping it mid-turn would drive the bank to the opposite stop | ||
| Line 234: | Line 238: | ||
| 48 || <code>y</code> || <code>SCR_USER1</code> || 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 | | 48 || <code>y</code> || <code>SCR_USER1</code> || 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 || <code>q</code> || <code>SCR_USER2</code> || −10 || 10 || 0.5 || 1 || || Roll-test pitch target (°); 0 uses the built-in 3° default | | 49 || <code>q</code> || <code>SCR_USER2</code> || −10 || 10 || 0.5 || 1 || || Roll-test pitch target (°); 0 uses the built-in 3° default | ||
|- | |- | ||
| 50 || <code>f</code> || <code>SCR_USER3</code> || −20 || 20 || 1 || 5 || || Roll-step demand (°) for step-response tuning; 0 = level. Watch the achieved attitude come back on telemetry <code>0x250</code> | | 50 || <code>f</code> || <code>SCR_USER3</code> || −20 || 20 || 1 || 5 || || Roll-step demand (°) for step-response tuning; 0 = level. Watch the achieved attitude come back on telemetry <code>0x250</code> | ||
|- | |- | ||
| 51 || <code>B</code> || <code>SCR_USER4</code> || 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 | | 51 || <code>B</code> || <code>SCR_USER4</code> || 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 | ||
| Line 247: | Line 251: | ||
! Index !! Meaning | ! Index !! Meaning | ||
|- | |- | ||
| <code>0xFE</code> || Protocol-version query: the reply's value is the version (currently ''' | | <code>0xFE</code> || Protocol-version query: the reply's value is the version (currently '''9'''). Any tool should check this before trusting its own copy of this map | ||
|- | |- | ||
| <code>0xFF</code> || As a request: dump the whole table (one <code>0x261</code> per entry, paced). As a reply: the end-of-dump marker, value = number of entries sent | | <code>0xFF</code> || As a request: dump the whole table (one <code>0x261</code> per entry, paced). As a reply: the end-of-dump marker, value = number of entries sent | ||
| Line 262: | Line 266: | ||
== Keeping this page true == | == Keeping this page true == | ||
The authoritative copy of | This page is GENERATED by <code>tools/export_wiki.py</code> in the autopilot repository, from the same tables the keyboard tuner runs on; regenerate and re-paste it after any parameter change. The authoritative copy of the map is the <code>PT</code> whitelist in <code>foil_tune.lua</code> on the flight controller. Any change to indices, ranges or locks bumps <code>PROTO_VERSION</code> on both sides of the wire — if the version on this page and the one the boat reports at index <code>0xFE</code> disagree, trust the boat and update this page. | ||
Revision as of 22:10, 27 September 2026
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
| Property | Value |
|---|---|
| Protocol version | 9 (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–59 with gaps; 37/38/58 retired, 59 off the grid) |
| 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 (0x010–0x012) — tuning can only use idle bus time
|
| Source of truth | The PT table in foil_tune.lua; mirrored in tools/foil_tune.py, which generates this page (tools/export_wiki.py)
|
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.
| 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
0x261per entry, then an end marker (index0xFF, value = entry count). A passive listener is repopulated after every power-cycle without asking, and can always re-request with0x262/0xFF. - Cursor convention. When the helm keyboard's cursor settles on a cell, the tool re-requests that cell's parameter(s). The resulting
0x261is 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 (
0x260–0x262) and the throttle beeper (0x338) always yield to the safety frames — rear foil0x010, height sensors0x011/0x012— bit-for-bit, without delaying them. A full 50-parameter restore plus its 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 = 0means "inner loop off", which is not a tune and must not be one key-repeat away on a manned foiler.HYD_RKPwas floored at 0.15 on the same reasoning until 2026-08-24; it now reaches 0, because the term is computed in the Lua script and so never provided the passive stability the floor was protecting, while the front elevons are closed by FBWA ~20× faster than the boat's divergent pitch root. Its default is still 0.4. - 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 knobsSCR_USER1(flips to roll-test mode, dropping the height loop on the very next 10 Hz iteration) andSCR_USER4(open-loop rear-foil jog, a bench calibration function) are flight-mode changes, not parameter tweaks.TRN_REVflips 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 | 1.2 | 0.02 | 0.1 | The rear foil's pitch-error gain. Floored at 0 since 2026-08-24 (was 0.15). K = 0.059 neutralises the natural −59 mm static margin, but the term runs in the Lua script and so was never the passive stability it was described as; the front elevons are closed by FBWA at TCONST 0.5 s, ~20× faster than the 9.9 s divergent root. SITL measures it as harmful in normal operation (ride-height sd 29.6 → 18.1 mm in chop as 0.4 → 0, monotone). Default 0 since tune rev 22: the rear is a 2 °/s stepper, and through it the term is a second, laggy pitch loop that also dithers the stepper on pitch noise | |
| 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 | |
| 58 | — | retired | Was HYD_IGATE (integrator-gating bitmask, added to measure what the two integrator gates were worth and removed once measured). Reserved forever
| |||||
| 59 | — | off the grid | Is HYD_RTKI, since tune rev 22 the rate at which the height integrator's standing pitch demand is moved onto the rear foil (1/s; seeded 0.05 = a 20 s hand-over, 0 = off). It keeps the height loop centred when the speed schedule is wrong — a heavier driver, a current, a wrong RSCHED. On no cell of the grid on purpose: a rate to leave alone, not a knob for the water. Before rev 22 the same index was the abandoned front-deflection follow-up gain. The index is spoken for and must never be reused
|
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 9). 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 1–9 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
This page is GENERATED by tools/export_wiki.py in the autopilot repository, from the same tables the keyboard tuner runs on; regenerate and re-paste it after any parameter change. The authoritative copy of the map is the PT whitelist in foil_tune.lua on the flight controller. 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.