feat(perception): split vegetation evidence layers

This commit is contained in:
DCCONSTRUCTIONS
2026-08-28 15:37:10 +03:00
parent a2c3385062
commit e10b96b546
26 changed files with 868 additions and 233 deletions
@@ -51,6 +51,7 @@ import { M48TRiskQualityResultView } from "./M48TRiskQualityResult";
import { M49TgsFailClosedResultView } from "./M49TgsFailClosedResult";
import { M49TgsFullShadowResultView } from "./M49TgsFullShadowResult";
import { VegetationShadowResultView } from "./VegetationShadowResult";
import { VegetationBenchmarkResultView } from "./VegetationBenchmarkResult";
export { isAdvancedLaboratoryWorkId };
export type { AdvancedLaboratoryWorkId };
@@ -93,6 +94,9 @@ export function AdvancedLaboratoryResult({
failedSessionId: string | null;
replayError: string | null;
}) {
if (workId === "lab-v1-vegetation-benchmark" && results.vegetationBenchmark) {
return <VegetationBenchmarkResultView rigLabel={rigLabel} result={results.vegetationBenchmark} />;
}
if (workId === "lab-v1-vegetation-shadow" && results.vegetationShadow) {
return <VegetationShadowResultView rigLabel={rigLabel} result={results.vegetationShadow} />;
}
@@ -71,7 +71,6 @@ export function M49TgsFullShadowEvidence({
const controller = new AbortController();
setSemantic(null);
setSemanticError(null);
if (semanticOverride) return () => controller.abort();
void fetchE47SemanticSlamResult({
resultId: result.source.linkedSemanticResultId,
signal: controller.signal,
@@ -87,7 +86,7 @@ export function M49TgsFullShadowEvidence({
if (!controller.signal.aborted) setSemanticError(message(caught));
});
return () => controller.abort();
}, [result.source.linkedSemanticResultId, result.source.linkedVisualResultId, semanticOverride]);
}, [result.source.linkedSemanticResultId, result.source.linkedVisualResultId]);
useEffect(() => {
const controller = new AbortController();
@@ -201,15 +200,28 @@ export function M49TgsFullShadowEvidence({
const handleSequenceChange = useCallback((sequence: number | null) => {
setActiveSequence(sequence);
}, []);
const semanticLayers = useMemo<readonly M4ReplayThreatSemanticLayer[]>(() => [
...(semantic ? [{
id: "urban",
controlLabel: "ГОРОД · EoMT",
resultId: semantic.resultId,
taxonomy: semantic.taxonomy,
label: "EoMT Cityscapes semantic · recorded video",
maskAriaLabel: "EoMT urban semantic prediction",
}] : []),
...(semanticOverride ? [{
...semanticOverride,
id: semanticOverride.id ?? "vegetation",
controlLabel: semanticOverride.controlLabel ?? "ПРИРОДА · DDRNet",
}] : []),
], [semantic, semanticOverride]);
return (
<>
<M4ReplayThreatVisual
resultId={result.source.linkedVisualResultId}
semantic={semanticOverride ?? (semantic ? {
resultId: semantic.resultId,
taxonomy: semantic.taxonomy,
} : undefined)}
semanticLayers={semanticLayers}
initialSemanticLayerId={semanticOverride ? "vegetation" : "urban"}
showReviewAnchorBoxes={false}
reviewLabel="4 489 source-paced TGS frames"
evidenceLabel={evidenceLabel}
@@ -227,7 +239,7 @@ export function M49TgsFullShadowEvidence({
replacePointCloud: false,
}}
/>
{!semanticOverride && semanticError ? (
{semanticError ? (
<div className="m4-replay-threat-visual__pane-status" role="alert">
Semantic overlay недоступен: {semanticError}
</div>
@@ -96,6 +96,8 @@ function SpatialState({ message: text }: { message: string }) {
}
export interface M4ReplayThreatSemanticLayer {
id?: string;
controlLabel?: string;
resultId: string;
spatialResultId?: string | null;
maskUrl?: (sequence: number) => string;
@@ -155,6 +157,8 @@ const EMPTY_REVIEW_ANCHORS: readonly M4ReplayThreatReviewAnchor[] = [];
export function M4ReplayThreatVisual({
resultId,
semantic,
semanticLayers,
initialSemanticLayerId,
reviewAnchors = EMPTY_REVIEW_ANCHORS,
showReviewAnchorBoxes = true,
reviewLabel = "Контрольные примеры M4.8R1",
@@ -168,6 +172,8 @@ export function M4ReplayThreatVisual({
}: {
resultId: string;
semantic?: M4ReplayThreatSemanticLayer;
semanticLayers?: readonly M4ReplayThreatSemanticLayer[];
initialSemanticLayerId?: string;
reviewAnchors?: readonly M4ReplayThreatReviewAnchor[];
showReviewAnchorBoxes?: boolean;
reviewLabel?: string;
@@ -199,6 +205,35 @@ export function M4ReplayThreatVisual({
));
const [expanded, setExpanded] = useState(false);
const [selectedReviewAnchorIndex, setSelectedReviewAnchorIndex] = useState(0);
const availableSemanticLayers = useMemo<readonly M4ReplayThreatSemanticLayer[]>(
() => semanticLayers?.length ? semanticLayers : semantic ? [semantic] : [],
[semantic, semanticLayers],
);
const semanticLayerIdentity = availableSemanticLayers
.map((layer, index) => layer.id ?? `${layer.resultId}:${index}`)
.join("|");
const [selectedSemanticLayerId, setSelectedSemanticLayerId] = useState(
initialSemanticLayerId ?? "",
);
useEffect(() => {
if (!availableSemanticLayers.length) {
setSelectedSemanticLayerId("");
return;
}
const selectedStillExists = availableSemanticLayers.some(
(layer, index) => (layer.id ?? `${layer.resultId}:${index}`) === selectedSemanticLayerId,
);
if (selectedStillExists) return;
const preferred = initialSemanticLayerId
? availableSemanticLayers.find((layer) => layer.id === initialSemanticLayerId)
: null;
const next = preferred ?? availableSemanticLayers[0]!;
const nextIndex = availableSemanticLayers.indexOf(next);
setSelectedSemanticLayerId(next.id ?? `${next.resultId}:${nextIndex}`);
}, [availableSemanticLayers, initialSemanticLayerId, semanticLayerIdentity, selectedSemanticLayerId]);
const activeSemantic = availableSemanticLayers.find(
(layer, index) => (layer.id ?? `${layer.resultId}:${index}`) === selectedSemanticLayerId,
) ?? availableSemanticLayers[0];
const metricSceneRef = useRef<LaboratoryMetricEvidenceSceneHandle | null>(null);
const metadata = useM4ThreatTimelineMetadata(resultId, timelineEndpointRoot);
const playbackRange = useMemo(() => metadata.timeline ? ({
@@ -298,19 +333,21 @@ export function M4ReplayThreatVisual({
sequence: frame?.sequence ?? null,
endpointRoot: timelineEndpointRoot,
});
const semanticSpatialResultId = semantic
? semantic.spatialResultId === undefined ? semantic.resultId : semantic.spatialResultId
const semanticSpatialResultId = activeSemantic
? activeSemantic.spatialResultId === undefined
? activeSemantic.resultId
: activeSemantic.spatialResultId
: null;
const spatialSemanticTaxonomy = useMemo<readonly E47SemanticClass[]>(
() => semanticSpatialResultId && semantic
? semantic.taxonomy.map((item) => ({
() => semanticSpatialResultId && activeSemantic
? activeSemantic.taxonomy.map((item) => ({
classId: item.classId,
label: item.label,
disposition: item.disposition === "ambiguous" ? "ambiguous" : "labeled",
colorRgb: item.colorRgb,
}))
: [],
[semantic, semanticSpatialResultId],
[activeSemantic, semanticSpatialResultId],
);
const semanticTimeline = useE47SemanticTimelineFrame({
resultId: semanticSpatialResultId,
@@ -386,14 +423,14 @@ export function M4ReplayThreatVisual({
[frame, reviewAnchorBoxes, showReferenceMediaLayers, staticObstacleBoxes],
);
const semanticClasses = useMemo<readonly RecordedEvidenceSemanticClass[]>(
() => semantic?.taxonomy.map((item) => ({
() => activeSemantic?.taxonomy.map((item) => ({
id: item.classId,
label: `semantic: ${item.label}`,
})) ?? [],
[semantic?.taxonomy],
[activeSemantic?.taxonomy],
);
const semanticPalette = useMemo<readonly RecordedEvidenceSemanticPaletteEntry[]>(
() => semantic?.taxonomy.map((item) => ({
() => activeSemantic?.taxonomy.map((item) => ({
classId: item.classId,
color: item.disposition === "undefined"
? { kind: "transparent" as const }
@@ -404,7 +441,7 @@ export function M4ReplayThreatVisual({
? 0
: item.disposition === "ambiguous" ? 0.52 : 0.92,
})) ?? [],
[semantic?.taxonomy],
[activeSemantic?.taxonomy],
);
const semanticFrame = semanticTimeline.activeFrame?.sequence === frame?.sequence
? semanticTimeline.activeFrame
@@ -422,7 +459,7 @@ export function M4ReplayThreatVisual({
&& lastSpatialSemanticFrameRef.current.frame.sequence === spatialFrame?.sequence
? lastSpatialSemanticFrameRef.current.frame
: null;
const semanticIntegrityError = semantic && spatialFrame && spatialSemanticFrame && (
const semanticIntegrityError = activeSemantic && spatialFrame && spatialSemanticFrame && (
spatialSemanticFrame.sourcePointCount !== spatialFrame.pointCloudSourceCount
|| spatialFrame.pointCloudSampleCount !== spatialFrame.pointCloudSourceCount
|| spatialFrame.pointCloudBodyXyzM.length !== spatialFrame.pointCloudSourceCount
@@ -431,7 +468,7 @@ export function M4ReplayThreatVisual({
: null;
const alignedSemanticPointIds = useMemo<readonly (number | null)[] | undefined>(() => {
if (
!semantic
!activeSemantic
|| !showSpatialSemantic
|| !spatialFrame
|| !spatialSemanticFrame
@@ -441,7 +478,7 @@ export function M4ReplayThreatVisual({
const status = spatialSemanticFrame.statusCodes[index];
return status === 2 || status === 3 ? classId : null;
});
}, [semantic, semanticIntegrityError, showSpatialSemantic, spatialFrame, spatialSemanticFrame]);
}, [activeSemantic, semanticIntegrityError, showSpatialSemantic, spatialFrame, spatialSemanticFrame]);
const activeSpatialFrame = spatialFrame?.sequence === timelineFrame.activeSequence
? spatialFrame
: null;
@@ -610,19 +647,19 @@ export function M4ReplayThreatVisual({
},
), [metadata.timeline, spatialFrame, timelineFrame.availableFrames]);
const semanticOverlay: RecordedEvidenceSemanticOverlay | undefined =
semantic && showMediaSemantic && frame
activeSemantic && showMediaSemantic && frame
? {
src: semantic.maskUrl?.(frame.sequence)
?? e47SemanticMaskUrl(semantic.resultId, frame.sequence),
src: activeSemantic.maskUrl?.(frame.sequence)
?? e47SemanticMaskUrl(activeSemantic.resultId, frame.sequence),
prefetchSrcs: Array.from({ length: 12 }, (_, index) => index + 1)
.map((offset) => frame.sequence + offset)
.filter((sequence) => sequence < (metadata.timeline?.frameCount ?? 0))
.map((sequence) => semantic.maskUrl?.(sequence)
?? e47SemanticMaskUrl(semantic.resultId, sequence)),
.map((sequence) => activeSemantic.maskUrl?.(sequence)
?? e47SemanticMaskUrl(activeSemantic.resultId, sequence)),
classes: semanticClasses,
palette: semanticPalette,
opacity: 0.9,
ariaLabel: `${semantic.maskAriaLabel ?? "Semantic prediction"} frame ${frame.sequence + 1}`,
ariaLabel: `${activeSemantic.maskAriaLabel ?? "Semantic prediction"} frame ${frame.sequence + 1}`,
}
: undefined;
const accumulatedCameraPoints = cameraPointOverlay.overlay?.sequence === frame?.sequence
@@ -692,7 +729,7 @@ export function M4ReplayThreatVisual({
</div>
);
const mediaLayerControls = semantic
const mediaLayerControls = activeSemantic
|| (showReferenceMediaLayers && metadata.timeline?.cameraPointDelivery)
|| (showReferenceMediaLayers && metadata.timeline?.cameraObstacleProjectionDelivery) ? (
<div
@@ -700,7 +737,7 @@ export function M4ReplayThreatVisual({
role="group"
aria-label="Слои камеры и видео"
>
{semantic ? (
{activeSemantic ? (
<Button
size="compact"
shape="pill"
@@ -711,6 +748,20 @@ export function M4ReplayThreatVisual({
SEMANTICS
</Button>
) : null}
{availableSemanticLayers.length > 1 ? (
<SegmentedControl
value={selectedSemanticLayerId}
items={availableSemanticLayers.map((layer, index) => ({
value: layer.id ?? `${layer.resultId}:${index}`,
label: layer.controlLabel ?? layer.label ?? `SEMANTIC ${index + 1}`,
}))}
label="Источник семантики"
onChange={(value) => {
setSelectedSemanticLayerId(value);
setShowMediaSemantic(true);
}}
/>
) : null}
{showReferenceMediaLayers && metadata.timeline?.cameraPointDelivery ? (
<Button
size="compact"
@@ -1022,6 +1073,7 @@ export function M4ReplayThreatVisual({
<div
className="m4-replay-threat-visual__pane-toolbar"
data-pane-toolbar="media"
data-multi-semantic={availableSemanticLayers.length > 1 ? "true" : undefined}
>
{mediaLayerControls}
{mediaModeControls}
@@ -1238,8 +1290,8 @@ export function M4ReplayThreatVisual({
return (
<div className="l3-visual-audit m4-replay-threat-visual">
<LaboratoryEvidenceViewer
label={semantic
? semantic.label ?? "Semantic diagnostic replay"
label={activeSemantic
? activeSemantic.label ?? "Semantic diagnostic replay"
: `${evidenceLabel} recorded-realtime replay`}
className="m4-replay-threat-evidence-viewer"
mode={mediaMode ?? "none"}
@@ -0,0 +1,147 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { VegetationShadowResult } from "../../core/laboratory/vegetationShadow";
import {
M48MaskComparisonVisual,
type M48MaskComparisonCase,
} from "./M48FailureAtlasVisual";
function decimal(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
const VEGETATION_LABELS: Readonly<Record<string, string>> = {
high_grass: "Высокая трава",
low_grass: "Низкая трава",
bush: "Куст",
tree_trunk: "Ствол дерева",
tree_crown: "Крона дерева",
hedge: "Живая изгородь",
forest: "Лесная растительность",
crops: "Посевы",
};
function comparisonCases(result: VegetationShadowResult): readonly M48MaskComparisonCase[] {
return result.validationCases.map((item) => {
const focus = item.focus!;
return {
caseId: item.caseId,
title: `${VEGETATION_LABELS[focus.className] ?? focus.className} · truth ${decimal(focus.truthFraction * 100, 1)}% кадра`,
sourceUrl: item.assets.source,
truthUrl: item.assets.truth,
predictions: {
ddrnet: item.assets.ddrnet,
ppliteseg: item.assets.ppliteseg,
},
errors: {
ddrnet: item.assets.ddrnet_error,
ppliteseg: item.assets.ppliteseg_error,
},
};
});
}
export function VegetationBenchmarkResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: VegetationShadowResult;
}) {
const selected = result.candidates.find(
(candidate) => candidate.candidate === result.selectedCandidate,
)!;
const alternative = result.candidates.find(
(candidate) => candidate.candidate !== result.selectedCandidate,
)!;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="M4.8 · архивный benchmark растительности"
description="Отдельный truth-backed контур GOOSE для сравнения готовых fine-64 весов. Он не является частью RAVNOVES00 realtime LAB и открывается автономно без Worker 006."
status="ARCHIVE ANALYSIS · model qualification only · commands OFF"
statusTone="warning"
facts={[
{ label: "Источник", value: "GOOSE validation · 962 размеченных кадра · 12 hard cases" },
{ label: "Сравнение", value: "DDRNet-39 vs PPLiteSeg · official fine-64 weights" },
{ label: "Кейсы", value: "трава · куст · ствол · крона · изгородь · лес · посевы" },
{ label: "Authority", value: `${rigLabel} · MODEL QUALIFICATION ONLY · commands OFF` },
]}
brief={{
question: "Какие готовые веса лучше различают проезжаемую траву, кусты и стволы на размеченных off-road кадрах?",
approach: "Обе модели прогнаны на 962 кадрах, а 12 визуальных кейсов выбраны детерминированно по truth-поддержке восьми растительных классов. Viewer показывает source, ручной truth, prediction и error.",
principalResult: `${selected.loadedModelName} лидирует по vegetation IoU: ${decimal(selected.vegetationMeanIouPercent, 2)}% против ${decimal(alternative.vegetationMeanIouPercent, 2)}%.`,
limitation: "GOOSE — внешний размеченный домен. Результат выбирает стартовые веса, но не доказывает качество на fisheye RAVNOVES00 и не даёт navigation authority.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: "goose-fine64-ready-weights-benchmark-archive/v1",
components: result.candidates.map((candidate) => ({
kind: "model" as const,
name: candidate.loadedModelName,
version: candidate.candidate,
role: candidate.candidate === result.selectedCandidate
? "selected vegetation candidate"
: "comparison candidate",
identitySha256: candidate.checkpointSha256,
})),
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="M4.8 · GOOSE VEGETATION HARD CASES"
title="TRUTH — ручная разметка · PREDICTION — ответ модели · ERROR — расхождение"
kind="diagnostic-model"
resizable
>
<M48MaskComparisonVisual
cases={comparisonCases(result)}
initialCandidate={result.selectedCandidate}
/>
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="DDRNet выбран как стартовый vegetation candidate"
status={`${selected.loadedModelName} · перенос на ровер не доказан`}
statusTone="warning"
metrics={[
{
label: "GOOSE mIoU",
value: `${decimal(selected.meanIouPercent, 2)}% / ${decimal(alternative.meanIouPercent, 2)}%`,
hint: `${selected.candidate} / ${alternative.candidate} · полный validation split`,
},
{
label: "Vegetation IoU",
value: `${decimal(selected.vegetationMeanIouPercent, 2)}% / ${decimal(alternative.vegetationMeanIouPercent, 2)}%`,
hint: "grass/vegetation/bush/tree и родственные fine-64 labels",
},
{
label: "Worker shadow p95",
value: `${decimal(selected.shadowLatencyP95Ms, 2)} / ${decimal(alternative.shadowLatencyP95Ms, 2)} ms`,
hint: "чистый inference · одна тяжёлая модель за раз",
},
{
label: "Peak VRAM",
value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} / ${decimal(alternative.peakReservedVramBytes / 1024 ** 3, 2)} GiB`,
hint: `${selected.candidate} / ${alternative.candidate} · RTX 4090`,
},
]}
conclusion={{
proved: "Обе готовые fine-64 модели воспроизводимо запускаются; DDRNet лучше по aggregate vegetation IoU.",
notProved: "Не доказаны accuracy на нашем fisheye, temporal stability, collision safety и физическое поведение ровера.",
decision: "Хранить как архив квалификации весов. Проверку на RAVNOVES00 вести только в основной многослойной LAB.",
}}
/>
)}
/>
);
}
@@ -14,10 +14,6 @@ import {
fetchM49TgsFullShadowResult,
type M49TgsFullShadowResult,
} from "../../core/laboratory/m49TgsFullShadow";
import {
M48MaskComparisonVisual,
type M48MaskComparisonCase,
} from "./M48FailureAtlasVisual";
import { M4ReplayThreatVisual } from "./M4ReplayThreatVisual";
import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence";
@@ -25,37 +21,6 @@ function decimal(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
const VEGETATION_LABELS: Readonly<Record<string, string>> = {
high_grass: "Высокая трава",
low_grass: "Низкая трава",
bush: "Куст",
tree_trunk: "Ствол дерева",
tree_crown: "Крона дерева",
hedge: "Живая изгородь",
forest: "Лесная растительность",
crops: "Посевы",
};
function comparisonCases(result: VegetationShadowResult): readonly M48MaskComparisonCase[] {
return result.validationCases.map((item) => {
const focus = item.focus!;
return {
caseId: item.caseId,
title: `${VEGETATION_LABELS[focus.className] ?? focus.className} · truth ${decimal(focus.truthFraction * 100, 1)}% кадра`,
sourceUrl: item.assets.source,
truthUrl: item.assets.truth,
predictions: {
ddrnet: item.assets.ddrnet,
ppliteseg: item.assets.ppliteseg,
},
errors: {
ddrnet: item.assets.ddrnet_error,
ppliteseg: item.assets.ppliteseg_error,
},
};
});
}
function VegetationRouteEvidence({ result }: { result: VegetationShadowResult }) {
const route = result.routeVideo!;
const [tgs, setTgs] = useState<M49TgsFullShadowResult | null>(null);
@@ -83,16 +48,14 @@ function VegetationRouteEvidence({ result }: { result: VegetationShadowResult })
}, [route.baseM4ResultId, route.linkedTgsResultId]);
const semantic = {
id: "vegetation",
controlLabel: "ПРИРОДА · DDRNet",
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",
label: "DDRNet coarse vegetation material · recorded video",
maskAriaLabel: "DDRNet vegetation material prediction",
} as const;
if (route.linkedTgsResultId && tgs) {
@@ -100,19 +63,23 @@ function VegetationRouteEvidence({ result }: { result: VegetationShadowResult })
<M49TgsFullShadowEvidence
result={tgs}
semanticOverride={semantic}
evidenceLabel="LAB V1 · MATERIAL + YOLOX + TGS"
evidenceLabel="LAB V1 · EoMT + DDRNet + YOLOX + TGS"
/>
);
}
if (route.linkedTgsResultId && !tgsError) {
return <div className="m4-replay-threat-visual__pane-status" role="status">Открываем sealed TGS и coarse material timeline…</div>;
return (
<div className="m4-replay-threat-visual__pane-status" role="status">
Открываем sealed EoMT, TGS и coarse vegetation timeline…
</div>
);
}
return (
<>
<M4ReplayThreatVisual
resultId={route.baseM4ResultId}
evidenceLabel="LAB V1 · DDRNet"
showReferenceMediaLayers={route.viewKind === "coarse-material-policy-review"}
showReferenceMediaLayers
showSpatialOverlaySummary={false}
semantic={semantic}
/>
@@ -132,146 +99,117 @@ export function VegetationShadowResultView({
rigLabel: string;
result: VegetationShadowResult;
}) {
const route = result.routeVideo;
const selected = result.candidates.find(
(candidate) => candidate.candidate === result.selectedCandidate,
)!;
const alternative = result.candidates.find(
(candidate) => candidate.candidate !== result.selectedCandidate,
)!;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB V1 · готовые модели растительности"
description={result.routeVideo
? 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
? 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 не принят"}
title="LAB V1 · карта ровера · город + растительность"
description="Один recorded-контур RAVNOVES00 синхронно показывает городской EoMT, природный DDRNet, frozen YOLOX detections и causal TGS. Семантические маски переключаются, чтобы их цвета не скрывали друг друга; геометрическое veto остаётся независимым."
status={route
? "MULTILAYER RECORDED REVIEW · commands OFF · route truth отсутствует"
: "ROUTE EVIDENCE MISSING · commands OFF"}
statusTone="warning"
facts={[
{ label: "Источник", value: "GOOSE validation · 962 размеченных кадра · 12 vegetation hard cases" },
{ label: "Сравнение", value: "DDRNet-39 vs PPLiteSeg · official fine-64 weights" },
{ label: "Кейсы", value: "трава · куст · ствол · крона · изгородь · лес · посевы" },
...(result.routeVideo ? [{
label: "Видео",
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` },
{ label: "Источник", value: "RAVNOVES00 · sensor.camera.right · 4489 recorded frames" },
{ label: "Город", value: "EoMT Cityscapes · sealed E47 semantic archive" },
{ label: "Растительность", value: "DDRNet-39 fine-64 → coarse mission-neutral materials" },
{ label: "Safety", value: "YOLOX object boxes + causal TGS · semantic masks не снимают veto" },
{ label: "Authority", value: `${rigLabel} · VISUAL REVIEW 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?.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 не проецируется в пиксели без отдельной принятой калибровки.",
question: "Можно ли одновременно видеть городской и природный semantic stack, не теряя независимую геометрическую защиту?",
approach: "EoMT и DDRNet сохранены как два независимых sealed слоя на одной M4 timeline. В штатном M4.7 viewer пользователь переключает только отображаемую маску; YOLOX и TGS остаются активными слоями evidence.",
principalResult: route
? "Оба semantic archive доступны в одном viewer. Это не пиксельный fusion и не единая новая модель: городской и природный ответы остаются раздельными."
: "Route archive для этой immutable identity отсутствует.",
limitation: "RAVNOVES00 не имеет ручной truth. DDRNet заметно прыгает между HIGH GRASS, WOODY и UNKNOWN; поэтому subtype нельзя подавать напрямую в planner. Отсутствие класса никогда не означает свободный путь.",
}}
method={{
completeness: "complete",
completeness: route ? "complete" : "legacy-partial",
executionClass: "ai-inference",
pipelineId: "goose-fine64-ready-weights-to-ravnoves-policy-shadow/v1",
components: result.candidates.map((candidate) => ({
kind: "model" as const,
name: candidate.loadedModelName,
version: candidate.candidate,
role: candidate.candidate === result.selectedCandidate ? "selected policy provider" : "comparison candidate",
identitySha256: candidate.checkpointSha256,
})),
pipelineId: "ravnoves-eomt-ddrnet-yolox-causal-tgs-recorded-review/v1",
components: [
{
kind: "model",
name: "EoMT Cityscapes semantic",
version: "sealed E47 archive",
role: "urban semantic review",
identitySha256: null,
},
{
kind: "model",
name: selected.loadedModelName,
version: selected.candidate,
role: "vegetation material candidate",
identitySha256: selected.checkpointSha256,
},
{
kind: "algorithm",
name: "Frozen YOLOX + causal TGS",
version: "linked M4/M4.9 archives",
role: "independent object and geometry veto",
identitySha256: null,
},
],
}}
/>
)}
evidence={(
<>
<LaboratoryEvidence
eyebrow="M4.8 · GOOSE VEGETATION HARD CASES"
title="ERROR: красный — пропуск · жёлтый — лишнее · фиолетовый — перепутан тип · зелёный — совпадение"
kind="diagnostic-model"
resizable
>
<M48MaskComparisonVisual
cases={comparisonCases(result)}
initialCandidate={result.selectedCandidate}
/>
</LaboratoryEvidence>
{result.routeVideo ? (
<LaboratoryEvidence
eyebrow="M4.7 · RAVNOVES00 FULL VIDEO"
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
>
<VegetationRouteEvidence result={result} />
</LaboratoryEvidence>
) : null}
</>
evidence={route ? (
<LaboratoryEvidence
eyebrow="M4.7 · RAVNOVES00 FULL VIDEO"
title="EoMT CITY / DDRNet VEGETATION + YOLOX + CAUSAL TGS · 4489/4489 · TRUTH отсутствует"
kind="diagnostic-model"
resizable
>
<VegetationRouteEvidence result={result} />
</LaboratoryEvidence>
) : (
<LaboratoryEvidence
eyebrow="M4.7 · RAVNOVES00 FULL VIDEO"
title="ROUTE ARCHIVE отсутствует"
kind="diagnostic-model"
>
<div className="m4-replay-threat-visual__pane-status" role="alert">
Для этой immutable identity нет полного route video evidence.
</div>
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
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`}
title="Многослойный visual review собран; управление не авторизовано"
status="Semantics advisory · YOLOX/TGS veto cannot be cleared"
statusTone="warning"
metrics={[
{
label: "GOOSE mIoU",
value: `${decimal(selected.meanIouPercent, 2)}% / ${decimal(alternative.meanIouPercent, 2)}%`,
hint: `${selected.candidate} / ${alternative.candidate} · полный validation split`,
label: "Route masks",
value: route ? `${route.frameCount}/${route.frameCount}` : "0/4489",
hint: "sealed local playback · Worker для открытия не нужен",
},
{
label: "Vegetation IoU",
value: `${decimal(selected.vegetationMeanIouPercent, 2)}% / ${decimal(alternative.vegetationMeanIouPercent, 2)}%`,
hint: "агрегация классов grass/vegetation/bush/tree и родственных fine-64 labels",
label: "Semantic sources",
value: route ? "2 independent layers" : "0",
hint: "EoMT CITY / DDRNet VEGETATION · display switches, evidence does not fuse",
},
{
label: "Worker shadow p95",
value: `${decimal(selected.shadowLatencyP95Ms, 2)} / ${decimal(alternative.shadowLatencyP95Ms, 2)} ms`,
hint: "чистый inference · одна тяжёлая модель за раз",
label: "Vegetation worker p95",
value: `${decimal(selected.shadowLatencyP95Ms, 2)} ms`,
hint: "изолированный DDRNet inference; не совместный realtime stack",
},
{
label: "Cold prewarm",
value: `${decimal(selected.shadowPrewarmLatencyMs, 1)} / ${decimal(alternative.shadowPrewarmLatencyMs, 1)} ms`,
hint: "один явный inference до допуска кадров; исключён из steady-state p95",
label: "Vegetation peak VRAM",
value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} GiB`,
hint: "DDRNet candidate на Worker 006",
},
{
label: "Worker throughput",
value: `${decimal(selected.shadowThroughputFps, 1)} / ${decimal(alternative.shadowThroughputFps, 1)} FPS`,
hint: "изолированный Worker 006 · не realtime graph целиком",
},
{
label: "Peak VRAM",
value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} / ${decimal(alternative.peakReservedVramBytes / 1024 ** 3, 2)} GiB`,
hint: `${selected.candidate} / ${alternative.candidate} · RTX 4090`,
},
{
label: "Hard-case evidence",
value: "12 truth-backed cases",
hint: "8 vegetation strata · Worker для открытия не требуется",
},
...(result.routeVideo ? [{
label: "Route video",
value: "4489/4489 masks",
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: 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 геометрию не ослаблять.",
proved: "На одной recorded timeline доступны городской EoMT, природный DDRNet, YOLOX detections и causal TGS; LAB автономна от Worker.",
notProved: "Не доказаны совместный live-runtime EoMT+DDRNet, truth accuracy на fisheye, стабильные vegetation subtypes и безопасное управление ровером.",
decision: "Использовать маски только для диагностики. Следующий qualification gate — motion-aware temporal vegetation fusion и отдельный совместный realtime load test; до него planner/actuation остаются OFF.",
}}
/>
)}
@@ -10,6 +10,7 @@ export type LaboratoryProfileId =
| "rig-camera-local-surface-v1"
| "rig-track-geometry-temporal-v1"
| "rig-ravnoves-perception-gate-v1"
| "rig-goose-vegetation-benchmark-v1"
| "rig-pointpillars-transfer-v1"
| "rig-right-yolox-lidar-range-v1"
| "rig-nvidia-ready-stack-v1"
@@ -63,12 +64,19 @@ interface KnownWorkDefinition {
const rig = (rigLabel: string): string => rigLabel.trim() || "Сенсорный риг";
const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`>, KnownWorkDefinition>> = {
"lab-v1-vegetation-benchmark": {
profileId: "rig-goose-vegetation-benchmark-v1",
profileName: (rigLabel) => `${rig(rigLabel)} · GOOSE vegetation archive`,
experimentId: "lab-v1-vegetation-benchmark-archive",
experimentName: "DDRNet vs PPLiteSeg · truth-backed archival comparison",
variantName: "M4.8 · GOOSE truth · архивный анализ моделей",
},
"lab-v1-vegetation-shadow": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} · GOOSE vegetation qualification`,
profileName: (rigLabel) => `${rig(rigLabel)} · RAVNOVES00 rover perception gate`,
experimentId: "lab-v1-vegetation-mission-policy",
experimentName: "DDRNet vs PPLiteSeg · truth-backed vegetation hard cases",
variantName: "LAB V1 · готовые vegetation weights · GOOSE truth",
experimentName: "RAVNOVES00 · city + vegetation + TGS review",
variantName: "LAB V1 · EoMT + DDRNet + YOLOX + TGS · commands OFF",
},
"m48-object-centric-quality": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
@@ -18,6 +18,7 @@ function mergeResults(
next: AdvancedLaboratoryResults,
): AdvancedLaboratoryResults {
return {
vegetationBenchmark: next.vegetationBenchmark ?? current.vegetationBenchmark,
vegetationShadow: next.vegetationShadow ?? current.vegetationShadow,
m47Graph: next.m47Graph ?? current.m47Graph,
m48: next.m48 ?? current.m48,
@@ -122,6 +123,7 @@ export function useAdvancedLaboratoryCatalog({
const indexedResultId = index.find((item) => item.workId === selectedWorkId)?.resultId;
if (
[
"lab-v1-vegetation-benchmark",
"lab-v1-vegetation-shadow",
"m47-reference-graph-shadow",
"m48-object-centric-quality",