fix(m4): retain compact obstacle evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 22:16:33 +03:00
parent 13ed096f80
commit aacc6dc43b
24 changed files with 981 additions and 410 deletions
@@ -26,7 +26,7 @@ export function M4ReplayThreatResultView({
<LaboratorySummary
title="M4.6 · dual-evidence threat replay"
description="Camera и LiDAR дают независимые доказательства, после чего один source-neutral слой оценивает пересечение виртуального коридора, ближайшее сближение и TTC. Ни один сенсор не назначен first."
status="4489/4489 · replay-simulated · accepted"
status="Replay contract passed · CV quality gate open"
statusTone="warning"
facts={[
{
@@ -53,7 +53,7 @@ export function M4ReplayThreatResultView({
brief={{
question: "Может ли единый слой обнаруживать потенциальное препятствие по двум независимым источникам, не теряя LiDAR-only объекты и не объявляя camera-only наблюдение безопасным?",
approach: "Все 4489 кадров RAVNOVES00 повторно пропущены через неизменяемые detector, metric geometry и temporal ledgers. Текущий lio_pcl increment хранится отдельно от bounded rolling map: отсутствие повторной публикации точки не считается свободным пространством. Виртуальный base_footprint привязан к gravity-оси SLAM map и направлению сглаженной траектории, проверенному camera extrinsic.",
principalResult: `${metrics.evidence.currentMetric.toLocaleString("ru-RU")} current metric, ${metrics.evidence.rollingMapRetained.toLocaleString("ru-RU")} rolling-map и ${metrics.evidence.cameraOnly.toLocaleString("ru-RU")} camera-only публикаций учтены. Кадр 1880 удерживает обе видимые бетонные полусферы; критические fixtures: ${metrics.fixtures.passed}/${metrics.fixtures.total}, ложных safe: ${metrics.fixtures.criticalFalseNotThreat}.`,
principalResult: `${metrics.evidence.currentMetric.toLocaleString("ru-RU")} current metric, ${metrics.evidence.rollingMapRetained.toLocaleString("ru-RU")} rolling-map и ${metrics.evidence.cameraOnly.toLocaleString("ru-RU")} camera-only публикаций учтены. Кадры 1880 и 2584 закрепляют компактные бетонные полусферы как автоматические метрические регрессии; критические fixtures: ${metrics.fixtures.passed}/${metrics.fixtures.total}, ложных safe: ${metrics.fixtures.criticalFalseNotThreat}.`,
limitation: "Корпус и коридор пока виртуальные, replay не является live-проходом или физическим collision test. На машине виртуальная привязка должна замениться измеренным rigid T_body_from_sensor; independent object truth остаётся следующим gate.",
}}
method={{
@@ -105,8 +105,8 @@ export function M4ReplayThreatResultView({
)}
result={(
<LaboratoryResultSummary
title="Dual-evidence слой готов к следующей CV-итерации на recorded replay"
status="Replay gate accepted · physical authority withheld"
title="Replay-инфраструктура принята; object recall ещё проверяется"
status="Жёлтый: pipeline целостен, независимый object-truth gate не пройден"
statusTone="warning"
metrics={[
{
@@ -131,7 +131,7 @@ export function M4ReplayThreatResultView({
},
]}
conclusion={{
proved: `На неизменяемом RAVNOVES00 каждый current, rolling-map, stale/held и camera-only объект получил ровно одну консервативную оценку. CURRENT INCREMENT и ROLLING MAP независимо включаются в viewer; кадр 1880 закреплён как регрессия двух бетонных полусфер. ${metrics.bodyFrame.qualified}/${metrics.bodyFrame.available} body frames квалифицированы без переноса handheld roll/pitch на SLAM-мир.`,
proved: `На неизменяемом RAVNOVES00 каждый current, rolling-map, stale/held и camera-only объект получил ровно одну консервативную оценку. CURRENT INCREMENT и ROLLING MAP независимо включаются в viewer; кадры 1880 и 2584 закреплены как регрессии компактных бетонных полусфер. ${metrics.bodyFrame.qualified}/${metrics.bodyFrame.available} body frames квалифицированы без переноса handheld roll/pitch на SLAM-мир.`,
notProved: "Не доказаны live realtime, измеренный T_body_from_sensor и геометрия физического корпуса, независимая object-level правильность, навигационная или safety-пригодность и выдача команд.",
decision: "Сохранить dual-evidence provider как канонический replay seam и переходить к независимому object-centric gate; физическую геометрию и live/actuation authority не смешивать с дальнейшей CV-разработкой.",
}}
@@ -7,6 +7,7 @@ import {
type LaboratoryMetricSceneMode,
} from "../../components/laboratory/LaboratoryMetricEvidenceScene";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import { RecordedEvidenceImageScene } from "../../components/laboratory/RecordedEvidenceImageScene";
import {
RecordedEvidenceVideoScene,
type RecordedEvidenceBox,
@@ -108,7 +109,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
}, [ordinal, resultId]);
useEffect(() => {
if ((mode !== "video" && mode !== "camera") || (videoOverlay && videoSource)) return;
if (mode !== "video" || (videoOverlay && videoSource)) return;
const controller = new AbortController();
setVideoLoading(true);
setVideoError(null);
@@ -157,14 +158,6 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
return () => controller.abort();
}, [mode, resultId, videoOverlay, videoSource]);
useEffect(() => {
if (mode !== "camera" || !frame) return;
setVideoPlayback({
currentSeconds: frame.sourceTimeNs / 1_000_000_000,
playing: false,
});
}, [frame, mode]);
const activeVideoFrame = useMemo(
() => videoOverlay
? selectM4ThreatVideoFrame(videoOverlay.frames, videoPlayback.currentSeconds)
@@ -317,7 +310,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
) : undefined;
let content;
if (mode === "video" || mode === "camera") {
if (mode === "video") {
content = videoLoading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
@@ -335,14 +328,30 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
imageWidth={videoOverlay.imageWidth}
imageHeight={videoOverlay.imageHeight}
boxes={activeBoxes}
ariaLabel={
mode === "camera"
? `M4.6 exact camera sample ${ordinal}: ${activeBoxes.length} proposals`
: `M4.6 full video frame ${activeVideoFrame?.frameIndex ?? 0}: ${activeBoxes.length} proposals`
}
ariaLabel={`M4.6 full video frame ${activeVideoFrame?.frameIndex ?? 0}: ${activeBoxes.length} proposals`}
onPlaybackChange={setVideoPlayback}
/>
);
} else if (mode === "camera") {
content = sampleLoading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем точный CAMERA-кадр M4.6</span>
</div>
) : sampleError || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{sampleError ?? "CAMERA-кадр M4.6 недоступен."}</span>
</div>
) : (
<RecordedEvidenceImageScene
src={frame.cameraUrl}
imageWidth={800}
imageHeight={600}
boxes={activeBoxes}
ariaLabel={`M4.6 exact camera sample ${ordinal}: ${activeBoxes.length} proposals`}
/>
);
} else {
content = sampleLoading ? (
<div className="l3-visual-audit__state" role="status">
@@ -376,6 +385,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
<div className="l3-visual-audit m4-replay-threat-visual">
<LaboratoryEvidenceViewer
label="M4.6 dual-evidence replay: video, camera and metric 3D"
className="m4-replay-threat-evidence-viewer"
mode={mode}
modes={[
{ value: "video", label: "VIDEO" },