@h2loop/uav 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.hydron-plugin/plugin.json +3 -0
- package/README.md +281 -0
- package/USAGE.md +172 -0
- package/agents/uav-compare.md +36 -0
- package/agents/uav-investigate.md +60 -0
- package/agents/uav-verify.md +45 -0
- package/bridge/README.md +163 -0
- package/bridge/requirements.txt +2 -0
- package/bridge/test_bridge.py +153 -0
- package/bridge/uav_mavlink_bridge.py +799 -0
- package/commands/uav-batch-report.md +20 -0
- package/commands/uav-compare.md +14 -0
- package/commands/uav-preflight.md +12 -0
- package/commands/uav-save-finding.md +25 -0
- package/commands/uav-tune.md +12 -0
- package/data/apm-params.json +1 -0
- package/data/uav-format-converters.example.json +17 -0
- package/mcp.json +15 -0
- package/package.json +43 -0
- package/plugins/index.js +46 -0
- package/plugins/worker/worker-entry.js +11 -0
- package/skills/uav/SKILL.md +101 -0
- package/skills/uav/references/checks.md +174 -0
- package/skills/uav/references/compare.md +49 -0
- package/skills/uav/references/grounding.md +94 -0
- package/skills/uav/references/project-config.md +27 -0
- package/skills/uav/references/signals.md +60 -0
- package/skills/uav/references/tuning-methodology.md +79 -0
- package/skills/uav/references/tuning.md +72 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uav
|
|
3
|
+
description: >-
|
|
4
|
+
UAV domain expertise for ArduPilot / PX4 work — flight logs, parameters, tuning, and
|
|
5
|
+
telemetry. Load this at the START of any UAV task in whatever mode you are in (Code,
|
|
6
|
+
Plan, Debug, Ask): deciding which uav_* tool answers a question, decoding/triaging a
|
|
7
|
+
.bin/.log/.ulog/.tlog, auditing params, recommending tuning, interpreting a check
|
|
8
|
+
(battery, attitude, altitude, hover, rcou, rcin, vibration, mag, ekf), explaining WHY a
|
|
9
|
+
guardrail bound is what it is, or narrating a cross-flight regression. It carries the
|
|
10
|
+
tool map, the grounding discipline (guardrails judge, you narrate), signal semantics,
|
|
11
|
+
per-check interpretation, the tuning playbook, and the fan-out rules. This is a domain
|
|
12
|
+
LAYER, not a mode — you stay in your current mode and use it.
|
|
13
|
+
---
|
|
14
|
+
# UAV skill — ArduPilot/PX4 flight-log domain layer
|
|
15
|
+
|
|
16
|
+
You are doing UAV work inside a normal Hydron mode (Code / Plan / Debug / Ask). This skill
|
|
17
|
+
is the domain layer that rides across that mode — it does not replace it. The deterministic
|
|
18
|
+
harness (the `uav_*` tools) produces findings (severity + verdict); your job is to drive the
|
|
19
|
+
right tool, then **explain** the results grounded in guardrail bounds and their sources —
|
|
20
|
+
never to author a verdict from raw numbers.
|
|
21
|
+
|
|
22
|
+
## Tooling — which tool for which job (call these directly; no mode switch)
|
|
23
|
+
|
|
24
|
+
- **Triage one log** → `uav_analyze` (.bin/.log/.tlog/.ulog): decode + the vehicle-aware
|
|
25
|
+
system checks + param audit + persist the episode. It already isolates the decode, the
|
|
26
|
+
full param load, and per-table SQL and returns only the rendered report — call it directly,
|
|
27
|
+
do not wrap it in a subagent.
|
|
28
|
+
- **Ad-hoc read** → `uav_query`: read-only SQL over ONE decoded flight, for a single quick
|
|
29
|
+
peek (one aggregate / a few rows).
|
|
30
|
+
- **Real computation** → `uav_python`: pandas/numpy over the flight's decoded tables
|
|
31
|
+
(`load("VIBE")`, `load("ATT")`, …; `TimeUS` is µs since boot; `print()` results). Use this
|
|
32
|
+
the moment an analysis needs binning, correlation, scoring/ranking windows, regression, FFT,
|
|
33
|
+
or multi-signal joins. Rule of thumb: **if you're about to run a 2nd `uav_query` to assemble
|
|
34
|
+
an answer, write `uav_python` instead.** Numbers must come from code you actually ran.
|
|
35
|
+
- **Chart** → `uav_plot` (`timeseries` | `xy` | `fft`): the FINAL picture, not a substitute
|
|
36
|
+
for reasoning. Never hand-write SQL for a visualisation.
|
|
37
|
+
- **Parameters** → `uav_param_audit` (safety / range / consistency; ranges grounded in
|
|
38
|
+
`apm.pdef.xml`, never invented).
|
|
39
|
+
- **Tuning** → `uav_tune`: FFT over gyro/VIBE → dominant peak → recommended `INS_HNTCH_*`
|
|
40
|
+
notch params (recommend, then push via the gated MAVLink link — never auto-write).
|
|
41
|
+
- **Run-tracking** → `uav_tag`: label a flight (test profile / campaign) — the param-history
|
|
42
|
+
record ArduPilot lacks.
|
|
43
|
+
- **Handoff** → `uav_export`: one CSV per message type for PlotJuggler.
|
|
44
|
+
- **Record a conclusion** → `uav_finding`: at the END of an investigation, `op:"list"` first,
|
|
45
|
+
judge whether it's already represented (same root cause counts), then `op:"add"` only if
|
|
46
|
+
genuinely new (`supersedes_id` to replace a stale one). Only ABSOLUTE-bound findings the
|
|
47
|
+
deterministic checks did not already cover — never a relative-only one.
|
|
48
|
+
- **Bench / live vehicle** → `serial_*` (watch/drive the FC console) and the `mavlink` MCP
|
|
49
|
+
(read params/telemetry; gated param writes) when a link is configured.
|
|
50
|
+
|
|
51
|
+
## Load the reference that matches the task
|
|
52
|
+
|
|
53
|
+
Read each by its explicit path under `references/`:
|
|
54
|
+
|
|
55
|
+
- **`references/grounding.md`** — how thresholds/guardrails are sourced and cited. The core
|
|
56
|
+
principle (the LLM narrates, the guardrails judge), the three guardrail sources (OEM
|
|
57
|
+
datasheet / rule-of-thumb / per-airframe tuned) and their precedence, and the work-style
|
|
58
|
+
rules (chain-of-thought, ask-don't-assume, cite the bound, surface-don't-over-verdict).
|
|
59
|
+
**Read this before you state why a bound is what it is, or you are tempted to judge a raw
|
|
60
|
+
number yourself. A flag must clear an ABSOLUTE grounded bound — not merely stand out
|
|
61
|
+
relative to peers or its own baseline.**
|
|
62
|
+
- **`references/signals.md`** — signal/column dictionary + analysis method. Which column
|
|
63
|
+
actually answers a question (e.g. throttle = `RCOU` motor-mean / `CTUN.ThO`, **not** the
|
|
64
|
+
near-constant `CTUN.ThH`), EKF innovation names, plus the reason-and-explore-before-plotting
|
|
65
|
+
discipline. **Read this before any correlation / "does X relate to Y" / visualisation
|
|
66
|
+
question.**
|
|
67
|
+
- **`references/checks.md`** — per-check interpretation (battery, attitude, altitude, hover,
|
|
68
|
+
rcou, rcin, vibration, mag, ekf): the table/fields it reads, the rule, what an anomaly means
|
|
69
|
+
physically, and how to talk about it without over-verdicting. Read this when interpreting
|
|
70
|
+
any finding.
|
|
71
|
+
- **`references/tuning-methodology.md`** — the ORDERED tuning playbook: pre-tune gates →
|
|
72
|
+
harmonic notch → rate PID → attitude → position/altitude → sensor source (`EK3_SRC`) → drift
|
|
73
|
+
over the sortie. Read this for ANY "how do I tune / what params / why is it oscillating /
|
|
74
|
+
altitude is noisy" question. `references/tuning.md` is the notch-recommender detail.
|
|
75
|
+
- **`references/compare.md`** — how to narrate a cross-flight diff (params, findings, regime;
|
|
76
|
+
tuning-phase vs production reference; direction/magnitude; ranking without closing the
|
|
77
|
+
verdict). Read this for `/uav-compare` or the `uav-compare` subagent's output.
|
|
78
|
+
- **`references/project-config.md`** — the optional per-project override files under
|
|
79
|
+
`<project>/.hydron/` (thresholds, param rules, param metadata, format converters): what each
|
|
80
|
+
controls, merge-vs-replace semantics, and the exact path to WRITE when a user asks to change
|
|
81
|
+
a bound, a param range, or add a custom log format.
|
|
82
|
+
|
|
83
|
+
If the right reference is not obvious, read `references/grounding.md` first — the grounding
|
|
84
|
+
discipline governs almost every UAV answer.
|
|
85
|
+
|
|
86
|
+
## Orchestration — split unrelated work, fan it out
|
|
87
|
+
|
|
88
|
+
- When ONE message asks MULTIPLE INDEPENDENT investigation-class questions — each needing its
|
|
89
|
+
own multi-step drill-down, none depending on another's answer — spawn ONE **`uav-investigate`**
|
|
90
|
+
subagent PER question and issue those task calls TOGETHER so they run in parallel, then
|
|
91
|
+
synthesize the briefs. Do NOT work them serially in this thread, and do NOT merge unrelated
|
|
92
|
+
questions into one investigation. (E.g. "what caused the EKF spike at 800s?", "walk the
|
|
93
|
+
altitude-excursion cause-chain", and "cross-check the IMUs" → three parallel investigations.)
|
|
94
|
+
- Split only when the questions are genuinely independent. If one's answer feeds the next, do
|
|
95
|
+
them in order. If each is a single one-shot tool call (several `uav_analyze`/`uav_plot`
|
|
96
|
+
requests), just call the tools directly — in parallel where independent — with no subagent.
|
|
97
|
+
- Other specialist subagents: **`uav-compare`** (cross-flight regression over an airframe's
|
|
98
|
+
history) and **`uav-verify`** (adversarially confirm a proposed flag clears an ABSOLUTE
|
|
99
|
+
bound — use before reporting any anomaly the deterministic checks did NOT produce). Each runs
|
|
100
|
+
the same grounded discipline and returns only its brief; the heavy chatter stays out of the
|
|
101
|
+
main thread.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Per-check interpretation
|
|
2
|
+
|
|
3
|
+
**21 system checks** run as SQL over the per-flight in-memory tables, plus a **parameter
|
|
4
|
+
audit** over the PARM set (`param.*` findings). Each yields a `severity`
|
|
5
|
+
(`OK` / `HIGH` / `CRITICAL`) and a `verdict` (`PASS` / `FAIL` / `SKIP` — `SKIP` = required
|
|
6
|
+
table/param absent). These are deterministic. Your job is to explain a FAIL physically and
|
|
7
|
+
rank it — never to manufacture a verdict. Always cite the bound and its source (see
|
|
8
|
+
`grounding.md`). Column names follow the sample log; the decoder is FMT-driven, so verify
|
|
9
|
+
per-firmware at decode time. The detailed write-ups below cover the core checks; the full
|
|
10
|
+
catalog (by subsystem) and the param audit follow at the end.
|
|
11
|
+
|
|
12
|
+
## battery (CRITICAL)
|
|
13
|
+
|
|
14
|
+
- **Tables / fields:** `ERR` (`Subsys`, `ECode`), correlated with `MSG`, `MODE`, `BAT`.
|
|
15
|
+
- **Rule:** `ERR.Subsys=6 AND ECode=1` with count > 0 → CRITICAL. A correlation check —
|
|
16
|
+
join on `TimeUS` to assemble the "battery failsafe → RTL" story (`ERR` + `MSG`
|
|
17
|
+
"Battery critical" + `MODE`→RTL + `BAT` sag).
|
|
18
|
+
- **Physically:** the flight controller declared a battery failsafe — voltage or
|
|
19
|
+
capacity crossed the configured floor and the vehicle acted on it (commonly RTL/Land).
|
|
20
|
+
- **How to talk about it:** narrate the timeline (when the failsafe fired, what the mode
|
|
21
|
+
did after), cite the `err_subsys:6 / err_ecode:1` signature as the ArduPilot battery-failsafe
|
|
22
|
+
firmware constant (rule-of-thumb), and point
|
|
23
|
+
at the `BAT` sag if present. Don't speculate on root cause beyond what the timeline
|
|
24
|
+
shows; surface it as the top-ranked finding when present.
|
|
25
|
+
|
|
26
|
+
## attitude (HIGH)
|
|
27
|
+
|
|
28
|
+
- **Tables / fields:** `ATT` — `DesRoll-Roll`, `DesPitch-Pitch`.
|
|
29
|
+
- **Rule:** sustained `MAX(ABS(desired - actual)) > max_deg` (default 5°, over a sample
|
|
30
|
+
window) → HIGH.
|
|
31
|
+
- **Physically:** the vehicle could not hold the commanded attitude — control authority,
|
|
32
|
+
tuning, or a disturbance (wind, payload, a failing motor) is preventing it from
|
|
33
|
+
tracking the setpoint.
|
|
34
|
+
- **How to talk about it:** report the peak divergence in degrees vs the 5° bound (a seed
|
|
35
|
+
default), note whether it was sustained or a transient, and correlate with rcou
|
|
36
|
+
saturation or vibration before suggesting a cause. Rank, don't conclude.
|
|
37
|
+
|
|
38
|
+
## altitude (HIGH)
|
|
39
|
+
|
|
40
|
+
- **Tables / fields:** `CTUN` — `DAlt-Alt` (desired vs achieved altitude).
|
|
41
|
+
- **Rule:** `MAX(ABS(DAlt-Alt)) > max_m` (default 2 m) → HIGH.
|
|
42
|
+
- **Physically:** altitude tracking error — the vehicle is not holding commanded height,
|
|
43
|
+
often tied to thrust margin, baro/EKF altitude estimate, or aggressive descent.
|
|
44
|
+
- **How to talk about it:** state the peak error in metres vs the 2 m seed bound; tie it
|
|
45
|
+
to hover-throttle or EKF findings if they co-occur. A few-metre transient in an
|
|
46
|
+
aggressive manoeuvre is different from sustained error in a hold — say which.
|
|
47
|
+
|
|
48
|
+
## hover (HIGH)
|
|
49
|
+
|
|
50
|
+
- **Tables / fields:** `CTUN.ThH` (hover throttle).
|
|
51
|
+
- **Rule:** `AVG(ThH)` outside the band `[0.25, 0.75]` → HIGH.
|
|
52
|
+
- **Physically:** hover throttle near the band edges signals a thrust-to-weight problem —
|
|
53
|
+
low end = overpowered/twitchy or light; high end = underpowered/overweight, little
|
|
54
|
+
margin for manoeuvre or wind.
|
|
55
|
+
- **How to talk about it:** report the average hover throttle vs the `[0.25, 0.75]`
|
|
56
|
+
rule-of-thumb band, and frame it as a thrust-margin observation. Relate to the ≥ 2:1
|
|
57
|
+
thrust-to-weight rule-of-thumb. Surface; the engineer judges acceptability.
|
|
58
|
+
|
|
59
|
+
## rcou — motor outputs (HIGH)
|
|
60
|
+
|
|
61
|
+
- **Tables / fields:** `RCOU` — `C1..C8`.
|
|
62
|
+
- **Rule:** any channel pegged at max → HIGH (saturation / imbalance).
|
|
63
|
+
- **Physically:** a motor output pinned at its ceiling means that motor has no headroom —
|
|
64
|
+
the mixer is asking for more than it can give, indicating imbalance, a weak/failing
|
|
65
|
+
motor, CG offset, or undersized propulsion.
|
|
66
|
+
- **How to talk about it:** name which channels saturated and for how long, and correlate
|
|
67
|
+
with attitude divergence (a saturated motor often explains an attitude FAIL). Describe
|
|
68
|
+
the imbalance pattern; leave the mechanical diagnosis to the engineer.
|
|
69
|
+
|
|
70
|
+
## rcin — pilot inputs (OK / context)
|
|
71
|
+
|
|
72
|
+
- **Tables / fields:** `RCIN` — `C3` (throttle), other channels for range sanity.
|
|
73
|
+
- **Rule:** values outside `[1000, 2000]` µs → HIGH; otherwise context.
|
|
74
|
+
- **Physically:** this is what the pilot commanded. Out-of-range values can mean RC
|
|
75
|
+
calibration, signal loss, or a failsafe trigger; in-range values are context that helps
|
|
76
|
+
explain other findings (e.g. an attitude divergence during an aggressive stick input).
|
|
77
|
+
- **How to talk about it:** mostly use rcin as supporting context — "the divergence
|
|
78
|
+
coincided with a full-throttle climb the pilot commanded." Only flag it as a finding
|
|
79
|
+
when the ranges are genuinely out of bounds.
|
|
80
|
+
|
|
81
|
+
## vibration (HIGH)
|
|
82
|
+
|
|
83
|
+
- **Tables / fields:** `VIBE` — `VibeX`, `VibeY`, `VibeZ`; plus `VIBE.Clip`.
|
|
84
|
+
- **Rule:** any axis peak ≥ 30 m/s² → HIGH (FC max is 105). Clipping:
|
|
85
|
+
`MAX(Clip) > 0` → anomaly (any clip event).
|
|
86
|
+
- **Physically:** excessive vibration corrupts the IMU/accelerometer feed, which then
|
|
87
|
+
poisons the EKF state estimate — a root cause that propagates into altitude and EKF
|
|
88
|
+
findings. Clipping means the accelerometer railed and lost data outright.
|
|
89
|
+
- **How to talk about it:** report peak per-axis vs the 30 m/s² bound (rule-of-thumb)
|
|
90
|
+
and note any clip events against the `max:0` bound. Frame vibration as a likely
|
|
91
|
+
upstream cause when EKF/altitude also FAIL. Tuning (notch filtering) is the usual
|
|
92
|
+
remedy, but that analysis lives in a separate path — surface, don't prescribe here.
|
|
93
|
+
|
|
94
|
+
## mag — magnetometer (HIGH)
|
|
95
|
+
|
|
96
|
+
- **Tables / fields:** `MAG.Health`; `MSG` for yaw-alignment timing.
|
|
97
|
+
- **Rule:** `MIN(Health) < 1` → HIGH. Late yaw-align: first `MSG` matching
|
|
98
|
+
`/EKF.*yaw align/i` minus arm time > `late_align_s` (default 30 s); no such MSG → SKIP
|
|
99
|
+
that sub-check.
|
|
100
|
+
- **Physically:** an unhealthy magnetometer or a late yaw alignment means the heading
|
|
101
|
+
estimate was unreliable early in the flight — interference, a bad compass calibration,
|
|
102
|
+
or magnetic disturbance near the airframe.
|
|
103
|
+
- **How to talk about it:** state whether health dropped below 1 and/or how long yaw
|
|
104
|
+
alignment took vs the 30 s bound. Connect to any EKF/heading anomalies. If the
|
|
105
|
+
yaw-align MSG is absent, say the sub-check was skipped — never imply a clean pass over
|
|
106
|
+
missing data.
|
|
107
|
+
|
|
108
|
+
## ekf — EKF innovations (HIGH)
|
|
109
|
+
|
|
110
|
+
- **Tables / fields:** `XKF3` — innovations `IVN`, `IVE`, `IVD`.
|
|
111
|
+
- **Rule:** innovation ratio > `max_ratio` (default 0.5) → HIGH. This is a correlation /
|
|
112
|
+
trend check, not a single scalar — regime-dependent.
|
|
113
|
+
- **Physically:** large EKF innovations mean the filter's prediction and its sensor
|
|
114
|
+
measurements disagree — the state estimate is being pulled around. Common upstream
|
|
115
|
+
causes: vibration corrupting the IMU, mag/GPS disagreement, or sensor dropout.
|
|
116
|
+
- **How to talk about it:** report the peak innovation ratio vs the 0.5 seed bound (from
|
|
117
|
+
ArduPilot health criteria), and explicitly correlate with vibration, mag, and GPS
|
|
118
|
+
findings — EKF FAILs are usually downstream of one of those. This is exactly where a
|
|
119
|
+
model is tempted to hand-wave; resolve it to the explicit criterion and the correlated
|
|
120
|
+
evidence, not a vibe. Surface the chain; let the engineer rule on root cause.
|
|
121
|
+
|
|
122
|
+
## gps — GPS health (HIGH)
|
|
123
|
+
|
|
124
|
+
- **Tables / fields:** `GPS`/`GPA` — `NSats`, `HDop`.
|
|
125
|
+
- **Rule:** `NSats < 6` or `HDop > 2.0` → HIGH (fix loss / poor geometry).
|
|
126
|
+
- **Physically:** too few satellites or high HDOP means a weak position fix — the EKF
|
|
127
|
+
position/velocity estimate degrades, which can cascade into altitude and EKF findings,
|
|
128
|
+
especially outside GPS-denied regimes.
|
|
129
|
+
- **How to talk about it:** report satellite count and HDOP vs the `min:6` / `hdop_max:2.0`
|
|
130
|
+
bounds, and note when in the flight the fix degraded. Correlate with EKF innovations.
|
|
131
|
+
Surface and rank; the engineer decides whether the regime made it acceptable.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Full catalog (added checks — boundary + consistency tier)
|
|
136
|
+
|
|
137
|
+
Beyond the core checks above, these run when their data is present (else SKIP). Each cites
|
|
138
|
+
its bound's source; bounds live in `uav-thresholds.yaml`.
|
|
139
|
+
|
|
140
|
+
**Power & battery** (`BAT`, `POWR`)
|
|
141
|
+
- `min_cell_voltage` (CRITICAL) — `MIN(BAT.Volt)/cells` (cells≈`ROUND(MAX(Volt)/4.2)`) below per-cell floor. Cite datasheet min; per-cell sag under load is the real health signal.
|
|
142
|
+
- `voltage_sag` (HIGH) — `MAX(BAT.VoltR-Volt)` large → weak pack/connector/high IR under load.
|
|
143
|
+
- `over_current` (HIGH) — `MAX(BAT.Curr)` above ESC/motor rating (airframe-specific; ground from datasheet).
|
|
144
|
+
- `internal_resistance` (HIGH) — `MAX(BAT.Res)` high → ageing/damaged pack (seed-default; pack-specific, override per airframe).
|
|
145
|
+
- `board_brownout` (CRITICAL) — `MIN(POWR.Vcc)` dipped near/below 4.5V → flight-controller power-rail brownout; correlate with any reboots/errors.
|
|
146
|
+
|
|
147
|
+
**Control & propulsion** (`RATE`, `MOTB`, `ESC`)
|
|
148
|
+
- `rate_tracking` (HIGH) — desired-vs-actual body rates diverge (`RDes-R`,`PDes-P`,`YDes-Y`) → tune/saturation; pairs with vibration & oscillation.
|
|
149
|
+
- `thrust_margin` (HIGH) — `MOTB.FailFlags`>0 (thrust loss) or low `ThrOut`/`LiftMax` headroom → undersized/▼T:W (rule-of-thumb ~2:1).
|
|
150
|
+
- `esc_health` (HIGH) — ESC over-temp (`Temp`/`MotTemp`) or `Err`>0 → desync/overheat risk.
|
|
151
|
+
|
|
152
|
+
**Estimation & sensors** (`XKF4`, `GPA`, `MAG`, `IMU`)
|
|
153
|
+
- `ekf_variance` (HIGH) — `XKF4` SV/SP/SH/SM spikes (or `FS` set) → estimator stress/failsafe.
|
|
154
|
+
- `gps_accuracy` (HIGH) — `GPA.HAcc` above bound → poor fix quality even with sats.
|
|
155
|
+
- `mag_offsets` (HIGH) — large `MAG.Ofs*` → bad calibration / interference; correlate with current draw.
|
|
156
|
+
- `imu_health` (HIGH) — `IMU.EG`/`EA` error counts > 0 → IMU dropouts; check across instances.
|
|
157
|
+
|
|
158
|
+
## Parameter audit (`param.*`)
|
|
159
|
+
|
|
160
|
+
A separate deterministic pass over the PARM set (range/safety/consistency). Findings are
|
|
161
|
+
`tier:system`, `check` ids prefixed `param.`. Bounds in `<project>/.hydron/param-rules.yaml`
|
|
162
|
+
(create/edit this file to add or change a rule; merged over the built-in ruleset).
|
|
163
|
+
|
|
164
|
+
- **`param.safety.*`** — arming checks not disabled (`ARMING_CHECK≠0`, CRITICAL), battery
|
|
165
|
+
failsafe configured (`BATT_LOW/CRT_VOLT`, CRITICAL), radio failsafe (`FS_THR_ENABLE`),
|
|
166
|
+
geofence (`FENCE_ENABLE`). A disabled safety is a config risk, not a flight symptom — say so.
|
|
167
|
+
- **`param.consistency.*`** — cell-count vs failsafe voltages agree; frame class/type set;
|
|
168
|
+
notch filter enabled when expected.
|
|
169
|
+
- **`param.range.*`** — tuning/safety params within sane ranges (`INS_GYRO_FILTER`,
|
|
170
|
+
`ATC_RAT_*_P`, `MOT_SPIN_*`, `RTL_ALT`, …). Out-of-range = likely misconfig; cite the range.
|
|
171
|
+
|
|
172
|
+
When narrating a param finding, frame it as *configuration* (what's set vs recommended),
|
|
173
|
+
distinct from a *telemetry* anomaly (what the vehicle did) — and never invent a range; cite
|
|
174
|
+
`param-rules.yaml` / the datasheet.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Narrating a cross-flight diff
|
|
2
|
+
|
|
3
|
+
Comparison is the tuning-phase path: track how a single airframe's behaviour shifts
|
|
4
|
+
flight-to-flight. The harness produces the diff (param changes + finding changes); your
|
|
5
|
+
job is to narrate what moved, by how much, and in which direction — grounded in the
|
|
6
|
+
guardrails, ranked, and without closing the verdict.
|
|
7
|
+
|
|
8
|
+
## What to compare
|
|
9
|
+
|
|
10
|
+
- **Params** — what changed in the ~1,117-param set between the two flights. Highlight
|
|
11
|
+
the deltas that plausibly explain a finding shift (e.g. a notch-filter or gain change
|
|
12
|
+
alongside a vibration or attitude change).
|
|
13
|
+
- **Findings** — which checks improved, regressed, appeared, or disappeared between the
|
|
14
|
+
two flights, with each check's measured value on both sides and the bound it relates
|
|
15
|
+
to.
|
|
16
|
+
- **Regime** — note if the two flights were flown differently (aggressive vs hover,
|
|
17
|
+
GPS-denied vs open). A finding shift driven by a regime change is not the same as a
|
|
18
|
+
shift at constant regime. If the regime is ambiguous, ask before attributing the shift.
|
|
19
|
+
|
|
20
|
+
## Reference: tuning vs production
|
|
21
|
+
|
|
22
|
+
- **Tuning phase** → the reference is the *last* flight. You are tracking pattern shifts
|
|
23
|
+
over the tuning cycle; the question is "did this change move the airframe in the right
|
|
24
|
+
direction?"
|
|
25
|
+
- **Production vehicle** → the reference is the *fixed* guardrail bound, for consistency.
|
|
26
|
+
The question is "is this flight within bounds?"
|
|
27
|
+
|
|
28
|
+
Same machinery, different reference. State which one you are using.
|
|
29
|
+
|
|
30
|
+
## How to describe a shift
|
|
31
|
+
|
|
32
|
+
For each shifted finding, give:
|
|
33
|
+
- the check and its measured value on flight A vs flight B;
|
|
34
|
+
- the **direction** (improved toward / regressed away from the bound) and the
|
|
35
|
+
**magnitude** of the change;
|
|
36
|
+
- the guardrail bound it relates to and that bound's source (datasheet / rule-of-thumb /
|
|
37
|
+
tuned — see `grounding.md`);
|
|
38
|
+
- the plausible param change that correlates with it, if any.
|
|
39
|
+
|
|
40
|
+
Example shape: "Peak Z vibration fell from 41 to 28 m/s² (now under the 30 m/s²
|
|
41
|
+
customer-stated bound), coinciding with INS_HNTCH params being enabled — the EKF
|
|
42
|
+
innovation peak also dropped from 0.7 to 0.4."
|
|
43
|
+
|
|
44
|
+
## Rank, don't close the verdict
|
|
45
|
+
|
|
46
|
+
Order the shifts by significance (a CRITICAL battery change or a regression that crosses
|
|
47
|
+
a bound outranks a small in-bounds wobble). Present the ranked narrative and the
|
|
48
|
+
correlated evidence; leave the call — "is this tuning step good enough to keep?" — to the
|
|
49
|
+
engineer. Surface the chain; don't decide it.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Grounding — how thresholds and guardrails are sourced and cited
|
|
2
|
+
|
|
3
|
+
This is the customer's #1 concern: a generic model asked "is this flight OK?" guesses,
|
|
4
|
+
because it does not know *this* airframe's actual limits. UAV anomaly judgments must be
|
|
5
|
+
grounded in the vehicle's real constraints, not the model's priors.
|
|
6
|
+
|
|
7
|
+
## The principle: the LLM narrates, the guardrails judge
|
|
8
|
+
|
|
9
|
+
The verdict is kept out of your hands. The deterministic harness compares the measured
|
|
10
|
+
value against an **explicit guardrail** and produces the `Finding` (its `severity` and
|
|
11
|
+
its `verdict`). You only **explain** confirmed findings and answer questions — you never
|
|
12
|
+
decide "anomalous vs fine" from raw numbers. A hallucinated threshold therefore cannot
|
|
13
|
+
become a verdict: the threshold is data, not a guess.
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart LR
|
|
17
|
+
M["measured value (decoded)"] --> G{"guardrail check (deterministic)"}
|
|
18
|
+
GR["guardrails: OEM datasheet + rules-of-thumb + per-airframe bounds"] --> G
|
|
19
|
+
G -->|in bounds| OK["verdict: PASS"]
|
|
20
|
+
G -->|out of bounds| FAIL["verdict: FAIL (severity)"]
|
|
21
|
+
OK --> N["LLM narrates / answers (grounded in the finding, not the raw data)"]
|
|
22
|
+
FAIL --> N
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Where guardrails come from (the three sources)
|
|
26
|
+
|
|
27
|
+
Every bound lives in `uav-thresholds.yaml` and carries its source, so a finding can cite
|
|
28
|
+
*why* the bound is what it is — grounding the explanation, not just the verdict.
|
|
29
|
+
|
|
30
|
+
1. **OEM datasheet** — component-specific limits (battery min voltage, max current,
|
|
31
|
+
internal-resistance ceiling, ESC/motor ratings). The authoritative bounds for a given
|
|
32
|
+
airframe's parts. Hydron already RAG-indexes datasheets, so these can be retrieved per
|
|
33
|
+
component rather than hand-typed: **call `query_hydron_project`** with the component +
|
|
34
|
+
the limit you need (e.g. *"min cell voltage and max continuous current for the Tattu
|
|
35
|
+
6S 16000mAh on airframe X"*) and cite the returned value + its citation as the finding's
|
|
36
|
+
source. Only do this when a project with the datasheet ingested is selected; otherwise
|
|
37
|
+
fall back to `uav-thresholds.yaml`.
|
|
38
|
+
2. **Rule-of-thumb** — industry constants that hold across vehicles (e.g. ≥ 2:1
|
|
39
|
+
thrust-to-weight for a multicopter, a healthy hover-throttle band, vibration < 30 m/s²).
|
|
40
|
+
A small curated rules-of-thumb library in the pack.
|
|
41
|
+
3. **Per-airframe tuned** — values the lab sets for a specific vehicle in
|
|
42
|
+
`uav-thresholds.yaml`, refined over its tuning cycle.
|
|
43
|
+
|
|
44
|
+
**Precedence:** per-airframe tuned > OEM datasheet > rule-of-thumb default.
|
|
45
|
+
|
|
46
|
+
## Retrieval ladder — how to source a bound that isn't already in `uav-thresholds.yaml`
|
|
47
|
+
|
|
48
|
+
The verdict is ALWAYS deterministic (the harness compares the measured value to a bound).
|
|
49
|
+
These steps only supply *which bound to use and where it came from* — its citation. Walk
|
|
50
|
+
the ladder in order and stop at the first that yields a sourced bound; record which tier +
|
|
51
|
+
the citation on the finding:
|
|
52
|
+
|
|
53
|
+
1. **Local config** — `<project>/.hydron/uav-thresholds.yaml` (tuned / customer-stated).
|
|
54
|
+
Authoritative; no lookup needed. Create/edit this file to set or change a bound.
|
|
55
|
+
2. **KG / RAG** — `query_hydron_project` for an **ingested** OEM datasheet limit (cite the
|
|
56
|
+
returned citation). Only when a project with that datasheet is selected.
|
|
57
|
+
3. **Web** — a **web-search MCP (Exa by default)** when the bound is **not in the KG**:
|
|
58
|
+
`exa_web_search_exa` / `exa_web_fetch_exa` to find the OEM datasheet spec or the
|
|
59
|
+
published rule-of-thumb, then **cite the URL**. This is the "knowledge grounding outside
|
|
60
|
+
the KG" path — datasheets the lab never ingested, community tuning guides, manufacturer
|
|
61
|
+
PDFs.
|
|
62
|
+
|
|
63
|
+
If no tier yields a defensible bound, **say so and surface the raw value for the engineer**
|
|
64
|
+
— never invent a threshold. A web/KG result is a *source for a bound*, not a verdict; the
|
|
65
|
+
harness still decides PASS/FAIL. Treat web results with appropriate skepticism (prefer the
|
|
66
|
+
manufacturer's own datasheet over a forum post) and always attach the link so the engineer
|
|
67
|
+
can verify.
|
|
68
|
+
|
|
69
|
+
## Grounding applies to the system tier only
|
|
70
|
+
|
|
71
|
+
Only **system** findings (the built-in inspection-order checks) are guardrail-grounded
|
|
72
|
+
and enter the regression record / comparison. **User** findings (ad-hoc `uav_query`) are
|
|
73
|
+
exploratory, tagged `tier:'user'` with their query provenance, and are **not** part of
|
|
74
|
+
the canonical grounded comparison. They answer a question; they do not assert a grounded
|
|
75
|
+
verdict. Never fold a user query's result into the regression record or present it as a
|
|
76
|
+
grounded verdict.
|
|
77
|
+
|
|
78
|
+
## The hard tier — correlation checks
|
|
79
|
+
|
|
80
|
+
Simple bound checks (battery V/I/resistance) are deterministic and easy. IMU/EKF drift
|
|
81
|
+
needs multi-step analysis: trends, cross-signal correlation, regime awareness — not a
|
|
82
|
+
single scalar threshold. Grounding matters most here, precisely because it is where a
|
|
83
|
+
model is tempted to hand-wave. Even these resolve to explicit, explainable criteria, not
|
|
84
|
+
vibes.
|
|
85
|
+
|
|
86
|
+
## Work-style rules (cite this when narrating)
|
|
87
|
+
|
|
88
|
+
- **Chain-of-thought** — reason step-by-step over the findings before concluding.
|
|
89
|
+
- **Ask clarifying questions over assuming** — when vehicle type, configuration, regime,
|
|
90
|
+
or intent is ambiguous, ask; don't guess.
|
|
91
|
+
- **Cite the guardrail** — every anomaly states the bound it breached and that bound's
|
|
92
|
+
source (datasheet / rule-of-thumb / tuned), never an unsourced number.
|
|
93
|
+
- **Surface, don't over-verdict** — rank anomalies; leave the final call to the engineer.
|
|
94
|
+
Investigation is case-by-case.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Project config files (optional per-project overrides)
|
|
2
|
+
|
|
3
|
+
These tune the pack per project/lab. They live under `.hydron/` at the project root. All are
|
|
4
|
+
OPTIONAL: the tools ship built-in defaults and work without them. To customise, **WRITE the
|
|
5
|
+
file directly at the exact path below** — the tools read only these paths.
|
|
6
|
+
|
|
7
|
+
Merge semantics differ:
|
|
8
|
+
- The two YAML files are **MERGED** (deep-merged) over the built-in defaults (which are baked
|
|
9
|
+
into the pack, not shipped as files). Author them from scratch listing only the keys you are
|
|
10
|
+
overriding — everything else keeps its default.
|
|
11
|
+
- `apm-params.json` is different: it **REPLACES** the bundled snapshot wholesale. A partial
|
|
12
|
+
file drops every param you omit, so include the full set you need. The baseline snapshot
|
|
13
|
+
ships at the plugin's own `data/apm-params.json`.
|
|
14
|
+
|
|
15
|
+
Files:
|
|
16
|
+
|
|
17
|
+
- `<project>/.hydron/uav-thresholds.yaml` — per-airframe anomaly guardrail bounds. Deep-merged
|
|
18
|
+
over the built-in defaults; this is the authoritative first rung when sourcing a bound.
|
|
19
|
+
- `<project>/.hydron/param-rules.yaml` — curated `{param: {min, max, severity}}` range/safety
|
|
20
|
+
rules for `uav_param_audit`. Merged over the built-in ruleset.
|
|
21
|
+
- `<project>/.hydron/apm-params.json` — ArduPilot param metadata (name → `{min, max, units}`).
|
|
22
|
+
REPLACES the snapshot bundled with the plugin wholesale (not merged) when present.
|
|
23
|
+
- `<project>/.hydron/uav-format-converters.json` — registry of custom log-format converters
|
|
24
|
+
(proprietary formats → a known format, applied before decode). No default; add only if needed.
|
|
25
|
+
|
|
26
|
+
When a user asks to change a bound, a param range, or add a custom log format, **write to the
|
|
27
|
+
matching file above — do not hard-code the value** anywhere else.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Signal dictionary + analysis method (pick the RIGHT column before you plot)
|
|
2
|
+
|
|
3
|
+
Choosing the wrong column silently produces wrong conclusions (e.g. correlating vibration
|
|
4
|
+
against `CTUN.ThH` — a near-constant hover estimate — falsely shows "no relationship").
|
|
5
|
+
Look the signal up here first. Columns follow the sample log; the decoder is FMT-driven, so
|
|
6
|
+
verify per-firmware.
|
|
7
|
+
|
|
8
|
+
## Throttle (the classic trap)
|
|
9
|
+
- **`RCOU.C1…C8`** — actual **motor PWM output** (~1000–2000 µs). Use the **mean of the active
|
|
10
|
+
motors** as the true instantaneous throttle/effort. ← use this for "throttle".
|
|
11
|
+
- **`CTUN.ThI`** — throttle **input** (pilot demand, 0–1). **`CTUN.ThO`** — throttle **output**
|
|
12
|
+
(0–1, post-mix). Either is a valid throttle proxy.
|
|
13
|
+
- **`CTUN.ThH`** — throttle **hover**: the *slowly-learned* hover-throttle estimate
|
|
14
|
+
(`MOT_THST_HOVER`), **≈ constant over a flight**. **NOT instantaneous throttle** — never use
|
|
15
|
+
it as the throttle axis.
|
|
16
|
+
|
|
17
|
+
## Vibration / IMU
|
|
18
|
+
- `VIBE.VibeX/Y/Z` (m/s²) is **whole-airframe vibration sensed at the IMU**, reported **per IMU
|
|
19
|
+
instance** (`VIBE.IMU` = which IMU). It is **NOT per-motor** vibration — the log does not
|
|
20
|
+
attribute vibration to a specific motor. Label it "airframe vibration (IMU N)", never
|
|
21
|
+
"motor N vibration". Per-**motor** signals are `RCOU.Cn` (output PWM) and `ESC` (per-ESC
|
|
22
|
+
RPM/temp); to *attribute* a vibration peak to a motor you must correlate the FFT peak Hz with a
|
|
23
|
+
motor's RPM (`ESC.RPM`/`RPM` × poles), not read it off VIBE.
|
|
24
|
+
- `VIBE.Clip0/1/2` = cumulative clip counts (per IMU).
|
|
25
|
+
- `IMU.GyrX/Y/Z`, `AccX/Y/Z`; `IMU.I` = instance (interleaved — de-interleave for FFT);
|
|
26
|
+
`EG/EA` = gyro/accel error counts; `T` = temp; `GHz/AHz` = sample rates.
|
|
27
|
+
|
|
28
|
+
## Attitude / rates (tracking error = desired − actual)
|
|
29
|
+
- `ATT.DesRoll/Roll`, `DesPitch/Pitch`, `DesYaw/Yaw` (deg).
|
|
30
|
+
- `RATE.RDes/R`, `PDes/P`, `YDes/Y` (deg/s) + `*Out` (controller output).
|
|
31
|
+
|
|
32
|
+
## Altitude / position
|
|
33
|
+
- `CTUN.DAlt/Alt` (desired/achieved), `BAlt` (baro), `DCRt/CRt` (climb rate).
|
|
34
|
+
|
|
35
|
+
## Battery / power / ESC
|
|
36
|
+
- `BAT.Volt`, `VoltR` (resting), `Curr`, `CurrTot`, `Res` (internal resistance), `Temp`, `RemPct`.
|
|
37
|
+
- `POWR.Vcc` (board 5V rail). `ESC.RPM/Volt/Curr/Temp/MotTemp/Err` (per-ESC). `MOTB.LiftMax/ThrOut/ThLimit/FailFlags`.
|
|
38
|
+
|
|
39
|
+
## EKF (XKF* = EKF3, NKF* = EKF2)
|
|
40
|
+
- Innovations `XKF3`/`NKF3`: **velocity** `IVN/IVE/IVD` (+`IVT`), **position** `IPN/IPE/IPD`.
|
|
41
|
+
Vibration-driven stress shows in velocity innov; GPS issues in position innov.
|
|
42
|
+
- Variances `XKF4`/`NKF4`: `SV/SP/SH/SM`; `FS` = failsafe flags.
|
|
43
|
+
|
|
44
|
+
## Mag / GPS
|
|
45
|
+
- `MAG.MagX/Y/Z` (field), `OfsX/Y/Z` (offsets), `MOX/Y/Z` (motor compensation), `Health`.
|
|
46
|
+
- `GPS.NSats/HDop/Status`; `GPA.HAcc/VAcc/SAcc` (accuracy estimates).
|
|
47
|
+
|
|
48
|
+
## Method (general — fit the analysis to the data)
|
|
49
|
+
- **Explore first** with `uav_query` (SQL) and/or **Python** (`bash`: `python3` + pandas/numpy
|
|
50
|
+
over `uav_export` CSVs, or pymavlink on the log) — check columns, units, ranges, sample
|
|
51
|
+
counts, distribution, outliers. Reason before you plot.
|
|
52
|
+
- **Don't force one statistic.** Inspect the *shape* of a relationship (linear / threshold /
|
|
53
|
+
none / time-varying) and pick a summary that fits it. A single correlation can mislead —
|
|
54
|
+
clustered/narrow ranges, nonlinearity, and outliers all distort it — so per-bucket trends,
|
|
55
|
+
medians, and robust/aggregate measures often tell the real story. Report the evidence and
|
|
56
|
+
the caveats, not just a coefficient.
|
|
57
|
+
- Use a **representative** signal (aggregate noisy or biased channels rather than one arbitrary
|
|
58
|
+
sample). Confirm the column means what you think (above) before trusting the result.
|
|
59
|
+
- `uav_plot`: derived exprs for error signals (`ATT.DesRoll - ATT.Roll`), `xy` for correlations,
|
|
60
|
+
`fft` for spectra — the final picture after the reasoning, not a substitute for it.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Methodical tuning — the ordered playbook (generic, airframe-agnostic)
|
|
2
|
+
|
|
3
|
+
How to turn a log into **reasoned, ordered parameter suggestions** — the loop a good tuner
|
|
4
|
+
runs by hand, and what the Autopilot Methodical Configurator (AMC) automates. This is
|
|
5
|
+
METHODOLOGY, not a value table: you read the evidence from the log (`uav_query`/`uav_python`),
|
|
6
|
+
you take every parameter's valid **range/default from `apm.pdef.xml`** (via `uav_param_audit` —
|
|
7
|
+
never invent a range), and you suggest a *direction and magnitude* with justification. Actual
|
|
8
|
+
per-airframe target values are the lab's tuned profile, not baked in here.
|
|
9
|
+
|
|
10
|
+
Two hard rules, always:
|
|
11
|
+
- **Order matters — do not skip ahead.** Each stage assumes the ones above it are clean. Tuning
|
|
12
|
+
a PID loop on a vibrating airframe, or on top of a bad sensor source, chases ghosts.
|
|
13
|
+
- **Recommend only; change ONE thing; then re-fly and re-check.** You never write params (that's
|
|
14
|
+
the gated MAVLink link). Every suggestion cites the signal it's based on, the param + its
|
|
15
|
+
`apm.pdef.xml` range, the direction, and how to confirm it on the next log.
|
|
16
|
+
|
|
17
|
+
## Stage 0 — Pre-tune gates (must pass before ANY gain tuning)
|
|
18
|
+
If these fail, fix them first; do not touch PIDs.
|
|
19
|
+
- **Vibration / clipping**: `VIBE.VibeX/Y/Z` within bound, `VIBE.Clip*` not accumulating.
|
|
20
|
+
Clipping or high vibe corrupts the gyro the PIDs act on. → go to Stage 1.
|
|
21
|
+
- **Mechanical**: props balanced, frame rigid, IMU isolation intact (shows as broadband vibe).
|
|
22
|
+
- **Sensors healthy**: `MAG.Health`, GPS `NSats`/`HAcc`, `POWR.Vcc` rail, EKF not failsafing
|
|
23
|
+
(`XKF4.FS`). A bad sensor source is Stage 5 — but a *failing* one blocks everything.
|
|
24
|
+
|
|
25
|
+
## Stage 1 — Vibration & harmonic notch (FIRST)
|
|
26
|
+
Clean the gyro before touching gains. Use `uav_tune` (FFT → `INS_HNTCH_*`; see
|
|
27
|
+
`references/tuning.md`). Confirm the motor/prop peak, set the notch, re-fly, confirm the peak
|
|
28
|
+
dropped. Only then proceed.
|
|
29
|
+
|
|
30
|
+
## Stage 2 — Rate loop (inner PID: the core of the tune)
|
|
31
|
+
Signals: `RATE.RDes/R`, `PDes/P`, `YDes/Y` (deg/s) and `*Out` (controller output).
|
|
32
|
+
Read from the log with `uav_python`:
|
|
33
|
+
- **Tracking error** = desired − actual per axis. Large steady error → P too low.
|
|
34
|
+
- **Oscillation** on the *actual* trace (and on `*Out`) → gain too high (usually D, then P).
|
|
35
|
+
- **Lag / mushy response** → P/D too low, or `FLTD`/`FLTT` filters too aggressive.
|
|
36
|
+
|
|
37
|
+
Params (ranges from `apm.pdef.xml`): `ATC_RAT_RLL_P/I/D`, `ATC_RAT_PIT_P/I/D`,
|
|
38
|
+
`ATC_RAT_YAW_P/I/D`, plus `_FLTD` (D-term filter), `_FLTT` (target filter), `_FLTE`. Standard
|
|
39
|
+
heuristics: raise D until high-freq oscillation appears then back off ~25%; set P near D×10 as a
|
|
40
|
+
start; keep I so that I/P stays in the usual band. **One axis, one gain, at a time.** Justify each
|
|
41
|
+
from the tracking-error/oscillation evidence you measured.
|
|
42
|
+
|
|
43
|
+
## Stage 3 — Attitude (angle) loop (outer)
|
|
44
|
+
Only after the rate loop is crisp. Signals: `ATT.DesRoll/Roll`, `DesPitch/Pitch`, `DesYaw/Yaw`.
|
|
45
|
+
Sluggish angle response with a clean rate loop → raise `ATC_ANG_RLL_P` / `ATC_ANG_PIT_P` /
|
|
46
|
+
`ATC_ANG_YAW_P`. Overshoot → lower. These are usually a light touch.
|
|
47
|
+
|
|
48
|
+
## Stage 4 — Position / altitude / velocity loops
|
|
49
|
+
Signals: `CTUN.DAlt/Alt`, `DCRt/CRt` (climb-rate track), horizontal pos/vel from `XKF1`/`POS`.
|
|
50
|
+
Params: `PSC_POSZ_P`, `PSC_VELZ_P`, `PSC_ACCZ_P/I/D` (vertical), `PSC_POSXY_P`, `PSC_VELXY_*`
|
|
51
|
+
(horizontal). Poor altitude hold that is NOT a sensor problem → adjust the vertical PSC chain.
|
|
52
|
+
But first rule out the sensor source (Stage 5) — noisy altitude is usually source, not gain.
|
|
53
|
+
|
|
54
|
+
## Stage 5 — Sensor source & fusion (EK3_SRC) — esp. altitude
|
|
55
|
+
The EKF blends sources; picking the wrong primary shows as noise the PIDs can't fix.
|
|
56
|
+
- **Barometer is noisy near the ground** (prop-wash, ground effect). For low-altitude precision,
|
|
57
|
+
prefer **GPS** or a **rangefinder / 1-D lidar** for height. Params: `EK3_SRC1_POSZ`
|
|
58
|
+
(1=baro, 2=GPS, 3=rangefinder, …), `RNGFND*`, `EK3_RNG_USE_HGT`.
|
|
59
|
+
- Horizontal source: `EK3_SRC1_POSXY` / `_VELXY` (GPS vs beacon vs optical flow); yaw:
|
|
60
|
+
`_YAW` (compass vs GPS-yaw vs external).
|
|
61
|
+
- Evidence: compare `CTUN.BAlt` (baro) noise vs GPS/rangefinder height over the same window with
|
|
62
|
+
`uav_python`; if baro is the noisy one at low altitude, recommend switching `POSZ` source (or
|
|
63
|
+
blending) — with the range/enum from `apm.pdef.xml`.
|
|
64
|
+
|
|
65
|
+
## Stage 6 — Drift over the sortie (endurance / large airframes)
|
|
66
|
+
Gyro/estimator drift matters more on **long sorties** and **large airframes** (heavy airframes
|
|
67
|
+
lean on the estimator; small ones are GPS-compensated at ~5 Hz). Method:
|
|
68
|
+
- Trend the EKF innovation/variance over time, not just the peak: `XKF4.SV/SP/SH/SM` and
|
|
69
|
+
`XKF3` innovations (`uav_python`, binned over the flight). A steady upward drift in a variance
|
|
70
|
+
or a growing bias signals estimator/gyro drift.
|
|
71
|
+
- The **acceptable drift bound scales with the airframe** — so it is a **tunable per-airframe
|
|
72
|
+
threshold** (the lab's profile), not a fixed number here. Report the observed drift rate and
|
|
73
|
+
compare it against the airframe's bound if one is configured; otherwise surface the trend and
|
|
74
|
+
say no absolute bound is set.
|
|
75
|
+
|
|
76
|
+
## Closing the loop (every stage)
|
|
77
|
+
Suggest → (human) gate-write over the MAVLink param link → re-fly → re-run the relevant
|
|
78
|
+
analysis on the new log → confirm the metric moved the right way. Cite the before/after. Tag the
|
|
79
|
+
retest (`uav_tag`) so `uav-compare` can track the tuning trajectory across flights.
|