feat(self-obstruction): remove gripper self-obstruction as a blurry trigger

This commit is contained in:
CarolinePascal
2026-07-31 14:46:04 +02:00
parent fae2848dd7
commit 93fe77ee20
3 changed files with 22 additions and 3 deletions
@@ -104,6 +104,10 @@ def _parse_verdict(camera_key: str, result: Any, cfg: CameraCurationConfig) -> C
raw_label = result.get("view_label") raw_label = result.get("view_label")
label = str(raw_label).strip().lower().replace(" ", "_") if raw_label else "" label = str(raw_label).strip().lower().replace(" ", "_") if raw_label else ""
view_label = label if is_valid_view_label(label, cfg.view_vocabulary, cfg.allow_combos) else None view_label = label if is_valid_view_label(label, cfg.view_vocabulary, cfg.allow_combos) else None
if view_label is not None:
# Canonicalize combo order (``wrist_left`` -> ``left_wrist``) so the set
# of possible keys is deterministic regardless of the VLM's word order.
view_label = _order_combo(view_label.split("_"), cfg.view_vocabulary)
if raw_label and view_label is None: if raw_label and view_label is None:
logger.warning( logger.warning(
"camera %s: VLM returned view_label=%r which is not in the vocabulary %s; leaving unlabeled", "camera %s: VLM returned view_label=%r which is not in the vocabulary %s; leaving unlabeled",
@@ -5,9 +5,15 @@ together to judge the camera, not any single moment.
Do two things and return them as one JSON object. Do two things and return them as one JSON object.
1. QUALITY. Decide whether this camera view is usable for training a policy. 1. QUALITY. Decide whether this camera view is usable for training a policy.
Mark it UNUSABLE if it is blurry / out of focus, badly over- or Mark it UNUSABLE only for a genuine capture defect: blurry / out of focus,
under-exposed, mostly occluded, static/frozen, corrupted, or otherwise does badly over- or under-exposed, static/frozen, corrupted, or otherwise not
not clearly show the scene. Otherwise it is usable. showing the scene clearly. Otherwise it is usable.
Do NOT mark a view unusable because the robot's own gripper, arm, or
end-effector is visible or partially blocks the view — that is EXPECTED,
especially for a wrist-mounted camera where the gripper is normally in frame.
Only true image-quality problems count; the robot occluding its own
workspace does not.
2. VIEW LABEL. Choose the single best label for where this camera is mounted / 2. VIEW LABEL. Choose the single best label for where this camera is mounted /
what it looks at, using ONLY this closed vocabulary: what it looks at, using ONLY this closed vocabulary:
@@ -124,6 +124,15 @@ def test_curate_cameras_parses_and_validates(tmp_path):
assert verdicts["observation.images.c"].view_label is None # no frames assert verdicts["observation.images.c"].view_label is None # no frames
def test_curate_cameras_canonicalizes_combo_order():
cfg = CameraCurationConfig(view_vocabulary=VOCAB)
frames = {"observation.images.a": [_tiny_image()]}
# VLM emits the combo in the "wrong" order; we normalize to <side>_wrist.
vlm = _queued_vlm([{"usable": True, "blur_reason": None, "view_label": "wrist_left"}])
(verdict,) = curate_cameras(frames, cfg, vlm)
assert verdict.view_label == "left_wrist"
def test_build_name_mapping_and_collision(): def test_build_name_mapping_and_collision():
cfg = CameraCurationConfig(view_vocabulary=VOCAB) cfg = CameraCurationConfig(view_vocabulary=VOCAB)
existing = {"observation.images.cam_0": {}, "observation.images.cam_1": {}, "observation.images.top": {}} existing = {"observation.images.cam_0": {}, "observation.images.cam_1": {}, "observation.images.top": {}}