Never reference the ground attitude -- a flight-built hull lies on its side
The attitude reference was captured at the first sensor read, on the ground, where a ship built to fly typically lies over. That made every tilt reading relative to a meaningless datum, and worse: the hull's natural righting swing after liftoff was read both as tilt and as rate response, contaminating the very measurement the test exists to make. - Attitude reference is now taken in the air, after an explicit righting phase that waits for every axis rate to steady (5 s calm, 120 s cap). Until then maxTilt() reads 0, so nothing can trip on ground attitude. - The settled airborne attitude is recorded and reported per axis, with a warning if the hull settles far from level even under balanced lift. - Wizard now says outright that a ship lying over on the ground is fine. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
19
README.md
19
README.md
@@ -29,13 +29,18 @@ side drives which balloon, pitch sign, hover level) is measured on the ship by
|
|||||||
the **setup wizard**, which the install flow takes you straight into:
|
the **setup wizard**, which the install flow takes you straight into:
|
||||||
|
|
||||||
1. **Wiring** — the wizard pulses each free side; you say which burner fired.
|
1. **Wiring** — the wizard pulses each free side; you say which burner fired.
|
||||||
2. **Sign test** — the ship lifts off by itself, finds its own lift power, then
|
2. **Sign test** — the ship lifts off by itself, finds its own lift power,
|
||||||
*rocks* between front and aft pushes. The sign comes from
|
waits until buoyancy has righted it and the rates have steadied, and only
|
||||||
`rate(+push) − rate(−push)`, which cancels any constant lean exactly, so a
|
*then* takes its attitude reference. It rocks between front and aft pushes;
|
||||||
ship hanging crooked measures fine — that lean **is** the thing being
|
the sign comes from `rate(+push) − rate(−push)`, which cancels any constant
|
||||||
measured. It then learns the differential trim that levels the ship, saves
|
lean exactly, so a ship hanging crooked measures fine — that lean **is**
|
||||||
it as a warm-start, and descends to hover. No config editing, no freefall.
|
what is being measured. It then learns the differential trim that levels
|
||||||
Takes 2–4 minutes.
|
the ship, saves it as a warm-start, and descends to hover. No config
|
||||||
|
editing, no freefall. Takes 2–4 minutes.
|
||||||
|
|
||||||
|
A hull built to fly usually lies over on the ground; that is fine and
|
||||||
|
expected. The ground attitude is never used as a reference — nothing is
|
||||||
|
measured until the ship is airborne and steady.
|
||||||
|
|
||||||
After that the ship is ready: `FLY` from the menu or `ENGAGE` from the cockpit
|
After that the ship is ready: `FLY` from the menu or `ENGAGE` from the cockpit
|
||||||
console. Pitch and altitude keep self-learning in flight. The wizard can be
|
console. Pitch and altitude keep self-learning in flight. The wizard can be
|
||||||
|
|||||||
@@ -154,8 +154,12 @@ end
|
|||||||
-- mellom de to dyttene kansellerer enhver konstant skjev-drift eksakt. Og fordi vi VEKSLER mellom
|
-- mellom de to dyttene kansellerer enhver konstant skjev-drift eksakt. Og fordi vi VEKSLER mellom
|
||||||
-- dyttene gynger skipet rundt utgangspunktet i stedet for aa akkumulere tilt -- selvbegrensende.
|
-- dyttene gynger skipet rundt utgangspunktet i stedet for aa akkumulere tilt -- selvbegrensende.
|
||||||
--
|
--
|
||||||
|
-- BAKKE-ATTITYDEN BRUKES ALDRI: skipet er bygget for aa FLY, ikke for aa staa, saa paa bakken
|
||||||
|
-- ligger det gjerne paa siden. Vi venter til oppdriften har reist det opp OG ratene har roet seg
|
||||||
|
-- foer attityde-referansen settes -- ellers ville skipets egen oppretting blitt lest som respons.
|
||||||
|
--
|
||||||
-- 1/5 trapper seg luftbaaren fra bunnen (laerer ~hover paa kjoepet, ingen fast liftLevel)
|
-- 1/5 trapper seg luftbaaren fra bunnen (laerer ~hover paa kjoepet, ingen fast liftLevel)
|
||||||
-- 2/5 klatrer kontrollert til trygg maalehoyde (fart-P-hold rundt laert hover)
|
-- 2/5 klatrer til maalehoyde, lar skipet RETTE SEG OPP, setter attityde-referanse i lufta
|
||||||
-- 3/5 GYNGE-PROBE: front-dytt <-> bak-dytt, 2 sykluser; aksen som svarer blir pitchIndex.
|
-- 3/5 GYNGE-PROBE: front-dytt <-> bak-dytt, 2 sykluser; aksen som svarer blir pitchIndex.
|
||||||
-- Svak respons -> auto sterkere dytt. Maaler ALLE gimbal-akser (byggeretning likegyldig)
|
-- Svak respons -> auto sterkere dytt. Maaler ALLE gimbal-akser (byggeretning likegyldig)
|
||||||
-- 4/5 BALANSE: med fortegnet kjent laeres differanse-trimmen som holder skipet vannrett
|
-- 4/5 BALANSE: med fortegnet kjent laeres differanse-trimmen som holder skipet vannrett
|
||||||
@@ -196,12 +200,21 @@ local function runSigntest()
|
|||||||
if type(a) ~= "table" or type(a[1]) ~= "number" then return nil end
|
if type(a) ~= "table" or type(a[1]) ~= "number" then return nil end
|
||||||
if NAX == 0 then
|
if NAX == 0 then
|
||||||
NAX = math.min(3, #a)
|
NAX = math.min(3, #a)
|
||||||
a0 = {}
|
for i = 1, NAX do axRings[i] = utils.newRing(1.0) end
|
||||||
for i = 1, NAX do axRings[i] = utils.newRing(1.0); a0[i] = a[i] end
|
|
||||||
end
|
end
|
||||||
return a
|
return a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Attityde-referansen settes AaLDRI paa bakken: et skip bygget for aa FLY staar ikke stoett,
|
||||||
|
-- det ligger gjerne paa siden. Den attityden er meningsloes -- og oppdriftens egen oppretting
|
||||||
|
-- etter avgang ville blitt lest som baade "tilt" og "rate-respons". Vi setter referansen
|
||||||
|
-- FOERST naar skipet har rettet seg opp i lufta og ratene har roet seg.
|
||||||
|
local function setAttitudeRef()
|
||||||
|
if not lastAx then return end
|
||||||
|
a0 = {}
|
||||||
|
for i = 1, NAX do a0[i] = lastAx[i] end
|
||||||
|
end
|
||||||
|
|
||||||
local function axRates()
|
local function axRates()
|
||||||
local r = {}
|
local r = {}
|
||||||
for i = 1, NAX do r[i] = axRings[i]:slope() end
|
for i = 1, NAX do r[i] = axRings[i]:slope() end
|
||||||
@@ -338,13 +351,32 @@ local function runSigntest()
|
|||||||
|
|
||||||
local okC, cErr = recenter("2/5", "initial climb")
|
local okC, cErr = recenter("2/5", "initial climb")
|
||||||
if not okC then return bail(abortMsg(cErr)) end
|
if not okC then return bail(abortMsg(cErr)) end
|
||||||
local tSettle = now()
|
|
||||||
while now() - tSettle < 8 do
|
-- La skipet RETTE SEG OPP i lufta foer vi maaler noe som helst. Et flybygget skrog ligger
|
||||||
velTick(0, 0, "settle0")
|
-- gjerne paa siden paa bakken; oppdriften reiser det opp av seg selv naar det kommer i lufta.
|
||||||
hud("2/5", "Settling (learning true hover)...", {
|
-- Vi venter til ALLE akse-ratene har roet seg -- og setter FOERST DA attityde-referansen.
|
||||||
string.format("hover ~%.2f %.0fs left", hover, math.max(0, 8 - (now() - tSettle))),
|
local tR, calmR, worst = now(), 0, 0
|
||||||
|
while now() - tR < 120 and now() < deadline do
|
||||||
|
velTick(0, 0, "righting")
|
||||||
|
worst = 0
|
||||||
|
for i = 1, NAX do worst = math.max(worst, math.abs(axRings[i]:slope())) end
|
||||||
|
if worst < 0.5 then calmR = calmR + cfg.dt else calmR = 0 end
|
||||||
|
hud("2/5", "Letting the ship right itself...", {
|
||||||
|
"(a flight-built hull lies over on the ground;",
|
||||||
|
" buoyancy levels it once airborne)",
|
||||||
|
string.format("worst rate %.2f deg/s steady %.1f/5.0 s", worst, calmR),
|
||||||
|
string.format("hover ~%.2f", hover),
|
||||||
})
|
})
|
||||||
|
if calmR >= 5.0 then break end
|
||||||
|
local rel = est.altitude - h0
|
||||||
|
if rel < BAND_LO or rel > BAND_HI then
|
||||||
|
local okD, dErr = recenter("2/5", "altitude drift")
|
||||||
|
if not okD then return bail(abortMsg(dErr)) end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
setAttitudeRef() -- NAA er referansen meningsfull: skipets faktiske flyattityde
|
||||||
|
local settled = {}
|
||||||
|
for i = 1, NAX do settled[i] = (lastAx and lastAx[i]) or 0 end
|
||||||
|
|
||||||
-- rate-stabil maaling PER AKSE: hold hoyden (vDes=0) med gitt differanse til ratene roer
|
-- rate-stabil maaling PER AKSE: hold hoyden (vDes=0) med gitt differanse til ratene roer
|
||||||
-- seg. Returnerer tabell {avgRate per akse}. Lag-bevisst: minHold >= loft-etterslepet.
|
-- seg. Returnerer tabell {avgRate per akse}. Lag-bevisst: minHold >= loft-etterslepet.
|
||||||
@@ -476,6 +508,9 @@ local function runSigntest()
|
|||||||
term.clear(); term.setCursorPos(1, 1)
|
term.clear(); term.setCursorPos(1, 1)
|
||||||
print("==== SIGN TEST: SUCCESS ====")
|
print("==== SIGN TEST: SUCCESS ====")
|
||||||
print(string.format("pitch = gimbal axis a%d, sign %+d", axis, sign))
|
print(string.format("pitch = gimbal axis a%d, sign %+d", axis, sign))
|
||||||
|
local sline = ""
|
||||||
|
for i = 1, NAX do sline = sline .. string.format(" a%d %+.0f", i, settled[i]) end
|
||||||
|
print("airborne attitude:" .. sline)
|
||||||
print(string.format("balance trim %+.2f (saved warm-start)", 2 * levelTrim))
|
print(string.format("balance trim %+.2f (saved warm-start)", 2 * levelTrim))
|
||||||
if balanced then
|
if balanced then
|
||||||
print("Ship levelled out under this trim.")
|
print("Ship levelled out under this trim.")
|
||||||
@@ -483,6 +518,12 @@ local function runSigntest()
|
|||||||
print(string.format("NOTE: still %+.0f deg off level -- limited", pitchNow))
|
print(string.format("NOTE: still %+.0f deg off level -- limited", pitchNow))
|
||||||
print("pitch authority. FLY keeps learning it.")
|
print("pitch authority. FLY keeps learning it.")
|
||||||
end
|
end
|
||||||
|
if math.abs(settled[axis]) > 30 then
|
||||||
|
print("")
|
||||||
|
print("WARNING: it settled far from level even")
|
||||||
|
print("with balanced lift -- the hull may not be")
|
||||||
|
print("righting fully. Check balloon spacing.")
|
||||||
|
end
|
||||||
print(string.format("hover ~%.1f (saved as baseline)", hover))
|
print(string.format("hover ~%.1f (saved as baseline)", hover))
|
||||||
print("")
|
print("")
|
||||||
print("Ship is holding hover. Ready to FLY.")
|
print("Ship is holding hover. Ready to FLY.")
|
||||||
@@ -797,9 +838,12 @@ local function runSetup()
|
|||||||
print("Two steps, fully guided:")
|
print("Two steps, fully guided:")
|
||||||
print(" 1) Wiring -- map front/aft burner sides")
|
print(" 1) Wiring -- map front/aft burner sides")
|
||||||
print(" 2) Sign test -- short auto-flight that")
|
print(" 2) Sign test -- short auto-flight that")
|
||||||
print(" measures pitch response + hover")
|
print(" measures pitch response + balance")
|
||||||
print("")
|
print("")
|
||||||
print("Ship must be ON THE GROUND, open sky above.")
|
print("Just needs open sky above. It's FINE if the")
|
||||||
|
print("ship lies over on the ground -- it rights")
|
||||||
|
print("itself airborne, and nothing is measured")
|
||||||
|
print("until it has.")
|
||||||
print("")
|
print("")
|
||||||
write("[Enter] = start, q = cancel: ")
|
write("[Enter] = start, q = cancel: ")
|
||||||
if read():lower() == "q" then return end
|
if read():lower() == "q" then return end
|
||||||
@@ -811,10 +855,11 @@ local function runSetup()
|
|||||||
term.clear(); term.setCursorPos(1, 1)
|
term.clear(); term.setCursorPos(1, 1)
|
||||||
print("==== STEP 2: SIGN TEST ====")
|
print("==== STEP 2: SIGN TEST ====")
|
||||||
print("")
|
print("")
|
||||||
print("The ship now lifts off BY ITSELF, gives")
|
print("The ship lifts off BY ITSELF, waits until")
|
||||||
print("the front balloon a small push, verifies")
|
print("it has righted and steadied in the air,")
|
||||||
print("the response both ways, and comes back")
|
print("rocks front/aft to find the pitch axis,")
|
||||||
print("down to hover. Takes 1-3 minutes.")
|
print("learns the trim that levels it, then comes")
|
||||||
|
print("back down to hover. Takes 2-4 minutes.")
|
||||||
print("")
|
print("")
|
||||||
write("[Enter] = lift off, q = cancel: ")
|
write("[Enter] = lift off, q = cancel: ")
|
||||||
if read():lower() == "q" then return end
|
if read():lower() == "q" then return end
|
||||||
|
|||||||
Reference in New Issue
Block a user