feat(lab): separate RAVNOVES transfer from public benchmark
This commit is contained in:
@@ -14,9 +14,11 @@ import { fetchE34TemporalLayerResult } from "./e34TemporalLayer";
|
||||
import { fetchE35DegradationRecoveryResult } from "./e35DegradationRecovery";
|
||||
import { fetchE40ProductGateResult } from "./e40ProductGate";
|
||||
import { fetchL3PointPillarsVisualAudit } from "./l3PointPillarsVisualAudit";
|
||||
import { fetchL31PointPillarsRavnoves } from "./l31PointPillarsRavnoves";
|
||||
|
||||
export type AdvancedLaboratoryWorkId =
|
||||
| "l3-pointpillars-visual-audit"
|
||||
| "l31-pointpillars-ravnoves"
|
||||
| "e31-source-binding"
|
||||
| "e32-track-geometry"
|
||||
| "e33-worker-shadow"
|
||||
@@ -35,6 +37,7 @@ export interface AdvancedLaboratoryIndexItem {
|
||||
|
||||
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||
"l3-pointpillars-visual-audit",
|
||||
"l31-pointpillars-ravnoves",
|
||||
"e31-source-binding",
|
||||
"e32-track-geometry",
|
||||
"e33-worker-shadow",
|
||||
@@ -48,6 +51,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||
|
||||
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
|
||||
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
|
||||
"l31-pointpillars-ravnoves": "l31-pointpillars-ravnoves",
|
||||
"e31-source-binding": "e31-source-qualification",
|
||||
"e32-track-geometry": "e32-track-geometry",
|
||||
"e33-worker-shadow": "e33-worker-shadow",
|
||||
@@ -68,6 +72,7 @@ export function isAdvancedLaboratoryWorkId(
|
||||
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
|
||||
return {
|
||||
l3: null,
|
||||
l31: null,
|
||||
e31: null,
|
||||
e32: null,
|
||||
e33: null,
|
||||
@@ -169,6 +174,7 @@ export function advancedLaboratoryResultAvailable(
|
||||
results: AdvancedLaboratoryResults,
|
||||
): boolean {
|
||||
return workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
|
||||
: workId === "l31-pointpillars-ravnoves" ? results.l31 !== null
|
||||
: workId === "e31-source-binding" ? results.e31 !== null
|
||||
: workId === "e32-track-geometry" ? results.e32 !== null
|
||||
: workId === "e33-worker-shadow" ? results.e33 !== null
|
||||
@@ -193,6 +199,8 @@ export async function fetchAdvancedLaboratoryResult(
|
||||
const results = emptyAdvancedLaboratoryResults();
|
||||
if (workId === "l3-pointpillars-visual-audit") {
|
||||
results.l3 = await fetchL3PointPillarsVisualAudit({ fetcher, signal });
|
||||
} else if (workId === "l31-pointpillars-ravnoves") {
|
||||
results.l31 = await fetchL31PointPillarsRavnoves({ fetcher, signal });
|
||||
} else if (workId === "e31-source-binding") {
|
||||
results.e31 = await fetchOne(
|
||||
"/api/v1/laboratory/e31/results?limit=1",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type {
|
||||
E31LaboratoryResult,
|
||||
E32LaboratoryResult,
|
||||
E33LaboratoryResult,
|
||||
E37AcceptanceContractResult,
|
||||
E38PerceptionBaselineResult,
|
||||
E39PerceptionRefinementResult,
|
||||
} from "./advancedResults";
|
||||
import type { E34TemporalLayerResult } from "./e34TemporalLayer";
|
||||
import type { E35DegradationRecoveryResult } from "./e35DegradationRecovery";
|
||||
import type { E40PerceptionProductGateResult } from "./e40ProductGate";
|
||||
import type { L3PointPillarsVisualAuditResult } from "./l3PointPillarsVisualAudit";
|
||||
import type { L31PointPillarsRavnovesResult } from "./l31PointPillarsRavnoves";
|
||||
|
||||
export interface AdvancedLaboratoryResults {
|
||||
l3: L3PointPillarsVisualAuditResult | null;
|
||||
l31: L31PointPillarsRavnovesResult | null;
|
||||
e31: E31LaboratoryResult | null;
|
||||
e32: E32LaboratoryResult | null;
|
||||
e33: E33LaboratoryResult | null;
|
||||
e34: E34TemporalLayerResult | null;
|
||||
e35: E35DegradationRecoveryResult | null;
|
||||
e37: E37AcceptanceContractResult | null;
|
||||
e38: E38PerceptionBaselineResult | null;
|
||||
e39: E39PerceptionRefinementResult | null;
|
||||
e40: E40PerceptionProductGateResult | null;
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
import {
|
||||
fetchE34TemporalLayerResult,
|
||||
type E34TemporalLayerResult,
|
||||
} from "./e34TemporalLayer";
|
||||
import {
|
||||
fetchE35DegradationRecoveryResult,
|
||||
type E35DegradationRecoveryResult,
|
||||
} from "./e35DegradationRecovery";
|
||||
import {
|
||||
fetchE40ProductGateResult,
|
||||
type E40PerceptionProductGateResult,
|
||||
} from "./e40ProductGate";
|
||||
import { settledCatalogValue } from "./catalogTransport";
|
||||
import type { L3PointPillarsVisualAuditResult } from "./l3PointPillarsVisualAudit";
|
||||
import type { AdvancedLaboratoryResults } from "./advancedLaboratoryResults";
|
||||
export type { AdvancedLaboratoryResults } from "./advancedLaboratoryResults";
|
||||
|
||||
export interface E31LaboratoryResult {
|
||||
resultId: string;
|
||||
@@ -238,19 +236,6 @@ export interface E39PerceptionRefinementResult {
|
||||
access: "read-only";
|
||||
}
|
||||
|
||||
export interface AdvancedLaboratoryResults {
|
||||
l3: L3PointPillarsVisualAuditResult | null;
|
||||
e31: E31LaboratoryResult | null;
|
||||
e32: E32LaboratoryResult | null;
|
||||
e33: E33LaboratoryResult | null;
|
||||
e34: E34TemporalLayerResult | null;
|
||||
e35: E35DegradationRecoveryResult | null;
|
||||
e37: E37AcceptanceContractResult | null;
|
||||
e38: E38PerceptionBaselineResult | null;
|
||||
e39: E39PerceptionRefinementResult | null;
|
||||
e40: E40PerceptionProductGateResult | null;
|
||||
}
|
||||
|
||||
export class AdvancedLaboratoryContractError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
@@ -990,5 +975,17 @@ export async function fetchAdvancedLaboratoryResults({
|
||||
const e38 = settledCatalogValue(settled[6]);
|
||||
const e39 = settledCatalogValue(settled[7]);
|
||||
const e40 = settledCatalogValue(settled[8]);
|
||||
return { l3: null, e31, e32, e33, e34, e35, e37, e38, e39, e40 };
|
||||
return {
|
||||
l3: null,
|
||||
l31: null,
|
||||
e31,
|
||||
e32,
|
||||
e33,
|
||||
e34,
|
||||
e35,
|
||||
e37,
|
||||
e38,
|
||||
e39,
|
||||
e40,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
import {
|
||||
AdvancedLaboratoryContractError,
|
||||
type LaboratoryFetch,
|
||||
} from "./advancedResults";
|
||||
import type { L3VisualBox } from "./l3PointPillarsVisualAudit";
|
||||
|
||||
export interface L31ClassCounts {
|
||||
Vehicle: number;
|
||||
Pedestrian: number;
|
||||
Cyclist: number;
|
||||
}
|
||||
|
||||
export interface L31VisualFrameSummary {
|
||||
frameId: string;
|
||||
frameIndex: number;
|
||||
sessionSeconds: number;
|
||||
sourcePointCount: number;
|
||||
predictionCount: number;
|
||||
classCounts: L31ClassCounts;
|
||||
inferenceMs: number;
|
||||
}
|
||||
|
||||
export interface L31PointPillarsRavnovesResult {
|
||||
resultId: string;
|
||||
createdAtUtc: string;
|
||||
status: "cross-domain-transfer-measured-visual-review-required";
|
||||
sourceSessionId: string;
|
||||
sourcePackId: string;
|
||||
sourceLogicalContentSha256: string;
|
||||
model: {
|
||||
name: "pointpillars";
|
||||
sourceModelSha256: string;
|
||||
engineSha256: string;
|
||||
embeddedScoreThreshold: number;
|
||||
};
|
||||
execution: {
|
||||
workerHostId: "worker-006";
|
||||
sequential: true;
|
||||
parallelWorkers: 1;
|
||||
sourcePaced: false;
|
||||
existingTritonOnly: true;
|
||||
};
|
||||
metrics: {
|
||||
frameCount: number;
|
||||
inputAdmissionFraction: number;
|
||||
outputSchemaValidFraction: number;
|
||||
framesWithPredictions: number;
|
||||
framesWithVehiclePredictions: number;
|
||||
predictionCount: number;
|
||||
classCounts: L31ClassCounts;
|
||||
inferenceLatencyMs: { p50: number; p95: number; maximum: number };
|
||||
poseBindingAgeMs: { p50: number; p95: number; maximum: number };
|
||||
deterministicReplayFraction: number;
|
||||
deterministicReplayFrames: number;
|
||||
};
|
||||
frames: readonly L31VisualFrameSummary[];
|
||||
limitations: readonly string[];
|
||||
}
|
||||
|
||||
export interface L31VisualFrame {
|
||||
frameId: string;
|
||||
summary: L31VisualFrameSummary & {
|
||||
deterministicReplay: boolean;
|
||||
replayInferenceMs: number;
|
||||
visualBoxCount: number;
|
||||
visualBoxTruncated: boolean;
|
||||
};
|
||||
sourcePointCount: number;
|
||||
modelRangePointCount: number;
|
||||
sampledPointCount: number;
|
||||
pointsXyzi: readonly number[];
|
||||
truthBoxes: readonly L3VisualBox[];
|
||||
predictionBoxes: readonly L3VisualBox[];
|
||||
}
|
||||
|
||||
const RESULT_ID = /^l31-pointpillars-ravnoves-[a-f0-9]{64}$/;
|
||||
const PACK_ID = /^lidar-replay-pack-[a-f0-9]{64}$/;
|
||||
const SHA256 = /^[a-f0-9]{64}$/;
|
||||
const FRAME_ID = /^[0-9]{6}$/;
|
||||
|
||||
function record(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: ожидался объект.`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function array(value: unknown, label: string): readonly unknown[] {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: ожидался массив.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function string(value: unknown, label: string): string {
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: ожидалась строка.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function exact<T extends string>(value: unknown, expected: T, label: string): T {
|
||||
if (value !== expected) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: нарушен контракт.`);
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
function number(value: unknown, label: string, minimum = 0): number {
|
||||
if (
|
||||
typeof value !== "number"
|
||||
|| !Number.isFinite(value)
|
||||
|| value < minimum
|
||||
) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: неверное число.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function integer(value: unknown, label: string): number {
|
||||
const parsed = number(value, label);
|
||||
if (!Number.isInteger(parsed)) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: ожидалось целое.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function boolean(value: unknown, label: string): boolean {
|
||||
if (typeof value !== "boolean") {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: ожидался boolean.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function exactBoolean(
|
||||
value: unknown,
|
||||
expected: boolean,
|
||||
label: string,
|
||||
): boolean {
|
||||
const parsed = boolean(value, label);
|
||||
if (parsed !== expected) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: нарушен контракт.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function classCounts(value: unknown, label: string): L31ClassCounts {
|
||||
const counts = record(value, label);
|
||||
if (
|
||||
Object.keys(counts).sort().join(",") !== "Cyclist,Pedestrian,Vehicle"
|
||||
) {
|
||||
throw new AdvancedLaboratoryContractError(`${label}: классы изменились.`);
|
||||
}
|
||||
return {
|
||||
Vehicle: integer(counts.Vehicle, `${label}.Vehicle`),
|
||||
Pedestrian: integer(counts.Pedestrian, `${label}.Pedestrian`),
|
||||
Cyclist: integer(counts.Cyclist, `${label}.Cyclist`),
|
||||
};
|
||||
}
|
||||
|
||||
function distribution(
|
||||
value: unknown,
|
||||
label: string,
|
||||
): { p50: number; p95: number; maximum: number } {
|
||||
const item = record(value, label);
|
||||
return {
|
||||
p50: number(item.p50, `${label}.p50`),
|
||||
p95: number(item.p95, `${label}.p95`),
|
||||
maximum: number(item.maximum, `${label}.maximum`),
|
||||
};
|
||||
}
|
||||
|
||||
function parseSummary(value: unknown): L31VisualFrameSummary {
|
||||
const item = record(value, "L3.1 frame");
|
||||
const frameId = string(item.frame_id, "L3.1 frame_id");
|
||||
if (!FRAME_ID.test(frameId)) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 frame_id: неверный формат.");
|
||||
}
|
||||
return {
|
||||
frameId,
|
||||
frameIndex: integer(item.frame_index, "L3.1 frame_index"),
|
||||
sessionSeconds: number(item.session_seconds, "L3.1 session_seconds"),
|
||||
sourcePointCount: integer(
|
||||
item.source_point_count,
|
||||
"L3.1 source_point_count",
|
||||
),
|
||||
predictionCount: integer(
|
||||
item.prediction_count,
|
||||
"L3.1 prediction_count",
|
||||
),
|
||||
classCounts: classCounts(item.class_counts, "L3.1 class_counts"),
|
||||
inferenceMs: number(
|
||||
item.inference_ms,
|
||||
"L3.1 inference_ms",
|
||||
Number.MIN_VALUE,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function parseResult(value: unknown): L31PointPillarsRavnovesResult {
|
||||
const item = record(value, "L3.1 result");
|
||||
exact(
|
||||
item.schema_version,
|
||||
"missioncore.l31-pointpillars-ravnoves-result/v1",
|
||||
"L3.1 schema_version",
|
||||
);
|
||||
exact(item.access, "read-only", "L3.1 access");
|
||||
const resultId = string(item.result_id, "L3.1 result_id");
|
||||
const sourcePackId = string(item.source_pack_id, "L3.1 source_pack_id");
|
||||
const logicalSha = string(
|
||||
item.source_logical_content_sha256,
|
||||
"L3.1 logical content",
|
||||
);
|
||||
if (
|
||||
!RESULT_ID.test(resultId)
|
||||
|| !PACK_ID.test(sourcePackId)
|
||||
|| !SHA256.test(logicalSha)
|
||||
) {
|
||||
throw new AdvancedLaboratoryContractError(
|
||||
"L3.1: нарушена идентичность результата.",
|
||||
);
|
||||
}
|
||||
const model = record(item.model, "L3.1 model");
|
||||
const execution = record(item.execution, "L3.1 execution");
|
||||
const metrics = record(item.metrics, "L3.1 metrics");
|
||||
const frames = array(item.frames, "L3.1 frames").map(parseSummary);
|
||||
if (
|
||||
!frames.length
|
||||
|| frames.length > 18
|
||||
|| new Set(frames.map(({ frameId }) => frameId)).size !== frames.length
|
||||
) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 frames: неверный каталог.");
|
||||
}
|
||||
return {
|
||||
resultId,
|
||||
createdAtUtc: string(item.created_at_utc, "L3.1 created_at_utc"),
|
||||
status: exact(
|
||||
item.status,
|
||||
"cross-domain-transfer-measured-visual-review-required",
|
||||
"L3.1 status",
|
||||
),
|
||||
sourceSessionId: exact(
|
||||
item.source_session_id,
|
||||
"20260720T065719Z_viewer_live",
|
||||
"L3.1 source_session_id",
|
||||
),
|
||||
sourcePackId,
|
||||
sourceLogicalContentSha256: logicalSha,
|
||||
model: {
|
||||
name: exact(model.name, "pointpillars", "L3.1 model.name"),
|
||||
sourceModelSha256: string(
|
||||
model.source_model_sha256,
|
||||
"L3.1 model sha",
|
||||
),
|
||||
engineSha256: string(model.engine_sha256, "L3.1 engine sha"),
|
||||
embeddedScoreThreshold: number(
|
||||
model.embedded_score_threshold,
|
||||
"L3.1 score threshold",
|
||||
),
|
||||
},
|
||||
execution: {
|
||||
workerHostId: exact(
|
||||
execution.worker_host_id,
|
||||
"worker-006",
|
||||
"L3.1 worker",
|
||||
),
|
||||
sequential: exactBoolean(
|
||||
execution.sequential,
|
||||
true,
|
||||
"L3.1 sequential",
|
||||
) as true,
|
||||
parallelWorkers: (
|
||||
integer(execution.parallel_workers, "L3.1 parallel workers") === 1
|
||||
? 1
|
||||
: (() => {
|
||||
throw new AdvancedLaboratoryContractError(
|
||||
"L3.1 parallel workers: нарушен контракт.",
|
||||
);
|
||||
})()
|
||||
),
|
||||
sourcePaced: exactBoolean(
|
||||
execution.source_paced,
|
||||
false,
|
||||
"L3.1 source paced",
|
||||
) as false,
|
||||
existingTritonOnly: exactBoolean(
|
||||
execution.existing_triton_only,
|
||||
true,
|
||||
"L3.1 Triton",
|
||||
) as true,
|
||||
},
|
||||
metrics: {
|
||||
frameCount: integer(metrics.frame_count, "L3.1 frame count"),
|
||||
inputAdmissionFraction: number(
|
||||
metrics.input_admission_fraction,
|
||||
"L3.1 admission",
|
||||
),
|
||||
outputSchemaValidFraction: number(
|
||||
metrics.output_schema_valid_fraction,
|
||||
"L3.1 schema valid",
|
||||
),
|
||||
framesWithPredictions: integer(
|
||||
metrics.frames_with_predictions,
|
||||
"L3.1 frames with predictions",
|
||||
),
|
||||
framesWithVehiclePredictions: integer(
|
||||
metrics.frames_with_vehicle_predictions,
|
||||
"L3.1 frames with vehicles",
|
||||
),
|
||||
predictionCount: integer(
|
||||
metrics.prediction_count,
|
||||
"L3.1 predictions",
|
||||
),
|
||||
classCounts: classCounts(metrics.class_counts, "L3.1 metric classes"),
|
||||
inferenceLatencyMs: distribution(
|
||||
metrics.inference_latency_ms,
|
||||
"L3.1 latency",
|
||||
),
|
||||
poseBindingAgeMs: distribution(
|
||||
metrics.pose_binding_age_ms,
|
||||
"L3.1 pose age",
|
||||
),
|
||||
deterministicReplayFraction: number(
|
||||
metrics.deterministic_replay_fraction,
|
||||
"L3.1 determinism",
|
||||
),
|
||||
deterministicReplayFrames: integer(
|
||||
metrics.deterministic_replay_frames,
|
||||
"L3.1 determinism frames",
|
||||
),
|
||||
},
|
||||
frames,
|
||||
limitations: array(item.limitations, "L3.1 limitations").map(
|
||||
(entry) => string(entry, "L3.1 limitation"),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function parseBox(value: unknown): L3VisualBox {
|
||||
const box = record(value, "L3.1 box");
|
||||
const center = [
|
||||
number(box.x_m, "L3.1 box.x", -Infinity),
|
||||
number(box.y_m, "L3.1 box.y", -Infinity),
|
||||
number(box.z_m, "L3.1 box.z", -Infinity),
|
||||
] as const;
|
||||
const size = [
|
||||
number(box.length_m, "L3.1 box.length", Number.MIN_VALUE),
|
||||
number(box.width_m, "L3.1 box.width", Number.MIN_VALUE),
|
||||
number(box.height_m, "L3.1 box.height", Number.MIN_VALUE),
|
||||
] as const;
|
||||
const modelClass = string(box.model_class, "L3.1 box.class");
|
||||
if (!["Vehicle", "Pedestrian", "Cyclist"].includes(modelClass)) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 box.class: неизвестен.");
|
||||
}
|
||||
return {
|
||||
benchmarkClass: modelClass,
|
||||
centerXyzM: center,
|
||||
sizeLwhM: size,
|
||||
yawRad: number(box.yaw_rad, "L3.1 box.yaw", -Infinity),
|
||||
status: "model-prediction",
|
||||
score: number(box.score, "L3.1 box.score"),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchL31PointPillarsRavnoves({
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
}: {
|
||||
fetcher?: LaboratoryFetch;
|
||||
signal?: AbortSignal;
|
||||
} = {}): Promise<L31PointPillarsRavnovesResult | null> {
|
||||
const response = await fetcher(
|
||||
"/api/v1/laboratory/l31/pointpillars-ravnoves/results?limit=1",
|
||||
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new AdvancedLaboratoryContractError(
|
||||
`L3.1 RAVNOVES недоступен: HTTP ${response.status}.`,
|
||||
);
|
||||
}
|
||||
const catalog = record(await response.json(), "L3.1 catalog");
|
||||
exact(
|
||||
catalog.schema_version,
|
||||
"missioncore.l31-pointpillars-ravnoves-catalog-results/v1",
|
||||
"L3.1 catalog schema",
|
||||
);
|
||||
const items = array(catalog.items, "L3.1 catalog.items");
|
||||
if (items.length > 1) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 catalog: лишние результаты.");
|
||||
}
|
||||
return items.length ? parseResult(items[0]) : null;
|
||||
}
|
||||
|
||||
export async function fetchL31PointPillarsRavnovesFrame(
|
||||
resultId: string,
|
||||
frameId: string,
|
||||
{
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
}: {
|
||||
fetcher?: LaboratoryFetch;
|
||||
signal?: AbortSignal;
|
||||
} = {},
|
||||
): Promise<L31VisualFrame> {
|
||||
if (!RESULT_ID.test(resultId) || !FRAME_ID.test(frameId)) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 frame: неверная identity.");
|
||||
}
|
||||
const response = await fetcher(
|
||||
`/api/v1/laboratory/l31/pointpillars-ravnoves/${resultId}/frames/${frameId}`,
|
||||
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new AdvancedLaboratoryContractError(
|
||||
`L3.1 frame недоступен: HTTP ${response.status}.`,
|
||||
);
|
||||
}
|
||||
const payload = record(await response.json(), "L3.1 visual frame");
|
||||
exact(
|
||||
payload.schema_version,
|
||||
"missioncore.l31-pointpillars-ravnoves-visual-frame/v1",
|
||||
"L3.1 visual schema",
|
||||
);
|
||||
exact(payload.frame_id, frameId, "L3.1 visual frame_id");
|
||||
const summaryRaw = record(payload.summary, "L3.1 visual summary");
|
||||
const summary = parseSummary(summaryRaw);
|
||||
const points = record(payload.points, "L3.1 points");
|
||||
exact(points.layout, "flat-xyzi", "L3.1 points.layout");
|
||||
const pointValues = array(points.values, "L3.1 points.values").map(
|
||||
(entry, index) => number(entry, `L3.1 points[${index}]`, -Infinity),
|
||||
);
|
||||
const sampledPointCount = integer(
|
||||
points.sampled_point_count,
|
||||
"L3.1 sampled points",
|
||||
);
|
||||
if (sampledPointCount > 12_000 || pointValues.length !== sampledPointCount * 4) {
|
||||
throw new AdvancedLaboratoryContractError("L3.1 points: нарушен bound.");
|
||||
}
|
||||
return {
|
||||
frameId,
|
||||
summary: {
|
||||
...summary,
|
||||
deterministicReplay: boolean(
|
||||
summaryRaw.deterministic_replay,
|
||||
"L3.1 deterministic replay",
|
||||
),
|
||||
replayInferenceMs: number(
|
||||
summaryRaw.replay_inference_ms,
|
||||
"L3.1 replay latency",
|
||||
),
|
||||
visualBoxCount: integer(
|
||||
summaryRaw.visual_box_count,
|
||||
"L3.1 visual box count",
|
||||
),
|
||||
visualBoxTruncated: boolean(
|
||||
summaryRaw.visual_box_truncated,
|
||||
"L3.1 visual box truncated",
|
||||
),
|
||||
},
|
||||
sourcePointCount: integer(
|
||||
points.source_point_count,
|
||||
"L3.1 source points",
|
||||
),
|
||||
modelRangePointCount: integer(
|
||||
points.model_range_point_count,
|
||||
"L3.1 model range points",
|
||||
),
|
||||
sampledPointCount,
|
||||
pointsXyzi: pointValues,
|
||||
truthBoxes: [],
|
||||
predictionBoxes: array(
|
||||
payload.prediction_boxes,
|
||||
"L3.1 prediction boxes",
|
||||
).map(parseBox),
|
||||
};
|
||||
}
|
||||
@@ -21,7 +21,12 @@ export interface L3VisualBox {
|
||||
centerXyzM: readonly [number, number, number];
|
||||
sizeLwhM: readonly [number, number, number];
|
||||
yawRad: number;
|
||||
status: "matched" | "false-negative" | "true-positive" | "false-positive";
|
||||
status:
|
||||
| "matched"
|
||||
| "false-negative"
|
||||
| "true-positive"
|
||||
| "false-positive"
|
||||
| "model-prediction";
|
||||
score: number | null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user