Skip to content

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

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

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

Conversation

@L4co77

@L4co77 L4co77 commented Aug 7, 2026

Copy link
Copy Markdown

[written by AI]

Supersedes #819, which GitHub locked closed after an accidental force-push from a shallow clone. The branch and its full history are intact — only that PR record is stuck (its head froze on a bad commit and it can't be reopened), so this PR continues the same branch. Original review thread, including @davetcoleman's feedback: #819.

Changes since #819

  • Coverage grid now renders on the surface, not under the tableCreatePoseStamped sets orientation_xyzw="1;0;0;0" so the OBB reference_pose +Z is the outward surface normal (addresses @davetcoleman's "the green grid line seems to be under the table" note).
  • Arm returns to Home at the end — added a Move to Waypoint → Home step so the demo resets for the next run (addresses @davetcoleman's "move back to the home position to restart it" note).

Screencast.from.2026-08-07.13-57-37.webm

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 Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a “Select Region and Cover” workflow for selecting a surface region, generating and visualizing a coverage path, approving execution, and returning the arm home.
    • Added conversion of selected regions into masks and rasterized coverage-path generation with configurable spacing, margins, and standoff.
    • Added validation and visualization throughout the workflow.
  • Tests

    • Added coverage for region-mask conversion, path generation, behavior registration, and invalid input handling.
    • Excluded the UI-dependent workflow from headless integration tests.

Walkthrough

Adds two behavior-tree nodes for region-mask extraction and raster coverage-path generation. Registers them with the behavior factory and uses them in a new Select Region and Cover objective with planning, approval, execution, and Home recovery.

Changes

Region selection and surface coverage

Layer / File(s) Summary
Region mask extraction
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
Adds normalized polygon validation, cropped OpenCV mask rasterization, Mask2D output creation, and behavior-tree execution with test coverage.
Raster 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
Adds validated serpentine raster generation with spacing, margins, standoff, region-frame transforms, tool orientation, and geometry tests.
Behavior registration and objective workflow
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, src/lab_sim/objectives/select_region_and_cover.xml, src/lab_sim/test/objectives_integration_test.py
Adds build dependencies and test targets, registers both nodes, adds the complete selection-to-execution objective, and skips the objective in headless integration tests because its UI prompt service 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 declarations in two lab_sim_behaviors headers and exports their shared library target, including new geometry functions and behavior classes. 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, processing pipeline, testing, live verification, and limitations covered by the changeset.
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.

@L4co77
L4co77 marked this pull request as ready for review August 7, 2026 15:47
@L4co77
L4co77 requested a review from davetcoleman August 7, 2026 15:48

@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: 1

🧹 Nitpick comments (2)
src/lab_sim_behaviors/include/lab_sim_behaviors/generate_surface_coverage_path.hpp (1)

43-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the remaining rejection cases.

generateRasterPath also returns an empty vector for non-finite inputs, a negative margin, and a negative standoff. Add these cases so the contract matches src/lab_sim_behaviors/src/generate_surface_coverage_path.cpp lines 56-66.

📝 Proposed doc update
- * Returns an empty vector when the usable area (after `@p` margin) is non-positive, a spacing is
- * non-positive, or the region/spacing combination would exceed the internal pose cap.
+ * Returns an empty vector when any input is non-finite, the usable area (after `@p` margin) is
+ * non-positive, a spacing is non-positive, `@p` margin or `@p` standoff is negative, or the
+ * region/spacing combination would exceed the internal pose cap.
🤖 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/include/lab_sim_behaviors/generate_surface_coverage_path.hpp`
around lines 43 - 44, Update the documentation for generateRasterPath to state
that it also returns an empty vector when any input is non-finite, or when
margin or standoff is negative. Keep the existing rejection cases documented so
the contract matches the implementation.
src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp (1)

152-153: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Correct the comment: the implementation uses ceil, not floor.

generateRasterPath computes the counts with std::ceil. The values match here only because 0.2/0.05 and 0.4/0.02 divide evenly. The wrong operator in the comment contradicts the CoversFarEdgeWhenSpacingDoesNotDivideEvenly test.

📝 Proposed comment fix
-  // THEN it is a full grid: floor(0.2/0.05)+1 = 5 lines x floor(0.4/0.02)+1 = 21 points
+  // THEN it is a full grid: ceil(0.2/0.05)+1 = 5 lines x ceil(0.4/0.02)+1 = 21 points
🤖 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/test/test_generate_surface_coverage_path.cpp` around
lines 152 - 153, Correct the explanatory comment above the path size assertion
to describe the grid counts using ceil rather than floor, while preserving the
existing 5-by-21 calculation and ASSERT_THAT(path, SizeIs(105)) behavior.
🤖 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/get_mask2d_from_region.cpp`:
- Around line 89-98: Update the polygon rasterization in regionToMask2D to use
cv::fillPoly so arbitrary, including concave, regions are filled correctly;
retain the existing local-coordinate construction and mask behavior, and add
coverage for a concave region plus documentation of the accepted polygon
contract.

---

Nitpick comments:
In
`@src/lab_sim_behaviors/include/lab_sim_behaviors/generate_surface_coverage_path.hpp`:
- Around line 43-44: Update the documentation for generateRasterPath to state
that it also returns an empty vector when any input is non-finite, or when
margin or standoff is negative. Keep the existing rejection cases documented so
the contract matches the implementation.

In `@src/lab_sim_behaviors/test/test_generate_surface_coverage_path.cpp`:
- Around line 152-153: Correct the explanatory comment above the path size
assertion to describe the grid counts using ceil rather than floor, while
preserving the existing 5-by-21 calculation and ASSERT_THAT(path, SizeIs(105))
behavior.
🪄 Autofix

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: d776a562-4fae-44e0-be3b-4bad538df6e4

📥 Commits

Reviewing files that changed from the base of the PR and between f9f967b and 000d8d9.

📒 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/get_mask2d_from_region.cpp Outdated
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@davetcoleman

davetcoleman commented Aug 7, 2026

Copy link
Copy Markdown
Member

See my feedback in the previous PR

@L4co77

L4co77 commented Aug 7, 2026

Copy link
Copy Markdown
Author

See my feedback in the previous PR

select-2026-08-07_18.28.26.mp4

Draw a box on the wrist camera over a flat surface; the region is lifted to 3D, fitted to an
oriented box, and swept with a serpentine coverage path (spray/wipe feel), then planned,
approved, and executed, ending with a move back to Home.

New lab_sim_behaviors: GetMask2DFromRegion (rasterizes the normalized [0..1] region into a
Mask2D via cv::fillPoly) and GenerateSurfaceCoveragePath (boustrophedon raster over the fitted
box top face at a configurable standoff). Geometry is in pure free functions with unit tests.
CreatePoseStamped orientation_xyzw=1;0;0;0 keeps the OBB reference +Z outward so the coverage
renders on the surface, not under it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@L4co77
L4co77 force-pushed the feat/region-coverage-path-demo branch from a4e4320 to b3ab65c Compare August 7, 2026 16:49
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

MoveIt Pro Example WS - Objectives Integration Test Report

@davetcoleman

Copy link
Copy Markdown
Member

I'm really excited about this example application!

  • Can you make it a favorite? We are limited to 8, so perhaps it should replace an existing scan and plan example
  • Have the EEF closed before execution, so there's more of a pointed object moving over the plane. Might cause collision to raise up if needed.
  • Can we rename it to "Select Region for Coverage Path" or something better than @rlpratt12 can think of?

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