Gait: keyboard detent snaps the lever out of the walk/run DEAD BAND + KB
Byte-decoded finding [T1]: the gait SM has no stable state for a demand in
(walkStrideLength@0x534, reverseSpeedMax@0x538). Cap semantics settled from
LoadLocomotionClips @0x4a80d4: 0x538 is the walk->run TRANSITION CLIP's exit
speed (the run ENGAGE threshold -- not a reverse max) and 0x34c (run-cycle avg
root speed) is the mapper's continuous demand multiplier (FUN_004afd10). The
finished-callbacks up-shift when tgt > 0x534 but enter/sustain run only when
tgt >= 0x538, so a demand parked between them (Blackhawk: 22.02-30.87 =
throttle 36-50%) hunts walk -> shift-up -> shift-down forever, firing the
authored EngineShiftFwd/Rev sounds each swing -- the user's repro, faithfully
reproduced by all-authentic logic + data. Likely masked on the pod by
MECHANICAL throttle-quadrant detents ("5 speeds" lore; the software path is
notch-free) [T4] -- added to get-from-Nick.
Accommodation (keyboard = our stand-in lever): at key REST, snap the lever to
the nearer dead-band edge (walk cap, or run engage + margin since the cont
check is >=). Sweeping THROUGH the band while held stays continuous -- an
authentic moving lever; the single shift it fires is the authentic shift.
BT_GAIT_TRACE logs [gaitdetent] snaps.
KB: locomotion.md gait-dead-band section, pod-hardware.md mechanical-notch
hypothesis, open-questions.md Nick item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
537749cae3
commit
aa004eb7b8
@@ -2775,6 +2775,45 @@ void
|
||||
|
||||
const int fwd = gBTDrive.keyFwd, back = gBTDrive.keyBack;
|
||||
if (!fwd && !back) sDetent = 0; // keys released -> detent re-arms
|
||||
// GAIT DETENT (keyboard accommodation, 2026-07-16): the gait SM has NO
|
||||
// stable state for a demand between the walk cycle's cap
|
||||
// (walkStrideLength @0x534) and the run cycle's engage speed
|
||||
// (reverseSpeedMax @0x538 = the walk->run transition clip's exit
|
||||
// speed). A demand PARKED in that band hunts walk -> shift-up ->
|
||||
// shift-down forever, firing the authored EngineShiftFwd/Rev sounds
|
||||
// each swing (binary-verified: the BodyClipFinished thresholds at
|
||||
// 0x4a6f11/0x4a6fc7 + the continuous FUN_004afd10 demand formula
|
||||
// reproduce the hunt with all-authentic data). The pod's PHYSICAL
|
||||
// throttle lever plausibly rested only at mechanical notches -- the
|
||||
// software path is notch-free (pod-hardware.md), so the notches, if
|
||||
// real, lived in the quadrant hardware [T4]. Reproduce that feel:
|
||||
// when the keys REST, snap the lever out of the dead band to the
|
||||
// nearer edge. Sweeping THROUGH the band while a key is held stays
|
||||
// continuous -- that is an authentic moving lever, and the one
|
||||
// transition it triggers is the authentic shift.
|
||||
if (!fwd && !back && sLever > 0.0f
|
||||
&& walkStrideLength > 0.0f
|
||||
&& reverseSpeedMax > walkStrideLength
|
||||
&& reverseStrideLength >= reverseSpeedMax)
|
||||
{
|
||||
const float max_demand = reverseStrideLength * forwardThrottleScale;
|
||||
if (max_demand > 0.0f)
|
||||
{
|
||||
const float band_lo = walkStrideLength / max_demand;
|
||||
// margin: the run cont-check is tgt >= cap, so land the demand
|
||||
// a hair ABOVE the engage speed against float rounding
|
||||
const float band_hi = reverseSpeedMax / max_demand + 0.002f;
|
||||
if (sLever > band_lo && sLever < band_hi)
|
||||
{
|
||||
const float snapped =
|
||||
(sLever - band_lo < (band_hi - band_lo) * 0.5f) ? band_lo : band_hi;
|
||||
{ static int s_dLog = 0; if (getenv("BT_GAIT_TRACE") && s_dLog++ < 40)
|
||||
DEBUG_STREAM << "[gaitdetent] lever " << sLever << " in dead band ["
|
||||
<< band_lo << "," << band_hi << ") -> " << snapped << "\n" << std::flush; }
|
||||
sLever = snapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
float sweep = ((fwd ? 1.0f : 0.0f) - (back ? 1.0f : 0.0f)) * kLeverRate * dt;
|
||||
if (sweep != 0.0f && !sDetent)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user