fix(perception): stabilize replay body frame

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 20:04:08 +03:00
parent de19229895
commit c70ad345ea
12 changed files with 783 additions and 256 deletions
@@ -32,6 +32,15 @@ export interface M4ThreatReplayResult {
providerLatencyP95Ms: number;
providerLatencyMaxMs: number;
};
bodyFrame: {
available: number;
qualified: number;
rejected: number;
cameraForwardAlignmentDeg: {
p95: number;
maximum: number;
};
};
reasonCounts: Readonly<Record<string, number>>;
};
configuration: {
@@ -39,6 +48,11 @@ export interface M4ThreatReplayResult {
nominalSensorHeightM: number;
forwardCorridorM: number;
predictionHorizonSeconds: number;
bodyFrame: {
origin: "local-surface-vertical-projection";
up: "vendor-slam-map-gravity-axis";
forward: "smoothed-slam-trajectory-validated-by-camera-axis";
};
};
limitations: readonly string[];
}
@@ -254,7 +268,13 @@ export async function fetchM4ThreatReplayResult({
const evidence = object(metrics.evidence, "M4.6 evidence");
const fixtures = object(metrics.fixtures, "M4.6 fixtures");
const runtime = object(metrics.runtime, "M4.6 runtime");
const bodyFrame = object(metrics.body_frame, "M4.6 body frame");
const cameraAlignment = object(
bodyFrame.camera_forward_alignment_deg,
"M4.6 camera alignment",
);
const configuration = object(item.configuration, "M4.6 configuration");
const configuredBodyFrame = object(configuration.body_frame, "M4.6 configured body frame");
const sourceResultIds = object(item.source_result_ids, "M4.6 sources");
return {
resultId: resultId(item.result_id),
@@ -290,6 +310,15 @@ export async function fetchM4ThreatReplayResult({
providerLatencyP95Ms: number(runtime.provider_latency_p95_ms, "M4.6 p95"),
providerLatencyMaxMs: number(runtime.provider_latency_max_ms, "M4.6 max"),
},
bodyFrame: {
available: integer(bodyFrame.available, "M4.6 available body frames"),
qualified: integer(bodyFrame.qualified, "M4.6 qualified body frames"),
rejected: integer(bodyFrame.rejected, "M4.6 rejected body frames"),
cameraForwardAlignmentDeg: {
p95: number(cameraAlignment.p95, "M4.6 body frame camera alignment p95"),
maximum: number(cameraAlignment.maximum, "M4.6 body frame camera alignment maximum"),
},
},
reasonCounts: Object.fromEntries(
Object.entries(object(metrics.reason_counts, "M4.6 reasons")).map(
([key, value]) => [key, integer(value, `M4.6 ${key}`)],
@@ -301,6 +330,23 @@ export async function fetchM4ThreatReplayResult({
nominalSensorHeightM: number(configuration.nominal_sensor_height_m, "M4.6 height"),
forwardCorridorM: number(configuration.forward_corridor_m, "M4.6 corridor"),
predictionHorizonSeconds: number(configuration.prediction_horizon_seconds, "M4.6 horizon"),
bodyFrame: {
origin: exact(
configuredBodyFrame.origin,
"local-surface-vertical-projection",
"M4.6 body frame origin",
),
up: exact(
configuredBodyFrame.up,
"vendor-slam-map-gravity-axis",
"M4.6 body frame up",
),
forward: exact(
configuredBodyFrame.forward,
"smoothed-slam-trajectory-validated-by-camera-axis",
"M4.6 body frame forward",
),
},
},
limitations: array(item.limitations, "M4.6 limitations").map((value) => text(value, "M4.6 limitation")),
};
@@ -41,6 +41,10 @@ export function M4ReplayThreatResultView({
label: "Коридор",
value: `${result.configuration.forwardCorridorM} м · horizon ${result.configuration.predictionHorizonSeconds} с`,
},
{
label: "Опорная СК",
value: `SLAM gravity · route-forward · ${metrics.bodyFrame.qualified}/${metrics.bodyFrame.available} qualified`,
},
{
label: "Визуал",
value: "4489-frame VIDEO · 32 exact CAMERA/3D/PLAN samples",
@@ -48,14 +52,14 @@ export function M4ReplayThreatResultView({
]}
brief={{
question: "Может ли единый слой обнаруживать потенциальное препятствие по двум независимым источникам, не теряя LiDAR-only объекты и не объявляя camera-only наблюдение безопасным?",
approach: "Все 4489 кадров RAVNOVES00 повторно пропущены через неизменяемые detector, metric geometry и temporal ledgers. Geometry-only объекты получают метрическую оценку; camera-only и stale/held остаются unknown. Отдельная матрица из 9 детерминированных сценариев проверяет статические, сближающиеся и расходящиеся случаи.",
approach: "Все 4489 кадров RAVNOVES00 повторно пропущены через неизменяемые detector, metric geometry и temporal ledgers. Виртуальный base_footprint привязан к gravity-оси SLAM map и направлению сглаженной траектории, проверенному camera extrinsic. Geometry-only объекты получают метрическую оценку; camera-only и stale/held остаются unknown.",
principalResult: `${metrics.evidence.currentMetric.toLocaleString("ru-RU")} current metric и ${metrics.evidence.cameraOnly.toLocaleString("ru-RU")} camera-only наблюдений учтены; ${metrics.reasonCounts["geometry-only-evidence"]?.toLocaleString("ru-RU") ?? "0"} geometry-only оценок не потеряны. Критические fixtures: ${metrics.fixtures.passed}/${metrics.fixtures.total}, ложных safe: ${metrics.fixtures.criticalFalseNotThreat}.`,
limitation: "Корпус и коридор пока виртуальные, replay не является live-проходом или физическим collision test. Постоянная скорость — ограниченная модель, а independent object truth остаётся следующим gate.",
limitation: "Корпус и коридор пока виртуальные, replay не является live-проходом или физическим collision test. На машине виртуальная привязка должна замениться измеренным rigid T_body_from_sensor; independent object truth остаётся следующим gate.",
}}
method={{
completeness: "complete",
executionClass: "hybrid",
pipelineId: "dual-evidence-replay-threat/v1",
pipelineId: "dual-evidence-replay-threat/v2",
components: [
{
kind: "source",
@@ -127,8 +131,8 @@ export function M4ReplayThreatResultView({
},
]}
conclusion={{
proved: "На неизменяемом RAVNOVES00 каждый metric, stale/held и camera-only объект получил ровно одну консервативную оценку. Geometry-only препятствия участвуют в threat-решении без класса, camera-only и просроченные данные не превращаются в safe. Видео, точные camera samples и метрическое 3D-доказательство доступны в одном viewer.",
notProved: "Не доказаны live realtime, измеренная геометрия физического корпуса, независимая object-level правильность, навигационная или safety-пригодность и выдача команд.",
proved: `На неизменяемом RAVNOVES00 каждый metric, stale/held и camera-only объект получил ровно одну консервативную оценку. ${metrics.bodyFrame.qualified}/${metrics.bodyFrame.available} доступных body frames квалифицированы без переноса handheld roll/pitch на SLAM-мир; camera/route alignment p95 ${formatNumber(metrics.bodyFrame.cameraForwardAlignmentDeg.p95, 1)}°. Видео и точное 3D-доказательство доступны в одном viewer.`,
notProved: "Не доказаны live realtime, измеренный T_body_from_sensor и геометрия физического корпуса, независимая object-level правильность, навигационная или safety-пригодность и выдача команд.",
decision: "Сохранить dual-evidence provider как канонический replay seam и переходить к независимому object-centric gate; физическую геометрию и live/actuation authority не смешивать с дальнейшей CV-разработкой.",
}}
/>
@@ -54,8 +54,8 @@ test("M4.6 decodes accepted dual-evidence result without physical authority", as
created_at_utc: "2026-08-05T15:36:01.553Z",
status: "accepted",
profile_id: "m4-ravnoves00-virtual-corridor/v1",
rig_profile_id: "virtual-handheld-body-1000x600/v1",
corridor_profile_id: "ravnoves00-forward-corridor-8m/v1",
rig_profile_id: "virtual-base-footprint-1000x600/v2",
corridor_profile_id: "ravnoves00-forward-corridor-8m/v2",
source_result_ids: {
detector: `m4-detector-replay-${"b".repeat(64)}`,
geometry: `m4-geometry-replay-${"c".repeat(64)}`,
@@ -71,6 +71,12 @@ test("M4.6 decodes accepted dual-evidence result without physical authority", as
provider_latency_p95_ms: 19.8,
provider_latency_max_ms: 194.3,
},
body_frame: {
available: 3928,
qualified: 3861,
rejected: 67,
camera_forward_alignment_deg: { p95: 8.439, maximum: 24.252 },
},
reason_counts: { "geometry-only-evidence": 21958 },
},
configuration: {
@@ -78,6 +84,11 @@ test("M4.6 decodes accepted dual-evidence result without physical authority", as
nominal_sensor_height_m: 1.25,
forward_corridor_m: 8,
prediction_horizon_seconds: 5,
body_frame: {
origin: "local-surface-vertical-projection",
up: "vendor-slam-map-gravity-axis",
forward: "smoothed-slam-trajectory-validated-by-camera-axis",
},
},
limitations: ["replay only"],
accepted: true,
@@ -90,7 +101,10 @@ test("M4.6 decodes accepted dual-evidence result without physical authority", as
assert.equal(result.resultId, resultId);
assert.equal(result.metrics.evidence.currentMetric, 27299);
assert.equal(result.metrics.fixtures.criticalFalseNotThreat, 0);
assert.equal(result.metrics.bodyFrame.qualified, 3861);
assert.equal(result.metrics.bodyFrame.cameraForwardAlignmentDeg.p95, 8.439);
assert.deepEqual(result.configuration.virtualBodyM, [1, 0.6]);
assert.equal(result.configuration.bodyFrame.up, "vendor-slam-map-gravity-axis");
});
test("M4.6 binds exact CAMERA and metric 3D evidence to one replay frame", async () => {