Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
012d195
Add quaternion based orientation hold for fixed wing (inverted / knif…
swissembedded Jul 5, 2026
45f939b
Replace swing-twist heading removal with reduced attitude error
swissembedded Jul 5, 2026
f7d1b47
Add switchable altitude floor with automatic upright+climb recovery
swissembedded Jul 5, 2026
b295621
Add thrust vectoring servo mixer inputs with inverse thrust compensation
swissembedded Jul 5, 2026
7d61bec
Add per-attitude pitch trim to the orientation hold targets
swissembedded Jul 5, 2026
313d54a
Split knife edge pitch trim per side
swissembedded Jul 5, 2026
dad18db
Add figure sequencer with altitude assist (roll / loop / 4-point roll)
swissembedded Jul 6, 2026
740af30
Add programmable figure sequences with precondition gates
swissembedded Jul 6, 2026
e91487c
Reset rate-loop I-term on orientation hold target switches
swissembedded Jul 6, 2026
0788417
Add 3D LOCK mode: attitude lock on stick release
swissembedded Jul 6, 2026
ef5d3dd
Add MSP2_INAV_ORIENTATION_HOLD_TEST level-1 injection command
swissembedded Jul 6, 2026
92d4e61
Add hover throttle: altitude hold in the prop hang (doc section 5)
swissembedded Jul 6, 2026
b0a7031
Add IMPULSE (post-stall entry) and WAIT_POS (containment) segments
swissembedded Jul 7, 2026
c859383
Fix MSP_BOXNAMES overflow: shorter box names + SITL MSP out-buffer
swissembedded Jul 7, 2026
0950436
Keep pidOrientationHold out of ITCM (NOINLINE)
swissembedded Jul 8, 2026
5a43dca
docs: regenerate Settings.md (update_cli_docs.py)
swissembedded Jul 8, 2026
ffebe99
Orientation holds: active altitude assist (shared with figure sequencer)
swissembedded Jul 10, 2026
90b4918
Orientation holds: engage the altitude assist only once the attitude …
swissembedded Jul 10, 2026
44b7a4d
Orientation holds: roll like a pilot into near-antipodal targets
swissembedded Jul 10, 2026
9317423
Orientation holds: continuous axis blend instead of hard antipodal th…
swissembedded Jul 11, 2026
599575c
Orientation holds: persistent slewed target quaternion (v2 core)
swissembedded Jul 11, 2026
3e0c204
Prop hang: configurable hover throttle floor (ohold_hover_thr_min)
swissembedded Jul 11, 2026
44e2bac
Prop hang: hover throttle floor covers thrust vectoring too (doc)
swissembedded Jul 11, 2026
fb54d18
Prop hang: learn the damping reserve instead of hand-tuning a hover gain
swissembedded Jul 11, 2026
4b92db1
Prop hang: widen the limit-cycle detector band to 8 Hz
swissembedded Jul 11, 2026
3c5993e
Prop hang: persist the learned hover gain (ohold_hover_gain)
swissembedded Jul 11, 2026
d713856
docs: regenerate Settings.md (update_cli_docs.py)
swissembedded Jul 11, 2026
f675a23
TVC: raise the tvc_gain ceiling to 400 percent
swissembedded Jul 11, 2026
c9e40f9
Orientation holds: error leash with target pullback (qacro pattern)
swissembedded Jul 11, 2026
d5409f7
Orientation holds: own slew rate for preset entries (ohold_entry_rate)
swissembedded Jul 11, 2026
e7e863e
Orientation holds: stick deflection carves the target (ANGLE semantics)
swissembedded Jul 11, 2026
211fe8c
Orientation holds: the altitude assist yields to a deliberate pitch i…
swissembedded Jul 11, 2026
cab0e73
SITL: --lockstep, simulated time advances 1 ms per MSP_SIMULATOR frame
swissembedded Jul 11, 2026
73d6812
SITL lockstep: widen the frozen-clock window while RX bytes wait
swissembedded Jul 11, 2026
4bad112
SITL lockstep: anchor the creep window on every byte arrival
swissembedded Jul 11, 2026
5171d4a
SITL lockstep: mutex the clock state, the anchors were torn
swissembedded Jul 11, 2026
1052ad4
SITL lockstep: keep the creep window open while RX bytes wait unparsed
swissembedded Jul 11, 2026
c3a9594
SITL lockstep: absolute tick grid, never inherit the high-water mark
swissembedded Jul 12, 2026
83714d2
Flat spin: FLAT SPIN flight mode plus a SPIN figure segment
swissembedded Jul 12, 2026
4a68cfe
Prop hang: the learned gain applies only inside the hover regime
swissembedded Jul 12, 2026
e9d46ea
Prop hang: hover throttle defaults from the rig sweep (P 85, D 100)
swissembedded Jul 12, 2026
3b6974a
Orientation hold: exit handover slew toward ANGLE
swissembedded Jul 12, 2026
d2d2947
Knife/inverted throttle assist: criterion vz -> 0 (ohold_assist_thr_p/i)
swissembedded Jul 12, 2026
ca09277
Figure line-hold: earth-anchored trajectories, full attitude error
swissembedded Jul 12, 2026
3712ea1
Hover throttle: pilot corrects via climb-rate command, not override
swissembedded Jul 12, 2026
032649f
docs: regenerate Settings.md (ohold_assist_thr_p/i)
swissembedded Jul 12, 2026
edf4844
Hover regime: raise the baro position weight (ohold_hover_baro_weight)
swissembedded Jul 12, 2026
b079fbc
FLAT SPIN generalized: spin about the earth vertical in any held atti…
swissembedded Jul 12, 2026
d4a7f4f
Spin stick sense: aircraft-referenced, not earth-referenced
swissembedded Jul 12, 2026
f229856
Learned damping reserve generalized: one scale per regime
swissembedded Jul 12, 2026
28f75ff
Altitude ownership follows the attitude, not the selected box
swissembedded Jul 12, 2026
9c483b3
Knife edge speed feedforward (ohold_knife_speed_ff)
swissembedded Jul 12, 2026
322338b
Knife/inverted assist: keep the chosen speed as the forward component
swissembedded Jul 12, 2026
431f5b1
Stall reserve monitor: control effort is the EARLY speed-raise criterion
swissembedded Jul 12, 2026
a4613c5
Crash detection: impact followed by stillness stops the motor
swissembedded Jul 12, 2026
d27c673
GPS altitude requires a solid constellation (lock-quality Z gate)
swissembedded Jul 12, 2026
5169bbb
Crash detection: motor cut with re-allow gesture instead of disarm
swissembedded Jul 12, 2026
d3defc1
crash detection: baro-referenced stillness + GPS ground-speed gate
swissembedded Jul 13, 2026
bfaeda9
altitude floor: the recovery climb gets its own energy
swissembedded Jul 13, 2026
d962e18
altitude floor: pilot takeover releases the recovery
swissembedded Jul 13, 2026
9078bc3
sitl: no CPU-load arming gate in a SITL build
swissembedded Jul 13, 2026
cf607ac
sitl: skip the CPU-load arming gate only under --lockstep
swissembedded Jul 13, 2026
a8696c1
docs: OrientationHold feature guide (wiki-ready)
swissembedded Jul 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions docs/OrientationHold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Orientation Hold: 3D aerobatics for fixed wing

*(wiki-ready: this page is written so it can be pasted into the INAV
wiki as-is once the feature merges)*

Orientation Hold is a flight mode family that lets a fixed-wing model
hold ANY attitude: sustained inverted flight, knife edge (either side),
a prop hang with hands-free hover throttle, controlled flat spins, and
scripted aerobatic figures flown on a line. It is a quaternion
controller, so there is no gimbal lock and no special-casing at pitch
90 - a loop is just "pitch rotation, 360 degrees".

Status: bench-validated against a closed-loop JSBSim simulation
(deterministic lockstep, 50-case gust matrix, replay videos); first
hardware flights are upcoming. Treat everything here as experimental.

## Requirements

- Fixed wing (`platform_type = AIRPLANE`). Multirotors are untouched.
- A target built with `USE_ORIENTATION_HOLD` (F7/H7 class; F411 fits).
- Barometer required (altitude assist, floor, hover throttle).
- GPS optional but recommended: it gates crash detection in flight and
hardens the altitude estimate during aerobatics.

## The modes

| Box | What it does |
| --- | --- |
| INVERT | holds sustained inverted level flight |
| KNIFE L / KNIFE R | holds a left / right knife edge |
| P-HANG | prop hang: nose vertical, hover throttle owns the altitude |
| FLAT SPIN | controlled spin about the earth vertical; combine with the holds |
| 3DLOCK | sticks centered = hold the current attitude, deflected = rate flying |
| FLOOR | altitude safety floor with automatic upright + climb recovery |
| F ROLL / F LOOP / F 4PT | one-switch figures: axial roll, loop, 4-point roll |
| F SEQ | flies a scripted figure sequence (programmed via MSP) |

INVERT, KNIFE L, KNIFE R and P-HANG are four separate boxes; the
natural mapping is one multi-position selector switch with one band per
hold, plus a separate switch for FLOOR and one for the figure bands.

### Holds (INVERT, KNIFE L/R, P-HANG)

Problem being solved: flying inverted or on the knife edge by hand
means holding constant corrective pressure, and any distraction ends
the maneuver. The hold takes over the attitude; you keep flying.

- Engaging a hold slews the target from your current attitude to the
hold at `ohold_entry_rate` - no snap, no 180-degree surprise.
- Holds are heading-free: the rotation about the earth vertical stays
yours. Rudder steers in level/inverted flight; at the prop hang the
free axis is body roll (that is the torque-roll axis).
- Sticks carve held angle offsets from the hold (ANGLE semantics,
`ohold_stick_angle`); releasing returns the target gently. Yaw is
always a rate.
- Leaving a hold far from level hands over to ANGLE with a slew to the
horizon, so a hover exit does not whip through nose down.
- Per-attitude pitch trims: `ohold_inverted_pitch_trim`,
`ohold_knife_left_pitch_trim`, `ohold_knife_right_pitch_trim` (the
sides are separate on purpose - prop effects are not symmetric).

### Throttle behavior per hold

Holding the attitude is half the job; each hold also declares what the
throttle means:

- **P-HANG**: a hover throttle PID owns the altitude. Your throttle
stick commands a CLIMB RATE around the point where you engaged;
slamming the stick low remains a hard cut (bailout). The hover base
throttle is learned from your own throttle at engage - there is no
per-model hover setting to find. Altitude ownership follows the
ATTITUDE: pull a knife edge up into a harrier and the hover
controller takes the altitude over seamlessly.
- **KNIFE / INVERT**: the base is your throttle scaled so the forward
thrust component keeps the speed you chose; a slow vz-to-zero trim
adds power while the hold sinks (`ohold_assist_thr_p/i`), and a speed
feedforward puts more nose on the knife immediately when the throttle
(the v-squared proxy without an airspeed sensor) is low
(`ohold_knife_speed_ff`).
- **Stall reserve**: sustained control effort toward saturation raises
power while the attitude still looks clean - the early warning. By
the time an attitude degrades, the escalation chain has already gone
through effort trend, then sinking, then oscillation.

### FLAT SPIN family

Problem being solved: a spin mode wired to body yaw is only correct in
a flat attitude - inverted or knife-edge spins would be impossible.

The spin command is a rotation about the EARTH VERTICAL - exactly the
axis the holds leave free - distributed onto the body axes from the
current tilt. The identical mode therefore does:

- FLAT SPIN alone: the classic upright flat spin,
- FLAT SPIN + INVERT: inverted flat spin,
- FLAT SPIN + KNIFE L/R: knife-edge spin,
- FLAT SPIN + P-HANG: torque roll.

Your rudder commands the rotation rate, with aircraft-referenced sense:
right rudder spins the airframe right when upright AND when inverted -
seen from above, an inverted spin reverses, like a real aircraft.
Releasing the rudder stops the rotation with the attitude still held;
releasing the box recovers.

### 3DLOCK

Sticks centered: the current attitude is captured and held. Sticks
deflected: pure rate flying, and the lock follows - it freezes on
whatever attitude you had when the sticks came back to center. Think of
it as "hold whatever I'm doing" for improvised 3D.

### FLOOR (altitude safety floor)

Problem being solved: practicing low 3D means a mistake reaches the
ground before you do.

- Set the floor with `alt_floor_altitude` (meters above home). The
floor ARMS only after you have climbed above floor + margin once, so
switching it on before takeoff never grabs the aircraft.
- A predicted breach (sink rate looked ahead a few seconds) engages an
automatic upright + climb recovery that OVERRIDES the selected mode.
It catches out of a dive with the elevator still held, and out of a
spin.
- The recovery brings its own energy: a throttle floor of cruise +
pitch compensation, the motor keeps running through a panic-chopped
stick, and held roll/pitch sticks are ignored (they used to drag the
recovery target down). Yaw stays live for steering.
- The climb ends at floor + `alt_floor_margin`. To take over earlier:
center the sticks once, then any fresh roll/pitch input hands control
back immediately. Switching the box off always ends it.

### Figures (F ROLL, F LOOP, F 4PT, F SEQ)

One-switch figures fly an axial roll, a loop (`fig_loop_rate` - radius
is rate and speed: R = v / omega) or a 4-point roll. F SEQ flies a
scripted sequence of segments (roll / pitch / hold / wait-altitude /
wait-time / impulse / wait-position / spin), programmed over MSP;
community tooling can turn a written routine into such a script.

Figures fly ON A LINE: the heading captured at figure start anchors the
trajectory, and the full attitude error is regulated - a slow roll
stays on its string instead of walking off course. An altitude assist
holds the entry altitude through the figure. After the last segment the
sequencer is done - switch back to your normal mode; there is no
automatic level-off yet.

### Crash detection (`crash_g_threshold`)

Problem being solved: after an unscheduled arrival the prop keeps
churning until you walk over and disarm.

An impact spike above the threshold, followed by the airframe lying
still (no rotation, resting 1 g, frozen raw baro, and - with a GPS
fix - zero ground speed) CUTS the motor while staying armed. Moving the
throttle to zero and up again re-allows it deliberately: short motor
bursts are the most reliable way to find a plane in high grass or corn.
Hand-launch safe (it arms only once clearly flying). Opt-in, 0 = off.
Without GPS, keep the threshold above your figures' g load - a smooth
level line right after a hard pull is indistinguishable from lying
still on IMU + baro alone.

## Learned gains

Normal-flight gains are the reference. Each hold regime (hover;
inverted/knife/figures) learns its own damping scale from its own limit
cycles and persists it on disarm - fly a figure repeatedly and it gets
better. No per-regime hand tuning.

## Setup order

1. Trim the airframe physically first (level trim, CG via the 45-degree
inverted test, per-side knife-edge coupling, thrust line, aileron
differential). Never paper over a bad CG with software trim.
2. Set the per-attitude pitch trims from those flights.
3. Let the hover gain learner converge (a few prop hangs).
4. Then figure rates and the altitude assist.

All `ohold_*`, `alt_floor_*`, `fig_*` and `crash_g_threshold` settings
are documented in [Settings.md](Settings.md).

## Simulation

Everything above can be flown against a JSBSim plant through
`MSP_SIMULATOR`. SITL gained a deterministic lockstep mode
(`--lockstep`): the sim clock advances exactly 1 ms per injected frame,
so the same input produces the same flight bit for bit, host load does
not matter, and many SITL instances can run in parallel. Replay videos
of every mode and several scripted routines live in the companion bench
repository, all flown on one configuration.
Loading