feat(perception): integrate vegetation policy review
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
import {
|
||||
M4ReplayThreatVisual,
|
||||
type M4ReplayClassifiedSpatialFrame,
|
||||
type M4ReplayThreatSemanticLayer,
|
||||
} from "./M4ReplayThreatVisual";
|
||||
|
||||
const CLASSES: readonly RecordedEvidenceSemanticClass[] = [
|
||||
@@ -42,7 +43,15 @@ function message(error: unknown): string {
|
||||
: "Полный TGS spatial frame недоступен.";
|
||||
}
|
||||
|
||||
export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowResult }) {
|
||||
export function M49TgsFullShadowEvidence({
|
||||
result,
|
||||
semanticOverride,
|
||||
evidenceLabel = "M49 · full TGS shadow",
|
||||
}: {
|
||||
result: M49TgsFullShadowResult;
|
||||
semanticOverride?: M4ReplayThreatSemanticLayer;
|
||||
evidenceLabel?: string;
|
||||
}) {
|
||||
const [activeSequence, setActiveSequence] = useState<number | null>(null);
|
||||
const [semantic, setSemantic] = useState<E47SemanticSlamResult | null>(null);
|
||||
const [semanticError, setSemanticError] = useState<string | null>(null);
|
||||
@@ -62,6 +71,7 @@ export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowR
|
||||
const controller = new AbortController();
|
||||
setSemantic(null);
|
||||
setSemanticError(null);
|
||||
if (semanticOverride) return () => controller.abort();
|
||||
void fetchE47SemanticSlamResult({
|
||||
resultId: result.source.linkedSemanticResultId,
|
||||
signal: controller.signal,
|
||||
@@ -77,7 +87,7 @@ export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowR
|
||||
if (!controller.signal.aborted) setSemanticError(message(caught));
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [result.source.linkedSemanticResultId, result.source.linkedVisualResultId]);
|
||||
}, [result.source.linkedSemanticResultId, result.source.linkedVisualResultId, semanticOverride]);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
@@ -196,13 +206,13 @@ export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowR
|
||||
<>
|
||||
<M4ReplayThreatVisual
|
||||
resultId={result.source.linkedVisualResultId}
|
||||
semantic={semantic ? {
|
||||
semantic={semanticOverride ?? (semantic ? {
|
||||
resultId: semantic.resultId,
|
||||
taxonomy: semantic.taxonomy,
|
||||
} : undefined}
|
||||
} : undefined)}
|
||||
showReviewAnchorBoxes={false}
|
||||
reviewLabel="4 489 source-paced TGS frames"
|
||||
evidenceLabel="M49 · full TGS shadow"
|
||||
evidenceLabel={evidenceLabel}
|
||||
initialSpatialMode="3d"
|
||||
onActiveSequenceChange={handleSequenceChange}
|
||||
classifiedSpatialLayer={{
|
||||
@@ -217,7 +227,7 @@ export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowR
|
||||
replacePointCloud: false,
|
||||
}}
|
||||
/>
|
||||
{semanticError ? (
|
||||
{!semanticOverride && semanticError ? (
|
||||
<div className="m4-replay-threat-visual__pane-status" role="alert">
|
||||
Semantic overlay недоступен: {semanticError}
|
||||
</div>
|
||||
|
||||
@@ -378,12 +378,12 @@ export function M4ReplayThreatVisual({
|
||||
);
|
||||
}, [frame, metadata.timeline, showReferenceMediaLayers, showStaticObstacles]);
|
||||
const activeBoxes = useMemo(
|
||||
() => classifiedSpatialLayer || !showReferenceMediaLayers ? [] : [
|
||||
() => !showReferenceMediaLayers ? [] : [
|
||||
...boxes(frame?.cameraProposals ?? []),
|
||||
...staticObstacleBoxes,
|
||||
...reviewAnchorBoxes,
|
||||
],
|
||||
[classifiedSpatialLayer, frame, reviewAnchorBoxes, showReferenceMediaLayers, staticObstacleBoxes],
|
||||
[frame, reviewAnchorBoxes, showReferenceMediaLayers, staticObstacleBoxes],
|
||||
);
|
||||
const semanticClasses = useMemo<readonly RecordedEvidenceSemanticClass[]>(
|
||||
() => semantic?.taxonomy.map((item) => ({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
LaboratoryEvidence,
|
||||
LaboratoryResultSummary,
|
||||
@@ -8,11 +10,16 @@ import {
|
||||
vegetationVideoMaskUrl,
|
||||
type VegetationShadowResult,
|
||||
} from "../../core/laboratory/vegetationShadow";
|
||||
import {
|
||||
fetchM49TgsFullShadowResult,
|
||||
type M49TgsFullShadowResult,
|
||||
} from "../../core/laboratory/m49TgsFullShadow";
|
||||
import {
|
||||
M48MaskComparisonVisual,
|
||||
type M48MaskComparisonCase,
|
||||
} from "./M48FailureAtlasVisual";
|
||||
import { M4ReplayThreatVisual } from "./M4ReplayThreatVisual";
|
||||
import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence";
|
||||
|
||||
function decimal(value: number, digits = 1): string {
|
||||
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
|
||||
@@ -49,6 +56,75 @@ function comparisonCases(result: VegetationShadowResult): readonly M48MaskCompar
|
||||
});
|
||||
}
|
||||
|
||||
function VegetationRouteEvidence({ result }: { result: VegetationShadowResult }) {
|
||||
const route = result.routeVideo!;
|
||||
const [tgs, setTgs] = useState<M49TgsFullShadowResult | null>(null);
|
||||
const [tgsError, setTgsError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
setTgs(null);
|
||||
setTgsError(null);
|
||||
if (!route.linkedTgsResultId) return () => controller.abort();
|
||||
void fetchM49TgsFullShadowResult(route.linkedTgsResultId, {
|
||||
signal: controller.signal,
|
||||
}).then((next) => {
|
||||
if (controller.signal.aborted) return;
|
||||
if (next.source.linkedVisualResultId !== route.baseM4ResultId) {
|
||||
throw new Error("TGS и camera timeline имеют разные source identities.");
|
||||
}
|
||||
setTgs(next);
|
||||
}).catch((caught: unknown) => {
|
||||
if (!controller.signal.aborted) {
|
||||
setTgsError(caught instanceof Error ? caught.message : "Sealed TGS недоступен.");
|
||||
}
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [route.baseM4ResultId, route.linkedTgsResultId]);
|
||||
|
||||
const semantic = {
|
||||
resultId: route.workerResultId,
|
||||
spatialResultId: null,
|
||||
taxonomy: route.taxonomy,
|
||||
maskUrl: (sequence: number) => vegetationVideoMaskUrl(result.resultId, sequence),
|
||||
label: route.viewKind === "coarse-material-policy-review"
|
||||
? "Coarse material evidence · recorded video"
|
||||
: "DDRNet vegetation prediction · recorded video",
|
||||
maskAriaLabel: route.viewKind === "coarse-material-policy-review"
|
||||
? "Coarse material policy evidence"
|
||||
: "DDRNet vegetation prediction",
|
||||
} as const;
|
||||
|
||||
if (route.linkedTgsResultId && tgs) {
|
||||
return (
|
||||
<M49TgsFullShadowEvidence
|
||||
result={tgs}
|
||||
semanticOverride={semantic}
|
||||
evidenceLabel="LAB V1 · MATERIAL + YOLOX + TGS"
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (route.linkedTgsResultId && !tgsError) {
|
||||
return <div className="m4-replay-threat-visual__pane-status" role="status">Открываем sealed TGS и coarse material timeline…</div>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<M4ReplayThreatVisual
|
||||
resultId={route.baseM4ResultId}
|
||||
evidenceLabel="LAB V1 · DDRNet"
|
||||
showReferenceMediaLayers={route.viewKind === "coarse-material-policy-review"}
|
||||
showSpatialOverlaySummary={false}
|
||||
semantic={semantic}
|
||||
/>
|
||||
{tgsError ? (
|
||||
<div className="m4-replay-threat-visual__pane-status" role="alert">
|
||||
TGS слой недоступен: {tgsError}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function VegetationShadowResultView({
|
||||
rigLabel,
|
||||
result,
|
||||
@@ -68,10 +144,14 @@ export function VegetationShadowResultView({
|
||||
<LaboratorySummary
|
||||
title="LAB V1 · готовые модели растительности"
|
||||
description={result.routeVideo
|
||||
? "M4.8 сохраняет truth-backed сравнение моделей, а штатный M4.7 viewer показывает фактический DDRNet prediction на всей записи RAVNOVES00. Все 4489 масок запечатаны локально и открываются без Worker 006."
|
||||
? result.routeVideo.viewKind === "coarse-material-policy-review"
|
||||
? "M4.8 сохраняет truth-backed сравнение моделей, а штатный M4.7 синхронно показывает coarse material evidence, frozen YOLOX vetoes и causal TGS на всей записи RAVNOVES00. Все слои запечатаны локально и открываются без Worker 006."
|
||||
: "M4.8 сохраняет truth-backed сравнение моделей, а штатный M4.7 viewer показывает фактический DDRNet prediction на всей записи RAVNOVES00. Все 4489 масок запечатаны локально и открываются без Worker 006."
|
||||
: "Штатный M4.8-инструмент сравнивает две готовые fine-64 модели на полном GOOSE validation split и на 12 truth-backed hard cases, выбранных только по наличию нужной растительности. Sealed evidence открывается локально без Worker 006."}
|
||||
status={result.routeVideo
|
||||
? "DDRNet full-video prediction ready · route truth отсутствует"
|
||||
? result.routeVideo.viewKind === "coarse-material-policy-review"
|
||||
? "MULTILAYER POLICY REVIEW · commands OFF · route truth отсутствует"
|
||||
: "DDRNet full-video prediction ready · route truth отсутствует"
|
||||
: "Truth-backed model comparison · route transfer не принят"}
|
||||
statusTone="warning"
|
||||
facts={[
|
||||
@@ -80,15 +160,17 @@ export function VegetationShadowResultView({
|
||||
{ label: "Кейсы", value: "трава · куст · ствол · крона · изгородь · лес · посевы" },
|
||||
...(result.routeVideo ? [{
|
||||
label: "Видео",
|
||||
value: "RAVNOVES00 · 4489/4489 DDRNet masks · exact recorded sequence",
|
||||
value: result.routeVideo.viewKind === "coarse-material-policy-review"
|
||||
? "RAVNOVES00 · 4489/4489 coarse masks + YOLOX + TGS · exact sequence"
|
||||
: "RAVNOVES00 · 4489/4489 DDRNet masks · exact recorded sequence",
|
||||
}] : []),
|
||||
{ label: "Authority", value: `${rigLabel} · MODEL QUALIFICATION ONLY · commands OFF` },
|
||||
]}
|
||||
brief={{
|
||||
question: "Какие готовые веса лучше различают проезжаемую траву, кусты и стволы на размеченных off-road кадрах?",
|
||||
approach: "Обе модели последовательно прогнаны в одном изолированном CUDA-runtime на 962 кадрах. 12 визуальных кейсов выбраны детерминированно по truth-поддержке восьми растительных классов; один M4.8 viewer показывает source, truth, prediction и material-error для выбранной модели.",
|
||||
principalResult: `${selected.loadedModelName} лидирует по vegetation IoU: ${decimal(selected.vegetationMeanIouPercent, 2)}% против ${decimal(alternative.vegetationMeanIouPercent, 2)}%. ${result.routeVideo ? "Его фактическая temporal stability теперь видна на всех 4489 кадрах штатного recorded viewer." : "Ошибки по каждому типу проверяются в одном штатном инструменте."}`,
|
||||
limitation: "GOOSE — внешний размеченный домен; RAVNOVES00 — наш fisheye, но без ручной truth-разметки. Full-video слой показывает prediction, а не доказывает правильность. Папоротник отдельным классом отсутствует.",
|
||||
principalResult: `${selected.loadedModelName} лидирует по vegetation IoU: ${decimal(selected.vegetationMeanIouPercent, 2)}% против ${decimal(alternative.vegetationMeanIouPercent, 2)}%. ${result.routeVideo?.viewKind === "coarse-material-policy-review" ? "Fine-64 prediction сведён к mission-neutral материалам; YOLOX и TGS сохраняют независимое veto." : result.routeVideo ? "Его фактическая temporal stability теперь видна на всех 4489 кадрах штатного recorded viewer." : "Ошибки по каждому типу проверяются в одном штатном инструменте."}`,
|
||||
limitation: "GOOSE — внешний размеченный домен; RAVNOVES00 — наш fisheye, но без ручной truth-разметки. Материалы — prediction, а не доказательство проходимости. TGS не проецируется в пиксели без отдельной принятой калибровки.",
|
||||
}}
|
||||
method={{
|
||||
completeness: "complete",
|
||||
@@ -120,32 +202,25 @@ export function VegetationShadowResultView({
|
||||
{result.routeVideo ? (
|
||||
<LaboratoryEvidence
|
||||
eyebrow="M4.7 · RAVNOVES00 FULL VIDEO"
|
||||
title="DDRNet PREDICTION · 4489/4489 кадров · TRUTH для этой записи отсутствует"
|
||||
title={result.routeVideo.viewKind === "coarse-material-policy-review"
|
||||
? "COARSE MATERIAL + YOLOX VETO + CAUSAL TGS · 4489/4489 · TRUTH отсутствует"
|
||||
: "DDRNet PREDICTION · 4489/4489 кадров · TRUTH для этой записи отсутствует"}
|
||||
kind="diagnostic-model"
|
||||
resizable
|
||||
>
|
||||
<M4ReplayThreatVisual
|
||||
resultId={result.routeVideo.baseM4ResultId}
|
||||
evidenceLabel="LAB V1 · DDRNet"
|
||||
showReferenceMediaLayers={false}
|
||||
showSpatialOverlaySummary={false}
|
||||
semantic={{
|
||||
resultId: result.routeVideo.workerResultId,
|
||||
spatialResultId: null,
|
||||
taxonomy: result.routeVideo.taxonomy,
|
||||
maskUrl: (sequence) => vegetationVideoMaskUrl(result.resultId, sequence),
|
||||
label: "DDRNet vegetation prediction · recorded video",
|
||||
maskAriaLabel: "DDRNet vegetation prediction",
|
||||
}}
|
||||
/>
|
||||
<VegetationRouteEvidence result={result} />
|
||||
</LaboratoryEvidence>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
result={(
|
||||
<LaboratoryResultSummary
|
||||
title="DDRNet — стартовые веса; перенос на ровер ещё не доказан"
|
||||
status={`${selected.loadedModelName} выбран только как vegetation candidate`}
|
||||
title={result.routeVideo?.viewKind === "coarse-material-policy-review"
|
||||
? "Слои собраны для визуального policy review; управление не авторизовано"
|
||||
: "DDRNet — стартовые веса; перенос на ровер ещё не доказан"}
|
||||
status={result.routeVideo?.viewKind === "coarse-material-policy-review"
|
||||
? "Materials are advisory · YOLOX/TGS veto cannot be cleared"
|
||||
: `${selected.loadedModelName} выбран только как vegetation candidate`}
|
||||
statusTone="warning"
|
||||
metrics={[
|
||||
{
|
||||
@@ -186,13 +261,17 @@ export function VegetationShadowResultView({
|
||||
...(result.routeVideo ? [{
|
||||
label: "Route video",
|
||||
value: "4489/4489 masks",
|
||||
hint: "DDRNet prediction · exact sequence · Worker-independent playback",
|
||||
hint: result.routeVideo.viewKind === "coarse-material-policy-review"
|
||||
? "9 coarse states · YOLOX + causal TGS · Worker-independent playback"
|
||||
: "DDRNet prediction · exact sequence · Worker-independent playback",
|
||||
}] : []),
|
||||
]}
|
||||
conclusion={{
|
||||
proved: "Обе официальные fine-64 модели воспроизводимо запускаются на Worker 006; DDRNet лучше по aggregate vegetation IoU. Truth-backed hard cases прямо показывают траву, кусты и стволы, а не случайные автомобили и здания.",
|
||||
notProved: "Не доказаны accuracy на нашем fisheye-домене, папоротник как отдельный материал, collision safety и physical-live поведение ровера. Видео позволяет увидеть temporal stability, но без truth не превращает её в метрику качества.",
|
||||
decision: "Смотреть полный prediction на видео и собирать конкретные temporal/domain failure cases. DDRNet остаётся diagnostic candidate; LiDAR/TGS fail-closed геометрию не ослаблять.",
|
||||
decision: result.routeVideo?.viewKind === "coarse-material-policy-review"
|
||||
? "На одном M4.7 проверить ложные LOW GRASS/HIGH GRASS кандидаты против YOLOX и TGS. До truth-кейсов и integrated load этот слой не подключать к planner/actuation."
|
||||
: "Смотреть полный prediction на видео и собирать конкретные temporal/domain failure cases. DDRNet остаётся diagnostic candidate; LiDAR/TGS fail-closed геометрию не ослаблять.",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user