feat(perception): add ground-aware cuboid refusion

This commit is contained in:
DCCONSTRUCTIONS 2026-07-23 08:24:37 +03:00
parent b53d6d5a45
commit a0706fd5d8
8 changed files with 1116 additions and 114 deletions

View File

@ -13,7 +13,7 @@ import math
import statistics import statistics
from collections import defaultdict, deque from collections import defaultdict, deque
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass, replace
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@ -97,6 +97,9 @@ class TrackFusion:
status: str status: str
cuboid: Cuboid | None cuboid: Cuboid | None
source_indices: npt.NDArray[np.int64] source_indices: npt.NDArray[np.int64]
pre_ground_clustered_points: int = 0
ground_rejected_points: int = 0
support_ground_z_map: float | None = None
geometry: str = "none" geometry: str = "none"
observed_cuboid: Cuboid | None = None observed_cuboid: Cuboid | None = None
completion_fraction: float | None = None completion_fraction: float | None = None
@ -466,6 +469,157 @@ def _completion_profile(profile: Mapping[str, Any]) -> dict[str, Any]:
return dict(profile) return dict(profile)
def _object_support_ground_filter_profile(
profile: Mapping[str, Any],
) -> dict[str, Any]:
groups = {"person", "bicycle", "motorcycle", "vehicle"}
minimum_height = profile.get("minimum_height_above_ground_m")
maximum_height = profile.get("maximum_height_above_ground_m")
if (
profile.get("mode") != "local-ground-relative-object-support-v1"
or not isinstance(minimum_height, Mapping)
or set(minimum_height) != groups
or not isinstance(maximum_height, Mapping)
or set(maximum_height) != groups
):
raise RuntimeError("object-support ground filter profile is invalid")
scalar_checks = (
("local_radius_m", 0.5, 10.0),
("lower_percentile", 0.0, 30.0),
("maximum_below_support_m", 0.1, 3.0),
("maximum_above_support_m", 0.0, 0.5),
("fallback_below_support_m", 0.0, 1.0),
)
for name, lower, upper in scalar_checks:
try:
value = float(profile[name])
except (KeyError, TypeError, ValueError) as exc:
raise RuntimeError(f"object-support ground filter field {name} is invalid") from exc
if not lower <= value <= upper:
raise RuntimeError(f"object-support ground filter field {name} is invalid")
for group in groups:
try:
lower = float(minimum_height[group])
upper = float(maximum_height[group])
except (KeyError, TypeError, ValueError) as exc:
raise RuntimeError(
f"object-support ground filter height for {group} is invalid"
) from exc
if not 0.0 <= lower < upper <= 6.0:
raise RuntimeError(f"object-support ground filter height for {group} is invalid")
return dict(profile)
def _local_ground_z(
*,
all_points_map: FloatArray,
center_xy: FloatArray,
support_lower_z: float,
profile: Mapping[str, Any],
) -> float:
radius = float(profile["local_radius_m"])
if all_points_map.size:
distances = np.linalg.norm(all_points_map[:, :2] - center_xy, axis=1)
candidates = all_points_map[distances <= radius, 2]
else:
candidates = np.empty(0, dtype=np.float64)
estimate = (
float(np.percentile(candidates, float(profile["lower_percentile"])))
if candidates.size
else math.nan
)
if (
not math.isfinite(estimate)
or estimate < support_lower_z - float(profile["maximum_below_support_m"])
or estimate > support_lower_z + float(profile["maximum_above_support_m"])
):
estimate = support_lower_z - float(profile["fallback_below_support_m"])
return estimate
def _filter_object_support_by_ground(
indices: npt.NDArray[np.int64],
points_map: FloatArray,
*,
group: str,
profile: Mapping[str, Any],
) -> tuple[npt.NDArray[np.int64], float | None, int]:
"""Remove local ground only from one object's fitting support.
`points_map` is never modified or reduced. The returned indices are a
per-object view used by range/cuboid fitting; mapping, terrain and
traversability consumers continue to receive the complete cloud.
"""
if indices.size == 0:
return indices, None, 0
support = points_map[indices]
support_lower_z = float(np.percentile(support[:, 2], 5.0))
ground_z = _local_ground_z(
all_points_map=points_map,
center_xy=np.median(support[:, :2], axis=0),
support_lower_z=support_lower_z,
profile=profile,
)
minimum = ground_z + float(profile["minimum_height_above_ground_m"][group])
maximum = ground_z + float(profile["maximum_height_above_ground_m"][group])
keep = (support[:, 2] >= minimum) & (support[:, 2] <= maximum)
filtered = indices[keep]
return filtered, ground_z, int(indices.size - filtered.size)
def _support_overlap_fraction(
one: npt.NDArray[np.int64],
two: npt.NDArray[np.int64],
) -> float:
if one.size == 0 or two.size == 0:
return 0.0
shared = np.intersect1d(one, two, assume_unique=False).size
return float(shared / min(one.size, two.size))
def _suppress_duplicate_support_fusions(
fusions: Sequence[TrackFusion],
*,
overlap_threshold: float,
) -> tuple[TrackFusion, ...]:
accepted: list[TrackFusion] = []
suppressed: set[int] = set()
for item in sorted(fusions, key=lambda value: (-value.score, value.track_id)):
if item.cuboid is None:
continue
if any(
other.association_group == item.association_group
and _support_overlap_fraction(other.source_indices, item.source_indices)
>= overlap_threshold
for other in accepted
):
suppressed.add(item.track_id)
else:
accepted.append(item)
if not suppressed:
return tuple(fusions)
return tuple(
replace(
item,
distance_smoothed_m=None,
status="rejected-duplicate-lidar-support",
cuboid=None,
source_indices=np.empty(0, dtype=np.int64),
geometry="none",
observed_cuboid=None,
completion_fraction=None,
ground_z_map=None,
orientation_source=None,
temporal_status=None,
support_coverage_fraction=None,
)
if item.track_id in suppressed
else item
for item in fusions
)
class CuboidCompletionTracker: class CuboidCompletionTracker:
"""Complete visible LiDAR surfaces into provenance-marked, smoothed cuboids. """Complete visible LiDAR surfaces into provenance-marked, smoothed cuboids.
@ -667,24 +821,12 @@ class CuboidCompletionTracker:
center_xy: FloatArray, center_xy: FloatArray,
support_lower_z: float, support_lower_z: float,
) -> float: ) -> float:
ground = self.profile["ground"] return _local_ground_z(
radius = float(ground["local_radius_m"]) all_points_map=all_points_map,
if all_points_map.size: center_xy=center_xy,
distances = np.linalg.norm(all_points_map[:, :2] - center_xy, axis=1) support_lower_z=support_lower_z,
candidates = all_points_map[distances <= radius, 2] profile=self.profile["ground"],
else: )
candidates = np.empty(0, dtype=np.float64)
if candidates.size:
estimate = float(np.percentile(candidates, float(ground["lower_percentile"])))
else:
estimate = math.nan
if (
not math.isfinite(estimate)
or estimate < support_lower_z - float(ground["maximum_below_support_m"])
or estimate > support_lower_z + float(ground["maximum_above_support_m"])
):
estimate = support_lower_z - float(ground["fallback_below_support_m"])
return estimate
@staticmethod @staticmethod
def _amodal_axis_center(lower: float, upper: float, size: float, sensor: float) -> float: def _amodal_axis_center(lower: float, upper: float, size: float, sensor: float) -> float:
@ -732,6 +874,17 @@ def fuse_tracks(
session_seconds: float | None = None, session_seconds: float | None = None,
) -> tuple[TrackFusion, ...]: ) -> tuple[TrackFusion, ...]:
vehicle_labels = set(str(value) for value in association["vehicle_labels"]) vehicle_labels = set(str(value) for value in association["vehicle_labels"])
ground_filter = association.get("object_support_ground_filter")
if ground_filter is not None:
if not isinstance(ground_filter, Mapping):
raise RuntimeError("object-support ground filter profile is invalid")
ground_filter = _object_support_ground_filter_profile(ground_filter)
duplicate_overlap_value = association.get("support_duplicate_overlap_threshold")
duplicate_overlap_threshold = (
None if duplicate_overlap_value is None else float(duplicate_overlap_value)
)
if duplicate_overlap_threshold is not None and not 0.0 < duplicate_overlap_threshold <= 1.0:
raise RuntimeError("support duplicate overlap threshold is invalid")
accepted_tracks: list[dict[str, Any]] = [] accepted_tracks: list[dict[str, Any]] = []
for track in sorted(tracks, key=lambda value: float(value["score"]), reverse=True): for track in sorted(tracks, key=lambda value: float(value["score"]), reverse=True):
label = str(track["label"]) label = str(track["label"])
@ -767,11 +920,21 @@ def fuse_tracks(
allowed = np.asarray(association["semantic_ids"][group], dtype=np.uint8) allowed = np.asarray(association["semantic_ids"][group], dtype=np.uint8)
compatible = candidates[np.isin(sampled_semantic[candidates], allowed)] compatible = candidates[np.isin(sampled_semantic[candidates], allowed)]
clustered = _depth_cluster(compatible, depths, association) clustered = _depth_cluster(compatible, depths, association)
selected = _spatial_cluster( selected_before_ground = _spatial_cluster(
source_indices[clustered], source_indices[clustered],
points_lidar, points_lidar,
float(association["spatial_cluster_radius_m"][group]), float(association["spatial_cluster_radius_m"][group]),
) )
selected = selected_before_ground
support_ground_z = None
ground_rejected_points = 0
if ground_filter is not None:
selected, support_ground_z, ground_rejected_points = _filter_object_support_by_ground(
selected_before_ground,
points_map,
group=group,
profile=ground_filter,
)
ranges = np.linalg.norm(points_lidar[selected], axis=1) ranges = np.linalg.norm(points_lidar[selected], axis=1)
p10 = None if ranges.size == 0 else float(np.percentile(ranges, 10)) p10 = None if ranges.size == 0 else float(np.percentile(ranges, 10))
median = None if ranges.size == 0 else float(np.median(ranges)) median = None if ranges.size == 0 else float(np.median(ranges))
@ -786,6 +949,12 @@ def fuse_tracks(
support_coverage_fraction = None support_coverage_fraction = None
if compatible.size == 0: if compatible.size == 0:
status = "rejected-no-semantic-lidar-support" status = "rejected-no-semantic-lidar-support"
elif (
selected_before_ground.size >= minimum
and selected.size < minimum
and ground_rejected_points > 0
):
status = "rejected-ground-only-or-insufficient-object-support"
elif selected.size < minimum: elif selected.size < minimum:
status = f"rejected-fewer-than-{minimum}-clustered-points" status = f"rejected-fewer-than-{minimum}-clustered-points"
else: else:
@ -861,6 +1030,9 @@ def fuse_tracks(
status=status, status=status,
cuboid=cuboid, cuboid=cuboid,
source_indices=selected, source_indices=selected,
pre_ground_clustered_points=int(selected_before_ground.size),
ground_rejected_points=ground_rejected_points,
support_ground_z_map=support_ground_z,
geometry=geometry, geometry=geometry,
observed_cuboid=observed_cuboid, observed_cuboid=observed_cuboid,
completion_fraction=completion_fraction, completion_fraction=completion_fraction,
@ -870,7 +1042,11 @@ def fuse_tracks(
support_coverage_fraction=support_coverage_fraction, support_coverage_fraction=support_coverage_fraction,
) )
) )
return tuple(result) if duplicate_overlap_threshold is None:
return tuple(result)
return _suppress_duplicate_support_fusions(
result, overlap_threshold=duplicate_overlap_threshold
)
def fusion_document(item: TrackFusion) -> dict[str, Any]: def fusion_document(item: TrackFusion) -> dict[str, Any]:
@ -883,6 +1059,9 @@ def fusion_document(item: TrackFusion) -> dict[str, Any]:
"candidate_projected_points": item.candidate_points, "candidate_projected_points": item.candidate_points,
"semantic_compatible_points": item.semantic_points, "semantic_compatible_points": item.semantic_points,
"clustered_points": item.clustered_points, "clustered_points": item.clustered_points,
"pre_ground_clustered_points": item.pre_ground_clustered_points,
"ground_rejected_points": item.ground_rejected_points,
"support_ground_z_map": item.support_ground_z_map,
"distance_p10_m": item.distance_p10_m, "distance_p10_m": item.distance_p10_m,
"distance_median_m": item.distance_median_m, "distance_median_m": item.distance_median_m,
"distance_smoothed_m": item.distance_smoothed_m, "distance_smoothed_m": item.distance_smoothed_m,

View File

@ -0,0 +1,543 @@
#!/usr/bin/env python3
"""Materialize LAB E19 from immutable E14 detections, masks, and LiDAR.
The detector and semantic models are deliberately not rerun: LAB E19 changes
only calibrated LiDAR association, cuboid fitting, and presentation. The
parent result and LiDAR pack are validated first, then a new content-addressed
integrated-perception result is written without modifying either input.
"""
from __future__ import annotations
import argparse
import copy
import hashlib
import json
import os
import platform
import shutil
import tempfile
from collections import Counter
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
import numpy as np
from e10_fusion_runtime import (
CuboidCompletionTracker,
LidarReplayPack,
WorldStateProjector,
_completion_profile,
_object_support_ground_filter_profile,
canonical_json,
clearance,
distance_history,
fuse_tracks,
fusion_document,
project_points,
sha256,
)
from k1link.compute.integrated_perception import (
FUSION_SCHEMA,
IDENTITY_SCHEMA,
REPORT_SCHEMA,
RESULT_SCHEMA,
SEMANTIC_SCHEMA,
WORLD_SCHEMA,
validate_integrated_perception_result,
)
PIPELINE_ID = "fixed-e14-yolox-eomt-e19-ground-aware-refusion/v1"
def arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--job", type=Path, required=True)
parser.add_argument("--parent-result", type=Path, required=True)
parser.add_argument("--lidar-packs-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
return parser.parse_args()
def read_object(path: Path) -> dict[str, Any]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, dict):
raise RuntimeError(f"JSON root is not an object: {path}")
return value
def read_rows(path: Path) -> list[dict[str, Any]]:
rows = []
with path.open(encoding="utf-8") as stream:
for line in stream:
value = json.loads(line)
if not isinstance(value, dict):
raise RuntimeError(f"JSONL row is not an object: {path}")
rows.append(value)
return rows
def write_json(path: Path, value: object) -> None:
path.write_bytes(canonical_json(value) + b"\n")
os.chmod(path, 0o600)
def write_row(stream: Any, value: object) -> None:
stream.write(canonical_json(value).decode() + "\n")
def artifact(
path: Path,
kind: str,
media_type: str,
schema_version: str | None = None,
) -> dict[str, Any]:
value = {
"kind": kind,
"path": path.name,
"media_type": media_type,
"byte_length": path.stat().st_size,
"sha256": sha256(path),
}
if schema_version is not None:
value["schema_version"] = schema_version
return value
def read_profile(path: Path) -> tuple[dict[str, Any], str]:
resolved = path.resolve(strict=True)
profile = read_object(resolved)
association = profile.get("association")
if (
profile.get("schema_version") != "missioncore.e10-integrated-perception-profile/v1"
or profile.get("profile_id") != "lab-e19-ground-aware-cuboids-v1"
or profile.get("mode") != "full-session-qualification"
or not isinstance(association, dict)
or not isinstance(profile.get("cuboid_completion"), dict)
):
raise RuntimeError("LAB E19 profile contract is invalid")
ground_filter = association.get("object_support_ground_filter")
if not isinstance(ground_filter, dict):
raise RuntimeError("LAB E19 ground filter is missing")
_object_support_ground_filter_profile(ground_filter)
_completion_profile(profile["cuboid_completion"])
overlap = float(association.get("support_duplicate_overlap_threshold", 0.0))
if not 0.0 < overlap <= 1.0:
raise RuntimeError("LAB E19 support overlap threshold is invalid")
return profile, sha256(resolved)
def color_for(group: str, track_id: int) -> tuple[int, int, int]:
seed = hashlib.sha256(f"e10:{group}:{track_id}".encode()).digest()
return tuple(64 + value % 176 for value in seed[:3])
def materialize(args: argparse.Namespace) -> Path:
parent = validate_integrated_perception_result(
args.job,
args.parent_result,
args.lidar_packs_root,
)
if not parent.accepted:
raise RuntimeError("LAB E19 parent result is not accepted")
profile, profile_sha256 = read_profile(args.profile)
parent_result = read_object(parent.result_root / "result.json")
parent_report = read_object(parent.report_path)
parent_identity = parent_result["identity"]
parent_configuration = parent_identity["configuration"]
semantic_rows = read_rows(parent.semantic_path)
parent_fusion_rows = read_rows(parent.fusion_path)
parent_world_rows = read_rows(parent.world_path)
if not (
len(parent_fusion_rows) == len(parent_world_rows) == parent.frame_count
and profile["selection"]["required_frame_count"] == parent.frame_count
):
raise RuntimeError("LAB E19 parent selection changed")
identity = {
"schema_version": IDENTITY_SCHEMA,
"job_id": parent_identity["job_id"],
"input_sha256": parent_identity["input_sha256"],
"session_id": parent_identity["session_id"],
"source_id": parent_identity["source_id"],
"lidar_pack_id": parent_identity["lidar_pack_id"],
"selection": copy.deepcopy(parent_identity["selection"]),
"configuration": {
"pipeline": PIPELINE_ID,
"profile": profile,
"profile_sha256": profile_sha256,
"detector_profile_sha256": parent_configuration["detector_profile_sha256"],
"semantic_profile_sha256": parent_configuration["semantic_profile_sha256"],
"runner_sha256": sha256(Path(__file__).resolve(strict=True)),
"fusion_runtime_sha256": sha256(
Path(__file__).with_name("e10_fusion_runtime.py").resolve(strict=True)
),
"orchestrator_sha256": sha256(Path(__file__).resolve(strict=True)),
"container_image": parent_configuration["container_image"],
"valid_fov": copy.deepcopy(parent_configuration["valid_fov"]),
"refusion": {
"mode": "immutable-parent-detections-semantics-lidar-v1",
"parent_result_id": parent.result_id,
"parent_result_json_sha256": sha256(parent.result_root / "result.json"),
"parent_fusion_sha256": sha256(parent.fusion_path),
"parent_arrays_sha256": sha256(parent.arrays_path),
"detector_rerun": False,
"semantic_rerun": False,
"full_point_cloud_mutated": False,
},
},
"models": copy.deepcopy(parent_identity["models"]),
}
identity_sha256 = hashlib.sha256(canonical_json(identity)).hexdigest()
result_id = f"e10-integrated-perception-{identity_sha256}"
output_root = args.output_root.expanduser().absolute()
output_root.mkdir(mode=0o700, parents=True, exist_ok=True)
final = output_root / result_id
if final.exists():
validate_integrated_perception_result(args.job, final, args.lidar_packs_root)
return final
staging = Path(tempfile.mkdtemp(prefix=".e19-refusion-", dir=output_root))
os.chmod(staging, 0o700)
lidar = LidarReplayPack(parent.pack_root, expected_job_id=parent.job.job_id)
final_created = False
try:
semantic_path = staging / "semantic-frames.jsonl"
shutil.copyfile(parent.semantic_path, semantic_path)
os.chmod(semantic_path, 0o600)
fusion_path = staging / "fusion-frames.jsonl"
world_path = staging / "world-state.jsonl"
gpu_path = staging / "gpu-telemetry.jsonl"
with np.load(parent.arrays_path, allow_pickle=False) as parent_arrays:
frame_times_ns = parent_arrays["frame_times_ns"].copy()
semantic_frame_indices = parent_arrays["semantic_frame_indices"].copy()
semantic_masks = parent_arrays["semantic_masks"].copy()
if not np.array_equal(
semantic_frame_indices,
np.asarray([row["frame_index"] for row in semantic_rows], dtype=np.int64),
):
raise RuntimeError("LAB E19 semantic mask binding changed")
semantic_by_source = {
int(row["source_frame_index"]): mask
for row, mask in zip(semantic_rows, semantic_masks, strict=True)
}
history = distance_history(int(profile["association"]["distance_history_frames"]))
completion = CuboidCompletionTracker(profile["cuboid_completion"])
projector = WorldStateProjector(float(profile["world_state"]["velocity_history_limit_s"]))
support_offsets = [0]
support_points: list[np.ndarray] = []
support_colors: list[np.ndarray] = []
box_offsets = [0]
box_centers: list[tuple[float, float, float]] = []
box_half_sizes: list[tuple[float, float, float]] = []
box_quaternions: list[tuple[float, float, float, float]] = []
box_colors: list[tuple[int, int, int, int]] = []
status_counts: Counter[str] = Counter()
fusion_state_counts: Counter[str] = Counter()
accepted_cuboids = 0
old_accepted_cuboids = 0
ground_rejected_points = 0
tracks_with_ground_rejections = 0
duplicate_support_rejections = 0
fused_frames = 0
with (
fusion_path.open("x", encoding="utf-8", newline="\n") as fusion_stream,
world_path.open("x", encoding="utf-8", newline="\n") as world_stream,
):
for index, (parent_fusion, parent_world) in enumerate(
zip(parent_fusion_rows, parent_world_rows, strict=True)
):
session_seconds = float(parent_fusion["session_seconds"])
source_frame_index = int(parent_fusion["source_frame_index"])
old_accepted_cuboids += sum(
str(item.get("cuboid_status", "")).startswith("accepted-")
for item in parent_fusion["objects"]
)
lidar_frame = lidar.frame(index)
points_lidar = np.empty((0, 3), dtype=np.float64)
fusions = ()
fusion_state = str(parent_fusion["fusion_state"])
if fusion_state == "fused":
if lidar_frame is None:
raise RuntimeError(f"LAB E19 lost LiDAR frame {index}")
semantic_source = parent_fusion.get("semantic_source_frame_index")
semantic_map = semantic_by_source.get(int(semantic_source))
if semantic_map is None:
raise RuntimeError(f"LAB E19 lost semantic source {semantic_source}")
points_map, position, quaternion = lidar_frame
pixels, depths, source_indices, points_lidar = project_points(
points_map,
position,
quaternion,
lidar.profile,
)
fusions = fuse_tracks(
tracks=parent_fusion["objects"],
semantic_map=semantic_map,
pixels=pixels,
depths=depths,
source_indices=source_indices,
points_map=points_map,
points_lidar=points_lidar,
association=profile["association"],
distance_history=history,
completion_tracker=completion,
sensor_position_map=position,
session_seconds=session_seconds,
)
fused_frames += 1
fusion_state_counts[fusion_state] += 1
accepted = [item for item in fusions if item.cuboid is not None]
accepted_cuboids += len(accepted)
for item in fusions:
status_counts[item.status] += 1
ground_rejected_points += item.ground_rejected_points
tracks_with_ground_rejections += item.ground_rejected_points > 0
duplicate_support_rejections += (
item.status == "rejected-duplicate-lidar-support"
)
frame_support = []
frame_support_colors = []
for item in accepted:
if lidar_frame is None:
raise RuntimeError("LAB E19 accepted a cuboid without LiDAR")
color = color_for(item.association_group, item.track_id)
values = lidar_frame[0][item.source_indices].astype(np.float32)
frame_support.append(values)
frame_support_colors.append(
np.tile(np.asarray([color], dtype=np.uint8), (values.shape[0], 1))
)
box_centers.append(item.cuboid.center_map)
box_half_sizes.append(item.cuboid.half_size)
box_quaternions.append(item.cuboid.quaternion_xyzw)
box_colors.append((*color, 88))
if frame_support:
support = np.concatenate(frame_support)
colors = np.concatenate(frame_support_colors)
support_points.append(support)
support_colors.append(colors)
support_offsets.append(support_offsets[-1] + support.shape[0])
else:
support_offsets.append(support_offsets[-1])
box_offsets.append(box_offsets[-1] + len(accepted))
clearance_state = clearance(
points_lidar,
profile["world_state"]["clearance"],
)
world = projector.project(
frame_index=index,
source_frame_index=source_frame_index,
session_seconds=session_seconds,
fusion_state=fusion_state,
fusions=fusions,
points_lidar=points_lidar,
clearance_state=clearance_state,
delivery=parent_world["delivery"],
)
write_row(
fusion_stream,
{
"schema_version": FUSION_SCHEMA,
"frame_index": index,
"source_frame_index": source_frame_index,
"session_seconds": session_seconds,
"fusion_state": fusion_state,
"semantic_status": parent_fusion["semantic_status"],
"semantic_source_frame_index": parent_fusion.get(
"semantic_source_frame_index"
),
"objects": [fusion_document(item) for item in fusions],
},
)
write_row(world_stream, world)
fusion_stream.flush()
world_stream.flush()
os.fsync(fusion_stream.fileno())
os.fsync(world_stream.fileno())
os.chmod(fusion_path, 0o600)
os.chmod(world_path, 0o600)
arrays_path = staging / "transient-perception.npz"
np.savez_compressed(
arrays_path,
frame_times_ns=frame_times_ns,
semantic_frame_indices=semantic_frame_indices,
semantic_masks=semantic_masks,
support_offsets=np.asarray(support_offsets, dtype=np.int64),
support_points=np.concatenate(support_points)
if support_points
else np.empty((0, 3), dtype=np.float32),
support_colors=np.concatenate(support_colors)
if support_colors
else np.empty((0, 3), dtype=np.uint8),
box_offsets=np.asarray(box_offsets, dtype=np.int64),
box_centers=np.asarray(box_centers, dtype=np.float32).reshape((-1, 3)),
box_half_sizes=np.asarray(box_half_sizes, dtype=np.float32).reshape((-1, 3)),
box_quaternions=np.asarray(box_quaternions, dtype=np.float32).reshape((-1, 4)),
box_colors=np.asarray(box_colors, dtype=np.uint8).reshape((-1, 4)),
)
os.chmod(arrays_path, 0o600)
with gpu_path.open("x", encoding="utf-8", newline="\n") as gpu_stream:
write_row(
gpu_stream,
{
"schema_version": "missioncore.e19-refusion-telemetry/v1",
"gpu_used": False,
"parent_result_id": parent.result_id,
"reason": "detector and semantic inference outputs reused immutably",
},
)
gpu_stream.flush()
os.fsync(gpu_stream.fileno())
os.chmod(gpu_path, 0o600)
acceptance_profile = profile["acceptance"]
checks = {
"parent_result_accepted": parent.accepted,
"frame_accounting": len(parent_fusion_rows) == parent.frame_count,
"minimum_lidar_fused_frames": fused_frames
>= int(acceptance_profile["minimum_lidar_fused_frames"]),
"minimum_accepted_cuboids": accepted_cuboids
>= int(acceptance_profile["minimum_accepted_cuboids"]),
"ground_filter_exercised": ground_rejected_points > 0,
"duplicate_support_filter_exercised": duplicate_support_rejections > 0,
"full_point_cloud_preserved": True,
}
accepted = all(checks.values())
created_at = datetime.now(UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
parent_metrics = parent_report.get("metrics", {})
metrics = {
"source_span_seconds": parent_metrics.get("source_span_seconds"),
"detector": copy.deepcopy(parent_metrics.get("detector")),
"semantic": copy.deepcopy(parent_metrics.get("semantic")),
"fusion": {
"fused_frames": fused_frames,
"fusion_state_counts": dict(fusion_state_counts),
"accepted_cuboids": accepted_cuboids,
"parent_accepted_cuboids": old_accepted_cuboids,
"accepted_change_fraction": (
accepted_cuboids / old_accepted_cuboids - 1.0 if old_accepted_cuboids else None
),
"rejection_counts": dict(status_counts),
"ground_points_excluded_from_box_fitting": ground_rejected_points,
"track_observations_with_ground_exclusions": tracks_with_ground_rejections,
"duplicate_support_rejections": duplicate_support_rejections,
},
"refusion": {
"gpu_used": False,
"detector_rerun": False,
"semantic_rerun": False,
"parent_result_id": parent.result_id,
},
}
report = {
"schema_version": REPORT_SCHEMA,
"result_id": result_id,
"created_at_utc": created_at,
"state": "accepted" if accepted else "rejected",
"ground_truth": False,
"identity": identity,
"runtime": {
"hostname": platform.node(),
"python": platform.python_version(),
"numpy": np.__version__,
"execution": "cpu-only-refusion-of-validated-parent",
},
"metrics": metrics,
"acceptance": {
"accepted": accepted,
"checks": checks,
"navigation_or_safety_accepted": False,
},
"limitations": [
"Detector and semantic inference are inherited from the validated E14 parent.",
"This is a recorded refusion result, not a physical live K1 gate.",
"The complete point cloud is an immutable input and is not modified.",
"Completed cuboids remain class-prior diagnostic geometry, not 3D ground truth.",
"Navigation and safety authority remain disabled.",
],
}
report_path = staging / "run-report.json"
write_json(report_path, report)
artifacts = [
artifact(
semantic_path,
"e10-semantic-frames",
"application/x-ndjson",
SEMANTIC_SCHEMA,
),
artifact(
fusion_path,
"e10-fusion-frames",
"application/x-ndjson",
FUSION_SCHEMA,
),
artifact(
world_path,
"e10-world-state",
"application/x-ndjson",
WORLD_SCHEMA,
),
artifact(arrays_path, "e10-transient-perception", "application/x-npz"),
artifact(gpu_path, "worker-gpu-telemetry", "application/x-ndjson"),
artifact(
report_path,
"e10-run-report",
"application/json",
REPORT_SCHEMA,
),
]
result = {
"schema_version": RESULT_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": created_at,
"acceptance_state": report["state"],
"ground_truth": False,
"publication_scope": "recorded-integrated-realtime-qualification-only",
"frames_processed": parent.frame_count,
"artifacts": artifacts,
}
write_json(staging / "result.json", result)
os.rename(staging, final)
final_created = True
validate_integrated_perception_result(args.job, final, args.lidar_packs_root)
return final
except BaseException:
if staging.exists():
shutil.rmtree(staging)
if final_created and final.exists():
shutil.rmtree(final)
raise
finally:
lidar.close()
def main() -> int:
result = materialize(arguments())
report = read_object(result / "run-report.json")
print(
json.dumps(
{
"result_root": str(result),
"result_id": result.name,
"accepted": report["acceptance"]["accepted"],
"fusion": report["metrics"]["fusion"],
},
ensure_ascii=False,
sort_keys=True,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,172 @@
{
"schema_version": "missioncore.e10-integrated-perception-profile/v1",
"profile_id": "lab-e19-ground-aware-cuboids-v1",
"mode": "full-session-qualification",
"source": {
"source_id": "sensor.camera.right",
"resolution": [800, 600],
"calibration_slot": "camera_1",
"calibration_sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9"
},
"selection": {
"required_frame_count": 4489,
"required_source_start_frame_index": 0,
"required_source_end_frame_index": 4488,
"minimum_source_span_seconds": 448.0
},
"replay": {
"speed": 1.0,
"detector_queue_capacity": 2,
"semantic_queue_capacity": 1,
"semantic_sample_every_frames": 5,
"semantic_ttl_ms": 750.0
},
"association": {
"box_inset": {
"bottom_fraction": 0.02,
"horizontal_fraction": 0.05,
"top_fraction": 0.04
},
"depth_cluster_gap_fraction": 0.06,
"depth_cluster_minimum_gap_m": 0.5,
"group_nms_iou_threshold": 0.5,
"support_duplicate_overlap_threshold": 0.6,
"object_support_ground_filter": {
"mode": "local-ground-relative-object-support-v1",
"local_radius_m": 2.5,
"lower_percentile": 8.0,
"maximum_below_support_m": 1.2,
"maximum_above_support_m": 0.15,
"fallback_below_support_m": 0.25,
"minimum_height_above_ground_m": {
"person": 0.08,
"bicycle": 0.08,
"motorcycle": 0.08,
"vehicle": 0.12
},
"maximum_height_above_ground_m": {
"person": 2.5,
"bicycle": 2.5,
"motorcycle": 2.5,
"vehicle": 4.5
}
},
"maximum_cuboid_span_m": 15.0,
"maximum_distance_innovation_fraction": 0.25,
"maximum_distance_innovation_m": 1.5,
"maximum_oriented_extent_m": {
"bicycle": [3.5, 2.0, 2.5],
"motorcycle": [3.5, 2.0, 2.5],
"person": [1.5, 1.5, 2.8],
"vehicle": [12.5, 4.0, 4.5]
},
"minimum_cuboid_extent_m": 0.15,
"minimum_support_points": {
"bicycle": 4,
"motorcycle": 4,
"person": 4,
"vehicle": 8
},
"semantic_ids": {
"bicycle": [2],
"motorcycle": [3],
"person": [1],
"vehicle": [4, 5]
},
"spatial_cluster_radius_m": {
"bicycle": 0.9,
"motorcycle": 0.9,
"person": 0.9,
"vehicle": 1.5
},
"vehicle_labels": ["car", "truck", "bus"],
"distance_history_frames": 5
},
"cuboid_completion": {
"mode": "class-prior-amodal-v1",
"failure_policy": "reject",
"classes": {
"person": {
"nominal_size_m": [0.55, 0.55, 1.72],
"minimum_size_m": [0.35, 0.35, 1.3],
"maximum_size_m": [1.2, 1.2, 2.3],
"support_padding_m": [0.12, 0.12, 0.12]
},
"bicycle": {
"nominal_size_m": [1.8, 0.65, 1.5],
"minimum_size_m": [1.2, 0.4, 1.0],
"maximum_size_m": [2.5, 1.2, 2.2],
"support_padding_m": [0.18, 0.12, 0.12]
},
"motorcycle": {
"nominal_size_m": [2.1, 0.8, 1.45],
"minimum_size_m": [1.4, 0.5, 1.0],
"maximum_size_m": [3.0, 1.4, 2.2],
"support_padding_m": [0.2, 0.14, 0.14]
},
"car": {
"nominal_size_m": [4.5, 1.85, 1.55],
"minimum_size_m": [3.2, 1.45, 1.2],
"maximum_size_m": [5.8, 2.4, 2.3],
"support_padding_m": [0.25, 0.18, 0.15]
},
"truck": {
"nominal_size_m": [7.0, 2.5, 3.0],
"minimum_size_m": [4.8, 1.8, 1.8],
"maximum_size_m": [12.5, 3.2, 4.2],
"support_padding_m": [0.35, 0.22, 0.2]
},
"bus": {
"nominal_size_m": [10.5, 2.55, 3.2],
"minimum_size_m": [7.0, 2.1, 2.5],
"maximum_size_m": [13.5, 3.2, 4.2],
"support_padding_m": [0.4, 0.24, 0.2]
}
},
"ground": {
"local_radius_m": 2.5,
"lower_percentile": 8.0,
"maximum_below_support_m": 1.2,
"maximum_above_support_m": 0.15,
"fallback_below_support_m": 0.25
},
"orientation": {
"minimum_anisotropy_ratio": 1.35,
"face_width_switch_fraction": 1.05
},
"temporal": {
"center_alpha": 0.4,
"size_alpha": 0.2,
"yaw_alpha": 0.25,
"maximum_center_innovation_m": 2.0,
"maximum_yaw_innovation_degrees": 55.0,
"maximum_idle_s": 1.0,
"confirmation_hits": 3
},
"minimum_support_coverage_fraction": 0.75
},
"world_state": {
"velocity_history_limit_s": 1.0,
"clearance": {
"sector_count": 72,
"minimum_range_m": 0.5,
"maximum_range_m": 30.0,
"ground_percentile": 5.0,
"minimum_height_above_ground_m": 0.2,
"maximum_height_above_ground_m": 3.0,
"front_half_angle_degrees": 15.0
}
},
"acceptance": {
"detector_minimum_effective_fps": 9.5,
"detector_maximum_drop_fraction": 0.005,
"maximum_p95_world_state_age_ms": 175.0,
"semantic_minimum_effective_fps": 1.8,
"semantic_maximum_drop_fraction": 0.05,
"semantic_maximum_p95_completion_age_ms": 400.0,
"minimum_fresh_semantic_coverage": 0.9,
"minimum_lidar_fused_frames": 3500,
"minimum_accepted_cuboids": 1500,
"require_zero_failures": true
}
}

View File

@ -26,6 +26,7 @@ from e10_fusion_runtime import (
LidarReplayPack, LidarReplayPack,
WorldStateProjector, WorldStateProjector,
_completion_profile, _completion_profile,
_object_support_ground_filter_profile,
canonical_json, canonical_json,
clearance, clearance,
distance_history, distance_history,
@ -160,6 +161,14 @@ def read_profile(path: Path) -> tuple[dict[str, Any], str]:
if not isinstance(completion, dict): if not isinstance(completion, dict):
raise RuntimeError("LAB E13 cuboid completion contract is invalid") raise RuntimeError("LAB E13 cuboid completion contract is invalid")
_completion_profile(completion) _completion_profile(completion)
ground_filter = association.get("object_support_ground_filter")
if ground_filter is not None:
if not isinstance(ground_filter, dict):
raise RuntimeError("object-support ground filter contract is invalid")
_object_support_ground_filter_profile(ground_filter)
duplicate_overlap = association.get("support_duplicate_overlap_threshold")
if duplicate_overlap is not None and not 0.0 < float(duplicate_overlap) <= 1.0:
raise RuntimeError("support duplicate overlap contract is invalid")
return profile, sha256(resolved) return profile, sha256(resolved)

View File

@ -8,7 +8,7 @@ from collections.abc import Callable
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Literal, TypedDict from typing import Any, Literal, TypedDict
from uuid import UUID, uuid4 from uuid import UUID, uuid4
import numpy as np import numpy as np
@ -534,6 +534,19 @@ def _recorded_blueprint(
), ),
).visualizer() ).visualizer()
point_visualizer.id = RECORDED_POINTS_VISUALIZER_ID point_visualizer.id = RECORDED_POINTS_VISUALIZER_ID
accumulated_time_ranges = (
rrb.VisibleTimeRanges(time_ranges) if time_ranges is not None else None
)
point_overrides: list[Any] = [
rrb.EntityBehavior(visible=settings.show_points),
point_visualizer,
]
trajectory_overrides: list[Any] = [
rrb.EntityBehavior(visible=settings.show_trajectory),
]
if accumulated_time_ranges is not None:
point_overrides.append(accumulated_time_ranges)
trajectory_overrides.append(accumulated_time_ranges)
spatial_view = rrb.Spatial3DView( spatial_view = rrb.Spatial3DView(
origin="/world", origin="/world",
name="Пространственная сцена", name="Пространственная сцена",
@ -548,21 +561,17 @@ def _recorded_blueprint(
# therefore hide/reveal already-recorded entities without # therefore hide/reveal already-recorded entities without
# rewriting the data RRD. Keep the point visualizer alongside it # rewriting the data RRD. Keep the point visualizer alongside it
# so size/color overrides remain active for the same entity. # so size/color overrides remain active for the same entity.
"/world/points": [ "/world/points": point_overrides,
rrb.EntityBehavior(visible=settings.show_points), "/world/trajectory": trajectory_overrides,
point_visualizer,
],
"/world/trajectory": rrb.EntityBehavior(
visible=settings.show_trajectory,
),
# Dynamic perception must not inherit the mapping view's # Dynamic perception must not inherit the mapping view's
# historical accumulation window. It is rendered latest-at in # historical accumulation window. It is rendered latest-at in
# the dedicated perception view below. # the dedicated perception view below.
"/world/perception": rrb.EntityBehavior(visible=False), "/world/perception": rrb.EntityBehavior(visible=False),
}, },
# A positive window accumulates historical frames. With no # Clear any prior view-level accumulation and scope the operator's
# window, latest-at deliberately keeps one current LiDAR frame. # history window to mapping entities above. Dynamic perception then
time_ranges=time_ranges, # remains latest-at even when both layers share this world view.
time_ranges=rrb.VisibleTimeRanges([]),
) )
spatial_view.id = RECORDED_SPATIAL_VIEW_ID spatial_view.id = RECORDED_SPATIAL_VIEW_ID
camera_view = rrb.Spatial2DView( camera_view = rrb.Spatial2DView(
@ -599,9 +608,7 @@ def _recorded_blueprint(
name="Маршрут и время", name="Маршрут и время",
) )
metrics_view.id = RECORDED_METRICS_VIEW_ID metrics_view.id = RECORDED_METRICS_VIEW_ID
active_tab = {"spatial": 0, "perception": 1, "perception3d": 2, "metrics": 3}[ active_tab = {"spatial": 0, "perception": 1, "perception3d": 2, "metrics": 3}[active_view]
active_view
]
root_container = rrb.Tabs( root_container = rrb.Tabs(
spatial_view, spatial_view,
camera_view, camera_view,

View File

@ -21,15 +21,11 @@ RECORDED_SPATIAL_RESET_ROOT_CONTAINER_ID = UUID("27d95ad7-1e72-46ca-b854-d02cd99
RECORDED_PERCEPTION_ROOT_CONTAINER_ID = UUID("70ea9fd5-bbbb-4b23-8ae8-8098af92b997") RECORDED_PERCEPTION_ROOT_CONTAINER_ID = UUID("70ea9fd5-bbbb-4b23-8ae8-8098af92b997")
RECORDED_PERCEPTION_RESET_ROOT_CONTAINER_ID = UUID("d2196c6e-99da-4402-857c-4daea0dd3ae0") RECORDED_PERCEPTION_RESET_ROOT_CONTAINER_ID = UUID("d2196c6e-99da-4402-857c-4daea0dd3ae0")
RECORDED_PERCEPTION_3D_ROOT_CONTAINER_ID = UUID("81051015-9808-413b-90a4-1cfaacacbc4f") RECORDED_PERCEPTION_3D_ROOT_CONTAINER_ID = UUID("81051015-9808-413b-90a4-1cfaacacbc4f")
RECORDED_PERCEPTION_3D_RESET_ROOT_CONTAINER_ID = UUID( RECORDED_PERCEPTION_3D_RESET_ROOT_CONTAINER_ID = UUID("3de10822-0274-4a58-8d45-f35d86625303")
"3de10822-0274-4a58-8d45-f35d86625303"
)
RECORDED_METRICS_ROOT_CONTAINER_ID = UUID("374710a2-1d77-4349-bea4-8719e7aa1f09") RECORDED_METRICS_ROOT_CONTAINER_ID = UUID("374710a2-1d77-4349-bea4-8719e7aa1f09")
RECORDED_METRICS_RESET_ROOT_CONTAINER_ID = UUID("1e8ac565-bbd6-4554-9213-dad564e534e3") RECORDED_METRICS_RESET_ROOT_CONTAINER_ID = UUID("1e8ac565-bbd6-4554-9213-dad564e534e3")
RECORDED_POINTS_VISUALIZER_ID = UUID("ca037ec0-8761-4417-86ee-846fa2875303") RECORDED_POINTS_VISUALIZER_ID = UUID("ca037ec0-8761-4417-86ee-846fa2875303")
RECORDED_PERCEPTION_POINTS_VISUALIZER_ID = UUID( RECORDED_PERCEPTION_POINTS_VISUALIZER_ID = UUID("ab8db306-6981-49c5-b417-94fe2f01dbf0")
"ab8db306-6981-49c5-b417-94fe2f01dbf0"
)
RECORDED_CAMERA_VIEW_ID = UUID("5c1db75b-07cd-479a-903d-f4f2ed554513") RECORDED_CAMERA_VIEW_ID = UUID("5c1db75b-07cd-479a-903d-f4f2ed554513")
RECORDED_CAMERA_RESET_VIEW_ID = UUID("d0618e0c-c889-4222-a643-60a4a882935c") RECORDED_CAMERA_RESET_VIEW_ID = UUID("d0618e0c-c889-4222-a643-60a4a882935c")
RECORDED_PERCEPTION_3D_VIEW_ID = UUID("0496bd2e-2b4d-4a4f-87b8-3ce4f9f7e114") RECORDED_PERCEPTION_3D_VIEW_ID = UUID("0496bd2e-2b4d-4a4f-87b8-3ce4f9f7e114")
@ -65,25 +61,33 @@ def recorded_blueprint(
end=rr.TimeRangeBoundary.cursor_relative(), end=rr.TimeRangeBoundary.cursor_relative(),
) )
] ]
latest_time_ranges = rrb.VisibleTimeRanges([]) accumulated_time_ranges = (
rrb.VisibleTimeRanges(time_ranges) if time_ranges is not None else None
)
point_visualizer = rr.Points3D.from_fields( point_visualizer = rr.Points3D.from_fields(
radii=rr.Radius.ui_points(settings.point_size), radii=rr.Radius.ui_points(settings.point_size),
colors=( colors=(
[_parse_hex_color(settings.custom_color)] [_parse_hex_color(settings.custom_color)] if settings.palette == "custom" else None
if settings.palette == "custom"
else None
), ),
).visualizer() ).visualizer()
point_visualizer.id = RECORDED_POINTS_VISUALIZER_ID point_visualizer.id = RECORDED_POINTS_VISUALIZER_ID
perception_point_visualizer = rr.Points3D.from_fields( perception_point_visualizer = rr.Points3D.from_fields(
radii=rr.Radius.ui_points(settings.point_size), radii=rr.Radius.ui_points(settings.point_size),
colors=( colors=(
[_parse_hex_color(settings.custom_color)] [_parse_hex_color(settings.custom_color)] if settings.palette == "custom" else None
if settings.palette == "custom"
else None
), ),
).visualizer() ).visualizer()
perception_point_visualizer.id = RECORDED_PERCEPTION_POINTS_VISUALIZER_ID perception_point_visualizer.id = RECORDED_PERCEPTION_POINTS_VISUALIZER_ID
point_overrides: list[Any] = [
rrb.EntityBehavior(visible=settings.show_points),
point_visualizer,
]
trajectory_overrides: list[Any] = [
rrb.EntityBehavior(visible=settings.show_trajectory),
]
if accumulated_time_ranges is not None:
point_overrides.append(accumulated_time_ranges)
trajectory_overrides.append(accumulated_time_ranges)
spatial_view = rrb.Spatial3DView( spatial_view = rrb.Spatial3DView(
origin="/world", origin="/world",
name="Мир · LiDAR и объекты" if unified_perception else "Пространственная сцена", name="Мир · LiDAR и объекты" if unified_perception else "Пространственная сцена",
@ -94,24 +98,19 @@ def recorded_blueprint(
stroke_width=0.75, stroke_width=0.75,
), ),
overrides={ overrides={
"/world/points": [ # Accumulation belongs to mapping data only. Dynamic perception
rrb.EntityBehavior(visible=settings.show_points), # inherits the view's latest-at query and can never turn into an
point_visualizer, # object-history trail when the operator widens the cloud window.
], "/world/points": point_overrides,
"/world/trajectory": rrb.EntityBehavior( "/world/trajectory": trajectory_overrides,
visible=settings.show_trajectory,
),
# Perception is hidden in the mapping-only presentation. Unified
# perception below adds explicit per-entity latest-at overrides,
# allowing the native cloud to retain this view's accumulation.
"/world/perception": rrb.EntityBehavior(visible=False), "/world/perception": rrb.EntityBehavior(visible=False),
}, },
time_ranges=time_ranges, # Log an explicit empty range set so a previous accumulated blueprint
# for this stable view id is cleared instead of surviving in Rerun.
time_ranges=rrb.VisibleTimeRanges([]),
) )
spatial_view.id = ( spatial_view.id = (
RECORDED_SPATIAL_RESET_VIEW_ID RECORDED_SPATIAL_RESET_VIEW_ID if view_reset_generation else RECORDED_SPATIAL_VIEW_ID
if view_reset_generation
else RECORDED_SPATIAL_VIEW_ID
) )
camera_view = rrb.Spatial2DView( camera_view = rrb.Spatial2DView(
origin="/perception/camera", origin="/perception/camera",
@ -128,9 +127,7 @@ def recorded_blueprint(
}, },
) )
camera_view.id = ( camera_view.id = (
RECORDED_CAMERA_RESET_VIEW_ID RECORDED_CAMERA_RESET_VIEW_ID if view_reset_generation else RECORDED_CAMERA_VIEW_ID
if view_reset_generation
else RECORDED_CAMERA_VIEW_ID
) )
perception_3d_view = rrb.Spatial3DView( perception_3d_view = rrb.Spatial3DView(
# Cuboids are expressed in the same calibrated world frame as the # Cuboids are expressed in the same calibrated world frame as the
@ -181,31 +178,18 @@ def recorded_blueprint(
# semantic points and cuboids independently. # semantic points and cuboids independently.
spatial_view.visualizer_overrides["/world/perception"] = [ spatial_view.visualizer_overrides["/world/perception"] = [
rrb.EntityBehavior(visible=True), rrb.EntityBehavior(visible=True),
latest_time_ranges,
] ]
spatial_view.visualizer_overrides[ spatial_view.visualizer_overrides["/world/perception/lidar"] = [
"/world/perception/lidar"
] = [
rrb.EntityBehavior(visible=False), rrb.EntityBehavior(visible=False),
latest_time_ranges,
] ]
spatial_view.visualizer_overrides[ spatial_view.visualizer_overrides["/world/perception/support"] = [
"/world/perception/support"
] = [
rrb.EntityBehavior(visible=False), rrb.EntityBehavior(visible=False),
latest_time_ranges,
] ]
spatial_view.visualizer_overrides[ spatial_view.visualizer_overrides["/world/perception/semantic_points"] = [
"/world/perception/semantic_points"
] = [
rrb.EntityBehavior(visible=show_segmentation), rrb.EntityBehavior(visible=show_segmentation),
latest_time_ranges,
] ]
spatial_view.visualizer_overrides[ spatial_view.visualizer_overrides["/world/perception/boxes3d"] = [
"/world/perception/boxes3d"
] = [
rrb.EntityBehavior(visible=show_cuboids_3d), rrb.EntityBehavior(visible=show_cuboids_3d),
latest_time_ranges,
] ]
root_container = rrb.Horizontal( root_container = rrb.Horizontal(
camera_view, camera_view,
@ -222,9 +206,7 @@ def recorded_blueprint(
# Keep operator video and 3D cuboids as direct root views. Rerun's nested # Keep operator video and 3D cuboids as direct root views. Rerun's nested
# Tabs preserve their own active child and cannot be switched reliably by # Tabs preserve their own active child and cannot be switched reliably by
# a live blueprint channel after the operator has visited another child. # a live blueprint channel after the operator has visited another child.
active_tab = {"spatial": 0, "perception": 1, "perception3d": 2, "metrics": 3}[ active_tab = {"spatial": 0, "perception": 1, "perception3d": 2, "metrics": 3}[active_view]
active_view
]
root_container = rrb.Tabs( root_container = rrb.Tabs(
spatial_view, spatial_view,
camera_view, camera_view,

View File

@ -120,6 +120,22 @@ def test_e13_profile_pins_provenance_marked_amodal_completion() -> None:
assert profile["cuboid_completion"]["temporal"]["confirmation_hits"] == 3 assert profile["cuboid_completion"]["temporal"]["confirmation_hits"] == 3
def test_e19_profile_adds_ground_aware_support_without_mutating_e14() -> None:
_fusion, runner = _worker_modules()
root = Path(__file__).resolve().parents[1] / "experiments" / "perception" / "worker"
e14, _e14_digest = runner.read_profile(root / "e14_full_session_amodal_profile.json")
e19, _e19_digest = runner.read_profile(root / "e19_ground_aware_cuboid_profile.json")
assert "object_support_ground_filter" not in e14["association"]
assert "support_duplicate_overlap_threshold" not in e14["association"]
assert e19["profile_id"] == "lab-e19-ground-aware-cuboids-v1"
assert (
e19["association"]["object_support_ground_filter"]["mode"]
== "local-ground-relative-object-support-v1"
)
assert e19["association"]["support_duplicate_overlap_threshold"] == 0.6
def test_e14_profile_combines_full_session_and_amodal_gates() -> None: def test_e14_profile_combines_full_session_and_amodal_gates() -> None:
_fusion, runner = _worker_modules() _fusion, runner = _worker_modules()
profile_path = ( profile_path = (
@ -278,6 +294,84 @@ def test_e13_temporal_filter_reduces_cuboid_center_jitter() -> None:
assert outputs[-1].temporal_status == "confirmed" assert outputs[-1].temporal_status == "confirmed"
def test_e19_ground_filter_preserves_cloud_and_excludes_ground_from_box_support() -> None:
fusion, runner = _worker_modules()
profile, _digest = runner.read_profile(
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e19_ground_aware_cuboid_profile.json"
)
ground = np.asarray(
[[x, y, 0.0] for x in (9.5, 10.0, 10.5) for y in (-0.6, 0.0, 0.6)],
dtype=np.float64,
)
vehicle = np.asarray(
[[x, y, z] for x in (9.8, 10.2) for y in (-0.4, 0.4) for z in (0.3, 0.9, 1.4)],
dtype=np.float64,
)
cloud = np.concatenate((ground, vehicle))
unchanged = cloud.copy()
indices = np.arange(cloud.shape[0], dtype=np.int64)
filtered, ground_z, rejected = fusion._filter_object_support_by_ground(
indices,
cloud,
group="vehicle",
profile=profile["association"]["object_support_ground_filter"],
)
assert ground_z == 0.0
assert rejected == ground.shape[0]
assert filtered.tolist() == list(range(ground.shape[0], cloud.shape[0]))
np.testing.assert_array_equal(cloud, unchanged)
def test_e19_duplicate_tracks_cannot_publish_the_same_lidar_support_twice() -> None:
fusion, _runner = _worker_modules()
cuboid = fusion.Cuboid(
center_map=(10.0, 0.0, 0.8),
half_size=(2.25, 0.925, 0.775),
quaternion_xyzw=(0.0, 0.0, 0.0, 1.0),
)
def item(track_id: int, score: float, indices: list[int]) -> object:
source = np.asarray(indices, dtype=np.int64)
return fusion.TrackFusion(
track_id=track_id,
label="car",
association_group="vehicle",
score=score,
bbox_xyxy=(100.0, 100.0, 200.0, 200.0),
candidate_points=source.size,
semantic_points=source.size,
clustered_points=source.size,
distance_p10_m=9.5,
distance_median_m=10.0,
distance_smoothed_m=10.0,
status="accepted-class-prior-amodal-v1",
cuboid=cuboid,
source_indices=source,
)
result = fusion._suppress_duplicate_support_fusions(
[
item(10, 0.91, [1, 2, 3, 4, 5]),
item(11, 0.72, [1, 2, 3, 4]),
item(12, 0.80, [20, 21, 22, 23]),
],
overlap_threshold=0.6,
)
by_track = {value.track_id: value for value in result}
assert by_track[10].cuboid is not None
assert by_track[11].cuboid is None
assert by_track[11].status == "rejected-duplicate-lidar-support"
assert by_track[11].source_indices.size == 0
assert by_track[12].cuboid is not None
def test_e10_semantic_binding_is_explicit_about_freshness() -> None: def test_e10_semantic_binding_is_explicit_about_freshness() -> None:
_fusion, runner = _worker_modules() _fusion, runner = _worker_modules()
assert runner.semantic_binding(None, 2.0, 750.0) == ("unavailable", None) assert runner.semantic_binding(None, 2.0, 750.0) == ("unavailable", None)

View File

@ -237,7 +237,15 @@ def test_recorded_blueprint_accumulates_point_frames_on_session_timeline() -> No
visible_ranges = spatial_view.properties["VisibleTimeRanges"] visible_ranges = spatial_view.properties["VisibleTimeRanges"]
line_grid = spatial_view.properties["LineGrid3D"] line_grid = spatial_view.properties["LineGrid3D"]
assert visible_ranges.ranges.as_arrow_array().to_pylist() == [ assert visible_ranges.ranges.as_arrow_array().to_pylist() == []
assert line_grid.visible.as_arrow_array().to_pylist() == [False]
point_behavior, point_visualizer, point_time_ranges = spatial_view.visualizer_overrides[
"/world/points"
]
trajectory_behavior, trajectory_time_ranges = spatial_view.visualizer_overrides[
"/world/trajectory"
]
expected_accumulation = [
{ {
"timeline": SESSION_TIMELINE, "timeline": SESSION_TIMELINE,
"range": { "range": {
@ -246,9 +254,8 @@ def test_recorded_blueprint_accumulates_point_frames_on_session_timeline() -> No
}, },
} }
] ]
assert line_grid.visible.as_arrow_array().to_pylist() == [False] assert point_time_ranges.ranges.as_arrow_array().to_pylist() == expected_accumulation
point_behavior, point_visualizer = spatial_view.visualizer_overrides["/world/points"] assert trajectory_time_ranges.ranges.as_arrow_array().to_pylist() == expected_accumulation
trajectory_behavior = spatial_view.visualizer_overrides["/world/trajectory"]
perception_behavior = spatial_view.visualizer_overrides["/world/perception"] perception_behavior = spatial_view.visualizer_overrides["/world/perception"]
assert point_behavior.visible.as_arrow_array().to_pylist() == [False] assert point_behavior.visible.as_arrow_array().to_pylist() == [False]
assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True] assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True]
@ -277,9 +284,9 @@ def test_zero_accumulation_uses_latest_frame_instead_of_empty_time_range() -> No
blueprint = _recorded_blueprint(RerunSceneSettings(accumulation_seconds=0.0, show_grid=True)) blueprint = _recorded_blueprint(RerunSceneSettings(accumulation_seconds=0.0, show_grid=True))
spatial_view = blueprint.root_container.contents[0] spatial_view = blueprint.root_container.contents[0]
assert "VisibleTimeRanges" not in spatial_view.properties assert spatial_view.properties["VisibleTimeRanges"].ranges.as_arrow_array().to_pylist() == []
point_behavior, point_visualizer = spatial_view.visualizer_overrides["/world/points"] point_behavior, point_visualizer = spatial_view.visualizer_overrides["/world/points"]
trajectory_behavior = spatial_view.visualizer_overrides["/world/trajectory"] (trajectory_behavior,) = spatial_view.visualizer_overrides["/world/trajectory"]
assert point_behavior.visible.as_arrow_array().to_pylist() == [True] assert point_behavior.visible.as_arrow_array().to_pylist() == [True]
assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True] assert trajectory_behavior.visible.as_arrow_array().to_pylist() == [True]
point_components = {str(batch.component_descriptor()) for batch in point_visualizer.overrides} point_components = {str(batch.component_descriptor()) for batch in point_visualizer.overrides}
@ -309,18 +316,32 @@ def test_dynamic_blueprint_reuses_scene_ids_without_playback_mutation() -> None:
assert first.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID assert first.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID
assert second.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID assert second.root_container.contents[3].id == RECORDED_METRICS_VIEW_ID
first_point_behavior, first_point_visualizer = first_view.visualizer_overrides["/world/points"] first_point_behavior, first_point_visualizer, first_point_time_ranges = (
second_point_behavior, second_point_visualizer = second_view.visualizer_overrides[ first_view.visualizer_overrides["/world/points"]
"/world/points" )
second_point_behavior, second_point_visualizer, second_point_time_ranges = (
second_view.visualizer_overrides["/world/points"]
)
first_trajectory, first_trajectory_time_ranges = first_view.visualizer_overrides[
"/world/trajectory"
]
second_trajectory, second_trajectory_time_ranges = second_view.visualizer_overrides[
"/world/trajectory"
] ]
first_trajectory = first_view.visualizer_overrides["/world/trajectory"]
second_trajectory = second_view.visualizer_overrides["/world/trajectory"]
assert first_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID assert first_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID
assert second_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID assert second_point_visualizer.id == RECORDED_POINTS_VISUALIZER_ID
assert first_point_behavior.visible.as_arrow_array().to_pylist() == [False] assert first_point_behavior.visible.as_arrow_array().to_pylist() == [False]
assert second_point_behavior.visible.as_arrow_array().to_pylist() == [True] assert second_point_behavior.visible.as_arrow_array().to_pylist() == [True]
assert first_trajectory.visible.as_arrow_array().to_pylist() == [True] assert first_trajectory.visible.as_arrow_array().to_pylist() == [True]
assert second_trajectory.visible.as_arrow_array().to_pylist() == [False] assert second_trajectory.visible.as_arrow_array().to_pylist() == [False]
assert (
first_point_time_ranges.ranges.as_arrow_array().to_pylist()
== first_trajectory_time_ranges.ranges.as_arrow_array().to_pylist()
)
assert (
second_point_time_ranges.ranges.as_arrow_array().to_pylist()
== second_trajectory_time_ranges.ranges.as_arrow_array().to_pylist()
)
payload = recorded_blueprint_rrd( payload = recorded_blueprint_rrd(
RerunSceneSettings(show_points=False, show_trajectory=False), RerunSceneSettings(show_points=False, show_trajectory=False),
@ -373,17 +394,14 @@ def test_viewer_blueprint_reset_is_bounded_and_recreates_render_views() -> None:
cuboid_view = cuboids.root_container.contents[2] cuboid_view = cuboids.root_container.contents[2]
assert cuboid_view.origin == "/world" assert cuboid_view.origin == "/world"
assert "VisibleTimeRanges" not in cuboid_view.properties assert "VisibleTimeRanges" not in cuboid_view.properties
cuboid_points, cuboid_point_visualizer = cuboid_view.visualizer_overrides[ cuboid_points, cuboid_point_visualizer = cuboid_view.visualizer_overrides["/world/points"]
"/world/points"
]
cuboid_perception = cuboid_view.visualizer_overrides["/world/perception"] cuboid_perception = cuboid_view.visualizer_overrides["/world/perception"]
cuboid_diagnostic_lidar = cuboid_view.visualizer_overrides[ cuboid_diagnostic_lidar = cuboid_view.visualizer_overrides["/world/perception/lidar"]
"/world/perception/lidar"
]
assert cuboid_points.visible.as_arrow_array().to_pylist() == [True] assert cuboid_points.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_point_visualizer.id != initial.root_container.contents[0].visualizer_overrides[ assert (
"/world/points" cuboid_point_visualizer.id
][1].id != initial.root_container.contents[0].visualizer_overrides["/world/points"][1].id
)
assert cuboid_perception.visible.as_arrow_array().to_pylist() == [True] assert cuboid_perception.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_diagnostic_lidar.visible.as_arrow_array().to_pylist() == [False] assert cuboid_diagnostic_lidar.visible.as_arrow_array().to_pylist() == [False]
perception_behavior = initial.root_container.contents[0].visualizer_overrides[ perception_behavior = initial.root_container.contents[0].visualizer_overrides[
@ -415,18 +433,16 @@ def test_viewer_blueprint_unifies_original_video_and_independent_ai_layers() ->
assert camera_view.visualizer_overrides[ assert camera_view.visualizer_overrides[
"/perception/camera/segmentation" "/perception/camera/segmentation"
].visible.as_arrow_array().to_pylist() == [True] ].visible.as_arrow_array().to_pylist() == [True]
semantic_behavior, semantic_time_ranges = spatial_view.visualizer_overrides[ (semantic_behavior,) = spatial_view.visualizer_overrides["/world/perception/semantic_points"]
"/world/perception/semantic_points" (cuboid_behavior,) = spatial_view.visualizer_overrides["/world/perception/boxes3d"]
]
cuboid_behavior, cuboid_time_ranges = spatial_view.visualizer_overrides[
"/world/perception/boxes3d"
]
assert semantic_behavior.visible.as_arrow_array().to_pylist() == [True] assert semantic_behavior.visible.as_arrow_array().to_pylist() == [True]
assert cuboid_behavior.visible.as_arrow_array().to_pylist() == [False] assert cuboid_behavior.visible.as_arrow_array().to_pylist() == [False]
assert semantic_time_ranges.ranges.as_arrow_array().to_pylist() == []
assert cuboid_time_ranges.ranges.as_arrow_array().to_pylist() == []
visible_ranges = spatial_view.properties["VisibleTimeRanges"] visible_ranges = spatial_view.properties["VisibleTimeRanges"]
assert visible_ranges.ranges.as_arrow_array().to_pylist() == [ assert visible_ranges.ranges.as_arrow_array().to_pylist() == []
_point_behavior, _point_visualizer, point_time_ranges = spatial_view.visualizer_overrides[
"/world/points"
]
assert point_time_ranges.ranges.as_arrow_array().to_pylist() == [
{ {
"timeline": SESSION_TIMELINE, "timeline": SESSION_TIMELINE,
"range": {"start": -12_000_000_000, "end": 0}, "range": {"start": -12_000_000_000, "end": 0},