Skip to content

Add region-coverage-path demo for GetRegionFromUser (wrist camera) - #819

Closed
L4co77 wants to merge 1 commit into
mainfrom
feat/region-coverage-path-demo
Closed

Add region-coverage-path demo for GetRegionFromUser (wrist camera)#819
L4co77 wants to merge 1 commit into
mainfrom
feat/region-coverage-path-demo

Conversation

@L4co77

@L4co77 L4co77 commented Jul 30, 2026

Copy link
Copy Markdown
region-2026-07-30_17.28.03.mp4

Demo requested by @davetcoleman on PR #20768 (GetRegionFromUser): rather than an orphan feature, an objective in lab sim that uses the wrist camera to let the user draw a bounding box and then runs a coverage sweep over that region.

What this adds

  • Objective select_region_and_cover.xml — the user draws a box on the wrist camera image; the box is lifted to 3D and swept with a serpentine coverage path, then planned and executed with approval.
  • GetMask2DFromRegion (lab_sim_behaviors) — rasterizes the PolygonStamped region from GetRegionFromUser into a Mask2D. GetRegionFromUser returns normalized [0..1] image coordinates, so this behavior takes camera_info and denormalizes them to pixels before filling the mask, so it aligns with the point-cloud projection in GetMasks3DFromMasks2D.
  • GenerateSurfaceCoveragePath (lab_sim_behaviors) — pure geometry: a boustrophedon (serpentine) raster of tool poses over the top face of the fitted oriented box, at a configurable standoff/line/point spacing. Named Surface... deliberately — core MoveIt Pro already ships a GenerateCoveragePath (a corner+area lawnmower); this one is OBB-driven with a standoff and tool-into-surface orientation.

Pipeline

SwitchUIPrimaryViewSwitchController (activate JTAC) → GetRegionFromUserGetPointCloud/GetCameraInfoGetMask2DFromRegionGetMasks3DFromMasks2DGetPointCloudFromMask3DGetOrientedBoundingBoxFromPointCloudGenerateSurfaceCoveragePathPlanCartesianPathWaitForJointTrajectoryApprovalExecuteTrajectory.

The 2D→3D chain mirrors the shipped ml_auto_grasp_object_from_clicked_point.xml, swapping the SAM click for the drawn region. GetMask2DFromRegion and GenerateSurfaceCoveragePath are the only new behaviors; everything else already exists. The oriented box's reference_pose is an identity pose in the wrist-camera frame (CreatePoseStamped), which disambiguates the OBB orientation against the camera axes.

Approach / why

The geometry is extracted into pure free functions (regionToMask2D, generateRasterPath) and unit-tested in isolation — the tick() methods are thin I/O wrappers. This keeps the coverage math deterministic and testable without a running stack. regionToMask2D uses cv::fillConvexPoly, valid because GetRegionFromUser returns a convex box.

Testing

  • 10 unit tests across the two pure functions: the coverage raster (happy-path grid, region-frame rotation, serpentine ordering + tool-into-surface orientation, degenerate/NaN/pose-cap error paths) and the mask (denormalization to pixels — which pins the [0..1]→pixel scaling — plus the <3-points, non-positive-dimensions, and zero-area error paths).
  • test_behavior_plugins.cpp extended so pluginlib load is verified for both new behaviors.
  • The objective is added to skip_objectives in objectives_integration_test.py (it needs an interactive UI prompt, like Marker Visualization Example / Teleoperate).

Live verification

Run end-to-end in lab_sim (mock hardware) against a #20768-built backend: draw a box on the wrist camera → the full pipeline recovers the 3D region, fits an OBB, generates the coverage path, and PlanCartesianPath plans it into an approvable ~1300-point trajectory that ExecuteTrajectory runs. Live testing surfaced and fixed four issues the objective-skipping CI could not: the reference-pose behavior name, the normalized-coordinate denormalization above, point_spacing vs blending_radius, and activating the admittance controller before execution.

Limitations (demo, not production)

  • OBB +Z signGenerateSurfaceCoveragePath assumes the fitted box's +Z is the outward surface normal. A PCA-fit OBB does not guarantee axis assignment or sign; the camera-frame reference_pose is the only disambiguator today.
  • No collision object backs the sweep — the standoff is the sole clearance.
  • No move-to-start before the first Cartesian segment; the arm is assumed near the region.
  • Reachability — a region far from the arm or on a steep surface can make the Cartesian path infeasible. Pick a flat, reachable region.
  • Wrist-camera topic names and coverage params (line_spacing / point_spacing / standoff) are tuned for lab_sim.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fecfc7ca-8f04-4f99-9028-894fea9f17c5

📥 Commits

Reviewing files that changed from the base of the PR and between 131591f and 87d56ad.

📒 Files selected for processing (1)
  • src/lab_sim/objectives/select_region_and_cover.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lab_sim/objectives/select_region_and_cover.xml

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a “Select Region and Cover” workflow using the wrist camera to capture a user-drawn area.
    • Automatically converts selected regions into masks and generates serpentine surface-coverage paths for execution.
    • Made the new workflow components available for behavior-tree use.
  • Tests
    • Added coverage for region masking, path generation, and behavior loading.
    • UI-dependent integration testing is skipped when the user-prompt service is unavailable.

Walkthrough

Adds the “Select Region and Cover” BehaviorTree objective, region-to-mask and surface-coverage behavior nodes, package and plugin registration, unit tests, and an integration-test skip for the unavailable UI server.

Changes

Select Region and Cover

Layer / File(s) Summary
Region-to-mask conversion
src/lab_sim_behaviors/include/lab_sim_behaviors/get_mask2d_from_region.hpp, src/lab_sim_behaviors/src/get_mask2d_from_region.cpp, src/lab_sim_behaviors/test/test_get_mask2d_from_region.cpp
Validates normalized polygon regions, rasterizes them into Mask2D, exposes the conversion through a BehaviorTree node, and tests success and error cases.
Surface coverage path generation
src/lab_sim_behaviors/include/lab_sim_behaviors/generate_surface_coverage_path.hpp, src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp, src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp
Generates bounded serpentine raster poses over oriented rectangular regions, exposes BehaviorTree ports, handles invalid inputs, and tests the geometry.
Behavior package wiring and validation
src/lab_sim_behaviors/CMakeLists.txt, src/lab_sim_behaviors/package.xml, src/lab_sim_behaviors/src/register_behaviors.cpp, src/lab_sim_behaviors/test/CMakeLists.txt, src/lab_sim_behaviors/test/test_behavior_plugins.cpp
Adds dependencies, compiles and registers both nodes, creates dedicated test targets, and verifies plugin instantiation.
Objective workflow
src/lab_sim/objectives/select_region_and_cover.xml, src/lab_sim/test/objectives_integration_test.py
Adds interactive region selection, sensor processing, mask iteration, coverage planning, approval, and trajectory execution. The integration test skips the objective because the UI server is unavailable.

Possibly related PRs

Suggested reviewers: davetcoleman, marioprats


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore (reviewers only)

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Human Review Check ❌ Error The PR adds public headers, classes, free functions, and new registered BehaviorTree plugin IDs in the exported lab_sim_behaviors library, which is a public API change. This PR requires review by a requested human reviewer. After review, a non-author requested reviewer should override this pre-merge check.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly explains the new objective, behaviors, pipeline, tests, live verification, and documented limitations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from 0a9c6b8 to 498816c Compare July 30, 2026 10:57
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from 498816c to 9f5f87a Compare July 30, 2026 11:30
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from 9f5f87a to a878e83 Compare July 30, 2026 14:43
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77 L4co77 self-assigned this Jul 30, 2026
@L4co77
L4co77 requested a review from davetcoleman July 30, 2026 15:29
@L4co77
L4co77 marked this pull request as ready for review July 30, 2026 15:29
@L4co77
L4co77 requested a review from marioprats July 30, 2026 15:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lab_sim_behaviors/src/get_mask2d_from_region.cpp (1)

78-87: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

cv::fillConvexPoly silently mis-rasterizes non-convex input.

The function assumes a convex polygon (per the header doc) but doesn't validate it. Since GetRegionFromUser is currently constrained to box-drawing, risk is low today, but as a public utility this could silently produce a wrong mask (rather than the documented explicit failure modes) if ever fed a concave/self-intersecting region.

Consider using cv::fillPoly (handles arbitrary simple polygons) instead of cv::fillConvexPoly, or explicitly validate convexity and return an error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lab_sim_behaviors/src/get_mask2d_from_region.cpp` around lines 78 - 87,
Replace the cv::fillConvexPoly call in the mask construction flow with
cv::fillPoly so arbitrary simple polygons are rasterized correctly without
relying on an unstated convexity assumption. Preserve the existing polygon
coordinates, mask dimensions, and fill value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lab_sim_behaviors/src/get_mask2d_from_region.cpp`:
- Around line 78-87: Replace the cv::fillConvexPoly call in the mask
construction flow with cv::fillPoly so arbitrary simple polygons are rasterized
correctly without relying on an unstated convexity assumption. Preserve the
existing polygon coordinates, mask dimensions, and fill value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 071c0df6-0959-4941-a36e-cb7fa48b389c

📥 Commits

Reviewing files that changed from the base of the PR and between 0bc120d and a878e83.

📒 Files selected for processing (13)
  • src/lab_sim/objectives/select_region_and_cover.xml
  • src/lab_sim/test/objectives_integration_test.py
  • src/lab_sim_behaviors/CMakeLists.txt
  • src/lab_sim_behaviors/include/lab_sim_behaviors/generate_surface_coverage_path.hpp
  • src/lab_sim_behaviors/include/lab_sim_behaviors/get_mask2d_from_region.hpp
  • src/lab_sim_behaviors/package.xml
  • src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp
  • src/lab_sim_behaviors/src/get_mask2d_from_region.cpp
  • src/lab_sim_behaviors/src/register_behaviors.cpp
  • src/lab_sim_behaviors/test/CMakeLists.txt
  • src/lab_sim_behaviors/test/test_behavior_plugins.cpp
  • src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp
  • src/lab_sim_behaviors/test/test_get_mask2d_from_region.cpp

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp`:
- Around line 85-102: The coverage path count calculations in
generateSurfaceCoveragePath must use ceil-based counts so non-divisible spacing
reaches both region edges; clamp the final X and Y coordinates to the positive
bounds, and apply kMaxCoveragePoses to these revised counts. In
src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp lines 77-94,
add a non-divisible-spacing test verifying both edges are reached and adjacent
samples never exceed the requested spacing.
- Around line 48-86: Extend validation in generate_surface_coverage_path to
require all three region_dimensions values to be finite and positive before any
calculations or narrowing casts. Also reject negative params.margin and
params.standoff while preserving the existing finite-parameter checks; allow
zero for these clearance inputs.

In `@src/lab_sim_behaviors/src/get_mask2d_from_region.cpp`:
- Around line 61-86: Validate every point’s x and y coordinates before the
min/max bounding-box loop in GetMask2DFromRegion. Reject any non-finite value or
value outside the normalized [0,1] range before calling to_px_x/to_px_y,
returning the function’s established unexpected-error result; only compute the
bounding box and rasterization polygon after all vertices pass validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a99da853-4a8b-4de0-8ad6-8ec59ceae597

📥 Commits

Reviewing files that changed from the base of the PR and between a878e83 and f51bf35.

📒 Files selected for processing (13)
  • src/lab_sim/objectives/select_region_and_cover.xml
  • src/lab_sim/test/objectives_integration_test.py
  • src/lab_sim_behaviors/CMakeLists.txt
  • src/lab_sim_behaviors/include/lab_sim_behaviors/generate_surface_coverage_path.hpp
  • src/lab_sim_behaviors/include/lab_sim_behaviors/get_mask2d_from_region.hpp
  • src/lab_sim_behaviors/package.xml
  • src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp
  • src/lab_sim_behaviors/src/get_mask2d_from_region.cpp
  • src/lab_sim_behaviors/src/register_behaviors.cpp
  • src/lab_sim_behaviors/test/CMakeLists.txt
  • src/lab_sim_behaviors/test/test_behavior_plugins.cpp
  • src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp
  • src/lab_sim_behaviors/test/test_get_mask2d_from_region.cpp

Comment thread src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp Outdated
Comment thread src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp Outdated
Comment thread src/lab_sim_behaviors/src/get_mask2d_from_region.cpp
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from f51bf35 to 131591f Compare July 30, 2026 19:00
@github-actions

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@davetcoleman

Copy link
Copy Markdown
Member

I want to see green grid lines of the coverage path trajectory in the video:
image

An example of what I'm talking about is in the product tour here on the Cold Spray slide:
https://docs.google.com/presentation/d/1Qko1JP_p29eNcGG02_-h3C7RSxajBH-ypQpk2Yg1PJw/edit?slide=id.g3e29b6662d0_0_7#slide=id.g3e29b6662d0_0_7

This graphic visualization will help users understand what is going on.

@davetcoleman

Copy link
Copy Markdown
Member

I know this is the wrong PR, but I feel like the mouse icon should change when we're in rectangle selection mode, maybe into like target sites or just a hand-click icon:

image

Overall, thanks for making this cool demo!

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@L4co77

L4co77 commented Aug 5, 2026

Copy link
Copy Markdown
Author

@davetcoleman

2d-2026-08-05_13.15.31.mp4

@L4co77

L4co77 commented Aug 5, 2026

Copy link
Copy Markdown
Author

[written by AI]

Added the green coverage-path visualization you asked for, @davetcoleman. VisualizePath now runs right after GenerateSurfaceCoveragePath, so the serpentine sweep is drawn as green grid lines over the region (like the Cold Spray slide) before it plans and executes.

Verified live end-to-end in lab_sim: drew a box on the wrist camera → the /visual_markers coverage marker is emitted (ns=coverage_path, RGBA [0,1,0,1], LINE_LIST) → PlanCartesianPathExecuteTrajectory, and the objective succeeds for a reachable region. I also lowered PlanCartesianPath blending_radius to 0.001 so the near-duplicate waypoints the raster emits at row turns don't trip the 2 * blending_radius check.

@davetcoleman

Copy link
Copy Markdown
Member

It's hard to see what's happening in the video once the robot starts moving, can you change the bottom right feed to be scene camera color?

From what I can tell, the green grid line seems to be under the table for some reason. Do you know why it's doing that?

At the end of the scan and plan objective, can you have the robot move back to the home position to restart it?

@L4co77

L4co77 commented Aug 7, 2026

Copy link
Copy Markdown
Author

It's hard to see what's happening in the video once the robot starts moving, can you change the bottom right feed to be scene camera color?

From what I can tell, the green grid line seems to be under the table for some reason. Do you know why it's doing that?

At the end of the scan and plan objective, can you have the robot move back to the home position to restart it?

Add VisualizePath after GenerateSurfaceCoveragePath so the operator sees the serpentine sweep before it is planned and executed. Lower PlanCartesianPath blending_radius to 0.001 so the near-duplicate waypoints the raster emits at row turns do not trip the '2 * blending_radius' check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@L4co77 L4co77 closed this Aug 7, 2026
@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from 87d56ad to eacc791 Compare August 7, 2026 12:03
@davetcoleman

Copy link
Copy Markdown
Member

Did you mean to close this?

The video looks much better! However, if you're up to the challenge of making this even better, can you select the corners of the burner stove top pad, and have it trace along that surface much closer? Right now, the bottles are in the way of the surface and feel kind of weird and arbitrary. Let's pretend it's spraying down or wiping down the flat surface of the burner in this demonstration.

@L4co77

L4co77 commented Aug 7, 2026

Copy link
Copy Markdown
Author

@davetcoleman

Claude broke this PR - a force-push from a shallow clone rewrote the branch history, so GitHub auto-closed it and it can't be reopened. Continuing the same work in a new PR: #835

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants