feat(lidar): qualify Patchwork++ on GOOSE

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 15:22:28 +03:00
parent 951b40c870
commit 60ba64004b
15 changed files with 1082 additions and 256 deletions
@@ -90,25 +90,43 @@ export interface DatasetNativeScanPreview {
}>;
}
export interface DatasetGroundMetrics {
precision: number;
recall: number;
f1: number;
groundIou: number;
accuracy: number;
artificialGroundRecall: number;
naturalGroundRecall: number;
obstacleNonGroundRecall: number;
}
export interface DatasetGroundComparison {
sourceId: "goose-3d/v2025-08-22";
frameId: string;
pointCount: number;
currentGround: number[];
patchworkGround: number[];
patchworkAssigned: number[];
groundTruthGround: number[];
evaluated: number[];
disagreement: number[];
currentDisagreement: number[];
patchworkDisagreement: number[];
metrics: {
precision: number;
recall: number;
f1: number;
groundIou: number;
accuracy: number;
artificialGroundRecall: number;
naturalGroundRecall: number;
obstacleNonGroundRecall: number;
current: DatasetGroundMetrics;
patchworkpp: DatasetGroundMetrics;
};
latencyMs: {
current: number;
patchworkpp: number;
};
patchworkAssignedFraction: number;
inputProfile: {
frameId: "sensor/lidar/vls128_roof";
sensorHeightM: number;
heightEvidence: "published-dimensioned-schematic";
normalizedScanProduced: false;
};
latencyMs: number;
}
export class DatasetGatewayContractError extends Error {}
@@ -500,7 +518,7 @@ export function parseDatasetGroundComparison(
): DatasetGroundComparison {
const source = record(value, "Ground comparison");
if (
source.schema_version !== "missioncore.dataset-ground-comparison-preview/v1"
source.schema_version !== "missioncore.dataset-ground-comparison-preview/v2"
|| source.source_id !== "goose-3d/v2025-08-22"
|| source.sampling !== "deterministic-even-index"
) {
@@ -508,41 +526,135 @@ export function parseDatasetGroundComparison(
}
const pointCount = number(source.point_count, "point_count");
const currentGround = integers(source.current_ground, "current_ground", 1);
const patchworkGround = integers(source.patchwork_ground, "patchwork_ground", 1);
const patchworkAssigned = integers(
source.patchwork_assigned,
"patchwork_assigned",
1,
);
const groundTruthGround = integers(
source.ground_truth_ground,
"ground_truth_ground",
1,
);
const evaluated = integers(source.evaluated, "evaluated", 1);
const disagreement = integers(source.disagreement, "disagreement", 1);
const currentDisagreement = integers(
source.current_disagreement,
"current_disagreement",
1,
);
const patchworkDisagreement = integers(
source.patchwork_disagreement,
"patchwork_disagreement",
1,
);
if (
pointCount < 1
|| pointCount > 50_000
|| currentGround.length !== pointCount
|| patchworkGround.length !== pointCount
|| patchworkAssigned.length !== pointCount
|| groundTruthGround.length !== pointCount
|| evaluated.length !== pointCount
|| disagreement.length !== pointCount
|| currentDisagreement.length !== pointCount
|| patchworkDisagreement.length !== pointCount
) {
throw new DatasetGatewayContractError("Ground comparison arrays не выровнены");
}
const metrics = record(source.metrics, "metrics");
const fraction = (key: string): number => {
const value = number(metrics[key], `metrics.${key}`);
if (value > 1) {
throw new DatasetGatewayContractError(`metrics.${key}: ожидалась доля`);
}
return value;
const parseMetrics = (
value: unknown,
label: string,
): DatasetGroundMetrics => {
const providerMetrics = record(value, label);
const fraction = (key: string): number => {
const result = number(providerMetrics[key], `${label}.${key}`);
if (result > 1) {
throw new DatasetGatewayContractError(`${label}.${key}: ожидалась доля`);
}
return result;
};
return {
precision: fraction("precision"),
recall: fraction("recall"),
f1: fraction("f1"),
groundIou: fraction("ground_iou"),
accuracy: fraction("accuracy"),
artificialGroundRecall: fraction("artificial_ground_recall"),
naturalGroundRecall: fraction("natural_ground_recall"),
obstacleNonGroundRecall: fraction("obstacle_non_ground_recall"),
};
};
const provider = record(source.provider, "provider");
const currentMetrics = parseMetrics(metrics.current, "metrics.current");
const patchworkMetrics = parseMetrics(
metrics.patchworkpp,
"metrics.patchworkpp",
);
const providers = record(source.providers, "providers");
const currentProvider = record(providers.current, "providers.current");
const patchworkProvider = record(providers.patchworkpp, "providers.patchworkpp");
if (
provider.provider_id !== "missioncore-local-percentile-ground/v1"
|| provider.ground_truth !== false
currentProvider.provider_id !== "missioncore-local-percentile-ground/v1"
|| currentProvider.ground_truth !== false
|| !/^[a-f0-9]{64}$/.test(
string(provider.implementation_sha256, "provider.implementation_sha256"),
string(
currentProvider.implementation_sha256,
"providers.current.implementation_sha256",
),
)
|| patchworkProvider.provider_id !== "patchworkpp/v1.4.1"
|| patchworkProvider.source_tag !== "v1.4.1"
|| patchworkProvider.source_commit
!== "3e6903a1d5537a4cc2ace897b0bbb98a92d6014c"
|| patchworkProvider.ground_truth !== false
|| !/^[a-f0-9]{64}$/.test(
string(
patchworkProvider.binary_sha256,
"providers.patchworkpp.binary_sha256",
),
)
) {
throw new DatasetGatewayContractError("Ground comparison provider несовместим");
}
const latency = record(source.latency_ms, "latency_ms");
const inputProfile = record(source.input_profile, "input_profile");
const sensorFrame = record(inputProfile.sensor_frame, "input_profile.sensor_frame");
const height = record(inputProfile.height, "input_profile.height");
const scope = record(inputProfile.scope, "input_profile.scope");
const sensorHeightM = number(
height.sensor_above_ground_m,
"height.sensor_above_ground_m",
);
if (
inputProfile.schema_version !== "missioncore.goose-patchwork-profile/v1"
|| inputProfile.source_id !== "goose-3d/v2025-08-22"
|| inputProfile.representation !== "native-scan"
|| sensorFrame.frame_id !== "sensor/lidar/vls128_roof"
|| sensorFrame.handedness !== "right"
|| sensorFrame.x !== "forward"
|| sensorFrame.y !== "left"
|| sensorFrame.z !== "up"
|| sensorFrame.one_revolution !== true
|| height.base_link_above_ground_m !== 0.64
|| height.lidar_above_base_link_m !== 1.6
|| sensorHeightM !== 2.24
|| height.evidence !== "published-dimensioned-schematic"
|| scope.patchworkpp_eligible !== true
|| scope.normalized_scan_produced !== false
|| scope.complete_vehicle_transform_known !== false
|| scope.deskew_claimed !== false
) {
throw new DatasetGatewayContractError("GOOSE Patchwork++ profile несовместим");
}
const patchworkAssignedFraction = number(
source.patchwork_assigned_fraction,
"patchwork_assigned_fraction",
);
if (patchworkAssignedFraction > 1) {
throw new DatasetGatewayContractError(
"patchwork_assigned_fraction: ожидалась доля",
);
}
const safety = record(source.safety, "safety");
if (
safety.qualification_only !== true
@@ -555,20 +667,27 @@ export function parseDatasetGroundComparison(
frameId: string(source.frame_id, "frame_id", true),
pointCount,
currentGround,
patchworkGround,
patchworkAssigned,
groundTruthGround,
evaluated,
disagreement,
currentDisagreement,
patchworkDisagreement,
metrics: {
precision: fraction("precision"),
recall: fraction("recall"),
f1: fraction("f1"),
groundIou: fraction("ground_iou"),
accuracy: fraction("accuracy"),
artificialGroundRecall: fraction("artificial_ground_recall"),
naturalGroundRecall: fraction("natural_ground_recall"),
obstacleNonGroundRecall: fraction("obstacle_non_ground_recall"),
current: currentMetrics,
patchworkpp: patchworkMetrics,
},
latencyMs: {
current: number(latency.current, "latency_ms.current"),
patchworkpp: number(latency.patchworkpp, "latency_ms.patchworkpp"),
},
patchworkAssignedFraction,
inputProfile: {
frameId: "sensor/lidar/vls128_roof",
sensorHeightM,
heightEvidence: "published-dimensioned-schematic",
normalizedScanProduced: false,
},
latencyMs: number(source.latency_ms, "latency_ms"),
};
}