feat: qualify K1 local surface over time
This commit is contained in:
@@ -6,6 +6,8 @@ import { createServer } from "vite";
|
||||
let server;
|
||||
let parseLidarLocalSurfaceCatalog;
|
||||
let parseLidarLocalSurfaceFrame;
|
||||
let parseLidarLocalSurfaceTimeline;
|
||||
let fetchLidarLocalSurfaceTimeline;
|
||||
let LidarLocalSurfaceContractError;
|
||||
|
||||
const modelId = `k1-local-surface-${"a".repeat(64)}`;
|
||||
@@ -71,6 +73,28 @@ function model(overrides = {}) {
|
||||
confidence: distribution(0.8),
|
||||
pose_binding_age_ms: distribution(7),
|
||||
surface_max_age_ms: distribution(1000),
|
||||
temporal_qualification: {
|
||||
prediction: {
|
||||
current_frame_excluded: true,
|
||||
sample_count: 9,
|
||||
residual_p50_m: distribution(0.04),
|
||||
residual_p95_m: distribution(0.2),
|
||||
inlier_fraction: distribution(0.95),
|
||||
},
|
||||
stability: {
|
||||
sample_count: 9,
|
||||
height_delta_m: distribution(0.01),
|
||||
slope_delta_deg: distribution(0.1),
|
||||
roughness_delta_m: distribution(0.002),
|
||||
jump_count: 1,
|
||||
},
|
||||
step_candidates: {
|
||||
is_ground_truth: false,
|
||||
frames_with_candidates: 8,
|
||||
cell_count: distribution(20),
|
||||
point_count: distribution(12),
|
||||
},
|
||||
},
|
||||
build_elapsed_ms: 20,
|
||||
},
|
||||
anchors: [{
|
||||
@@ -127,6 +151,7 @@ function frame(overrides = {}) {
|
||||
points_xyz_m: [[0, 0, 0], [1, 0, 0], [1, 1, 0.7], [2, 0, -0.3]],
|
||||
point_class: [1, 1, 2, 3],
|
||||
point_height_m: [0, 0.02, 0.7, -0.3],
|
||||
point_step_candidate: [0, 1, 0, 0],
|
||||
pose: {
|
||||
position_xyz_m: [0, 0, 1.3],
|
||||
orientation_xyzw: [0, 0, 0, 1],
|
||||
@@ -147,6 +172,23 @@ function frame(overrides = {}) {
|
||||
surface: 2,
|
||||
occupied: 1,
|
||||
below_surface: 1,
|
||||
step_candidate: 1,
|
||||
},
|
||||
prediction: {
|
||||
available: true,
|
||||
current_frame_excluded: true,
|
||||
cell_count: 32,
|
||||
residual_p50_m: 0.04,
|
||||
residual_p95_m: 0.2,
|
||||
inlier_fraction: 0.95,
|
||||
},
|
||||
temporal: {
|
||||
compared: true,
|
||||
height_delta_m: 0.01,
|
||||
slope_delta_deg: 0.1,
|
||||
roughness_delta_m: 0.002,
|
||||
jump: false,
|
||||
step_candidate_cell_count: 5,
|
||||
},
|
||||
classes: {},
|
||||
occupancy_policy: policy(),
|
||||
@@ -160,6 +202,41 @@ function frame(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function timeline(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.k1-local-surface-timeline/v1",
|
||||
model_id: modelId,
|
||||
source_pack_id: sourcePackId,
|
||||
session_id: "20260720T065719Z_viewer_live",
|
||||
frame_count: 4,
|
||||
source_frame_index: [1000, 1001, 1002, 1003],
|
||||
session_seconds: [0, 0.1, 0.2, 0.3],
|
||||
source_available: [1, 1, 1, 1],
|
||||
valid: [1, 1, 1, 1],
|
||||
prediction_available: [0, 1, 1, 1],
|
||||
prediction_residual_p50_m: [0, 0.04, 0.05, 0.03],
|
||||
prediction_residual_p95_m: [0, 0.2, 0.3, 0.15],
|
||||
prediction_inlier_fraction: [0, 0.95, 0.92, 0.97],
|
||||
sensor_height_m: [1.3, 1.31, 1.3, 1.29],
|
||||
slope_deg: [1, 1.1, 1.2, 1],
|
||||
roughness_m: [0.04, 0.04, 0.05, 0.04],
|
||||
confidence: [0.8, 0.8, 0.75, 0.82],
|
||||
temporal_compared: [0, 1, 1, 1],
|
||||
height_delta_m: [0, 0.01, 0.01, 0.01],
|
||||
slope_delta_deg: [0, 0.1, 0.1, 0.2],
|
||||
roughness_delta_m: [0, 0, 0.01, 0.01],
|
||||
temporal_jump: [0, 0, 1, 0],
|
||||
step_candidate_point_count: [1, 2, 3, 4],
|
||||
ground_truth: false,
|
||||
access: "read-only",
|
||||
authority: {
|
||||
commands_enabled: false,
|
||||
navigation_or_safety_accepted: false,
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
@@ -169,6 +246,8 @@ before(async () => {
|
||||
({
|
||||
parseLidarLocalSurfaceCatalog,
|
||||
parseLidarLocalSurfaceFrame,
|
||||
parseLidarLocalSurfaceTimeline,
|
||||
fetchLidarLocalSurfaceTimeline,
|
||||
LidarLocalSurfaceContractError,
|
||||
} = await server.ssrLoadModule("/src/core/lidar/localSurface.ts"));
|
||||
});
|
||||
@@ -182,10 +261,21 @@ test("decodes passive local-surface evidence", () => {
|
||||
assert.equal(decoded.items[0].source.passiveProcessingOnly, true);
|
||||
assert.equal(decoded.items[0].metrics.sensorHeightM.p50, 1.3);
|
||||
assert.equal(decoded.items[0].anchors[0].sourceFrameIndex, 1350);
|
||||
assert.equal(
|
||||
decoded.items[0].metrics.temporalQualification.prediction.sampleCount,
|
||||
9,
|
||||
);
|
||||
|
||||
const decodedFrame = parseLidarLocalSurfaceFrame(frame());
|
||||
assert.equal(decodedFrame.counts.occupied, 1);
|
||||
assert.equal(decodedFrame.counts.stepCandidate, 1);
|
||||
assert.equal(decodedFrame.prediction.currentFrameExcluded, true);
|
||||
assert.equal(decodedFrame.surface.sensorHeightM, 1.3);
|
||||
|
||||
const decodedTimeline = parseLidarLocalSurfaceTimeline(timeline());
|
||||
assert.equal(decodedTimeline.frameCount, 4);
|
||||
assert.deepEqual(decodedTimeline.temporalJump, [0, 0, 1, 0]);
|
||||
assert.equal(decodedTimeline.predictionResidualP50M[2], 0.05);
|
||||
});
|
||||
|
||||
test("rejects inferred free space", () => {
|
||||
@@ -210,3 +300,21 @@ test("rejects command authority", () => {
|
||||
LidarLocalSurfaceContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("fetches the complete local-surface timeline read-only", async () => {
|
||||
const requests = [];
|
||||
const decoded = await fetchLidarLocalSurfaceTimeline(modelId, {
|
||||
fetcher: async (input, init) => {
|
||||
requests.push({ input: String(input), method: init?.method });
|
||||
return new Response(JSON.stringify(timeline()), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
},
|
||||
});
|
||||
assert.deepEqual(requests, [{
|
||||
input: `/api/v1/lidar/local-surfaces/${modelId}/timeline`,
|
||||
method: "GET",
|
||||
}]);
|
||||
assert.equal(decoded.frameCount, 4);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user