Skip to content

Commit 1373aa7

Browse files
committed
fix: don't flag a locomotion stance foot's roll/swing as a failed flat plant
The clip grounding checks use static-pose thresholds, but a stance foot in a traveling clip is never statically flat: it rolls onto its ball as the body passes (toe planted, heel lifts = push-off), and at each step transition the pin alternates a beat before the landing foot is actually down (both heel and toe briefly airborne = swing). Both are correct gait, not grounding artifacts. In a locomotion clip (authored travel): - exempt a toe-planted, heel-lifted foot (push-off roll) from the flat-foot heel/toe/sole checks; - skip an airborne supported foot (sole well off the floor) as a swing/landing foot, extending the existing generic-`feet` swing-foot skip to pins. The endpoint contact-position check still catches a pin genuinely left off its anchor, so a real solver failure cannot hide. Static clips (squat, deadlift, plank, plié) keep the strict flat-foot bar. Cleared the large gait phase-transition heel/float warnings (box-step 0.121m -> 0.029m); constraint warnings 118 -> 109. 397/397 tests pass.
1 parent ff861a4 commit 1373aa7

2 files changed

Lines changed: 34 additions & 12 deletions

File tree

packages/posecode-eval/src/diagnostics.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ export interface ClipDiagnosticsCollector {
180180
finish(): ClipDiagnostics;
181181
}
182182

183-
export function createClipDiagnosticsCollector(sampleRateHz: number): ClipDiagnosticsCollector {
183+
export function createClipDiagnosticsCollector(
184+
sampleRateHz: number,
185+
isLocomotion = false,
186+
): ClipDiagnosticsCollector {
184187
const rate = Math.max(
185188
1,
186189
Math.min(120, Number.isFinite(sampleRateHz) ? sampleRateHz : DEFAULT_DIAGNOSTIC_SAMPLE_RATE_HZ),
@@ -195,16 +198,24 @@ export function createClipDiagnosticsCollector(sampleRateHz: number): ClipDiagno
195198
const state = feet[side];
196199
const foot = measureFootContact(m, side);
197200
let kind = supportKind(frame, side);
198-
// The generic `feet` group also contains a deliberately lifted swing
199-
// foot. Match ground-lock's own near-floor selection so that swing height
200-
// is not mislabeled as a failed planted contact; an explicit foot lock or
201-
// floor pin is always evaluated.
201+
// A supported foot whose sole is well off the floor is mid-swing, not
202+
// planted. Skip it in two cases: (1) the generic `feet` group's lifted
203+
// swing foot, and (2) any airborne foot in a locomotion clip — at a step
204+
// transition the stance pin alternates a beat before the landing foot is
205+
// actually down, so the descending foot is swinging, not a failed plant.
206+
// The endpoint contact-position check still catches a pin left airborne.
207+
const airborne =
208+
kind !== null
209+
&& foot !== null
210+
&& !isGroundLockFootPlanted(floorContactHeight(m, `foot_${side}`) ?? NaN);
202211
if (
203-
kind === "ground-lock"
204-
&& frame.groundLock.includes("feet")
205-
&& !frame.groundLock.includes(`foot_${side}`)
206-
&& foot
207-
&& !isGroundLockFootPlanted(floorContactHeight(m, `foot_${side}`) ?? NaN)
212+
airborne
213+
&& (
214+
(kind === "ground-lock"
215+
&& frame.groundLock.includes("feet")
216+
&& !frame.groundLock.includes(`foot_${side}`))
217+
|| isLocomotion
218+
)
208219
) kind = null;
209220
if (!kind || !foot) {
210221
state.supportKind = null;
@@ -220,8 +231,19 @@ export function createClipDiagnosticsCollector(sampleRateHz: number): ClipDiagno
220231
// knee-drive) legitimately shows a lifted heel/toe and a steep sole, so
221232
// measuring it as a failed flat plant fabricates warnings.
222233
const shinDeg = shinFromVerticalDeg(m, side);
234+
// A stance foot in a locomotion clip rolls onto its ball as the body
235+
// travels over it — the toe stays planted while the heel lifts (push-off).
236+
// That roll is correct gait, not a failed flat plant, so it is exempt from
237+
// the flat-foot checks. A fully airborne foot (toe also lifted) is not a
238+
// roll and stays measured; static clips keep the strict flat-foot bar.
239+
const pushOffRoll =
240+
isLocomotion
241+
&& Math.abs(foot.toeHeight) <= FOOT_CONTACT_HEIGHT_MAX
242+
&& foot.heelHeight > FOOT_CONTACT_HEIGHT_MAX;
223243
const expectedFlat =
224-
foot.plantigrade && (shinDeg === null || shinDeg <= FLAT_SOLE_SHIN_MAX_DEG);
244+
foot.plantigrade
245+
&& !pushOffRoll
246+
&& (shinDeg === null || shinDeg <= FLAT_SOLE_SHIN_MAX_DEG);
225247
if (expectedFlat) {
226248
state.minToeHeightMeters = Math.min(state.minToeHeightMeters ?? Infinity, foot.toeHeight);
227249
state.maxToeHeightMeters = Math.max(state.maxToeHeightMeters ?? -Infinity, foot.toeHeight);

packages/posecode-eval/src/probe.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ export function probeMovement(
846846
// Endpoint probes above power semantic movement checks. Separately sample
847847
// the solved clip between endpoints so a heel lift or collision that appears
848848
// only mid-transition cannot hide behind two valid terminal poses.
849-
const diagnosticsCollector = createClipDiagnosticsCollector(diagnosticSampleRateHz);
849+
const diagnosticsCollector = createClipDiagnosticsCollector(diagnosticSampleRateHz, clipHasTravel);
850850
for (let phaseIndex = 0; phaseIndex < tl.segments.length; phaseIndex++) {
851851
const seg = tl.segments[phaseIndex]!;
852852
const steps = Math.max(1, Math.ceil((seg.end - seg.start) * diagnosticSampleRateHz));

0 commit comments

Comments
 (0)