Skip to content

fix(controller): preserve UUID continuity across occlusion on main#1449

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/combine-prs-1155-1444
Draft

fix(controller): preserve UUID continuity across occlusion on main#1449
Copilot wants to merge 3 commits into
mainfrom
copilot/combine-prs-1155-1444

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

📝 Description

This ports the UUID continuity fix from the release branch onto main and folds in the follow-up Re-ID behavior review from the later PR sequence. The net effect is that static objects keep their identity through short occlusions without regressing the newer UUID assignment flow already present on main.

  • Scope

    • Carries forward the controller-side UUID continuity fix from the release branch onto main
    • Keeps the later Re-ID fallback intent aligned with main's current assignID flow, avoiding a stale fallback path from being reintroduced
  • Controller tracking

    • Reuse an existing GID when a track reappears instead of always minting a new one
    • Preserve UUID mappings while tracks are suspended or temporarily unreliable by pruning only truly inactive tracks
  • Tracker bindings

    • Expose suspended and unreliable C++ tracker states to Python so the controller can retain active UUID state across occlusion windows
  • Net behavior

    • Static-object identity remains stable across brief disappearance/reappearance cycles
    • Mainline Re-ID collection/query behavior remains intact
all_active_tracks = set(chain(
  reliable_tracks,
  self.tracker.get_unreliable_tracks(),
  self.tracker.get_suspended_tracks()))
self.uuid_manager.pruneInactiveTracks(all_active_tracks)
  • Tests
    • TestPruneInactiveTracks (7 cases in tests/sscape_tests/uuid_manager/test_uuid_manager.py): verifies that pruneInactiveTracks preserves UUID mappings for reliable, unreliable, and suspended tracks and only removes truly inactive entries; includes a regression guard documenting the pre-fix breakage
    • TestGIDReuseAcrossOcclusion (4 cases in tests/sscape_tests/uuid_manager/test_uuid_continuity.py): verifies that from_tracked_object reuses the existing GID from active_ids when an rv_id reappears, falls back to the UUID for new or pending-query tracks, and takes the setPrevious path when a prior-frame object is available

✨ Type of Change

Select the type of change your PR introduces:

  • 🐞 Bug fix – Non-breaking change which fixes an issue
  • 🚀 New feature – Non-breaking change which adds functionality
  • 🔨 Refactor – Non-breaking change which refactors the code base
  • 💥 Breaking change – Changes that break existing functionality
  • 📚 Documentation update
  • 🔒 Security update
  • 🧪 Tests
  • 🚂 CI

🧪 Testing Scenarios

Describe how the changes were tested and how reviewers can test them too:

  • ✅ Tested manually
  • 🤖 Ran automated end-to-end tests

Run the uuid-manager unit tests inside the controller test container:

make controller && make -C controller test-build && make -C tests uuid-manager-unit SUPASS=<password>

✅ Checklist

Before submitting the PR, ensure the following:

  • 🔍 PR title is clear and descriptive
  • 📝 For internal contributors: If applicable, include the JIRA ticket number (e.g., ITEP-123456) in the PR title. Do not include full URLs
  • 💬 I have commented my code, especially in hard-to-understand areas
  • 📄 I have made corresponding changes to the documentation
  • ✅ I have added tests that prove my fix is effective or my feature works

) (#1155)

When a static object becomes temporarily occluded and later reappears in the scene, the controller assigns a new UUID instead of preserving the original one. This breaks object identity continuity for static objects across short occlusions.

This change updates the controller logic to ensure that static objects retain their original UUID when reactivated from suspended tracks.

Fixes #1152

(cherry picked from commit f4d8d7f)

Co-authored-by: saratpoluri <1325325+saratpoluri@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Ports the controller-side UUID continuity fix to main so tracked objects keep stable identities across short occlusions by retaining UUID mappings while tracks are suspended or temporarily unreliable.

Changes:

  • Exposes suspended and unreliable tracker states through the Python MultipleObjectTracker pybind interface.
  • Updates controller tracking flow to prune UUID mappings using the union of reliable, unreliable, and suspended tracks (instead of only reliable tracks).
  • Adjusts UUID assignment to reuse an existing GID for a reappearing rv_id when available.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
controller/src/robot_vision/python/src/robot_vision/extensions/tracking.cpp Adds Python bindings for MultipleObjectTracker.get_suspended_tracks() and get_unreliable_tracks().
controller/src/robot_vision/include/rv/tracking/MultipleObjectTracker.hpp Exposes suspended/unreliable track retrieval on the C++ tracker wrapper.
controller/src/controller/uuid_manager.py Uses a set comprehension for active track IDs during pruning (more efficient membership checks).
controller/src/controller/ilabs_tracking.py Preserves UUID continuity by pruning with all active track states and reusing existing GID mappings for returning rv_ids.

Comment on lines +129 to +133
# Check if UUID manager already has a mapping for this rv_id
with self.uuid_manager.active_ids_lock:
existing_gid_data = self.uuid_manager.active_ids.get(sscape_object.rv_id)
existing_gid = existing_gid_data[0] if existing_gid_data else None
if existing_gid is None:
Comment on lines +183 to +188
reliable_tracks = self.tracker.get_reliable_tracks()
# Include ALL active C++ tracks to preserve UUID mappings
all_active_tracks = set(chain(
reliable_tracks,
self.tracker.get_unreliable_tracks(),
self.tracker.get_suspended_tracks()))
Agent-Logs-Url: https://github.com/open-edge-platform/scenescape/sessions/a99d5e2d-3cf2-41d2-b3cc-2f6b4acf71e8

Co-authored-by: saratpoluri <1325325+saratpoluri@users.noreply.github.com>
@saratpoluri saratpoluri force-pushed the copilot/combine-prs-1155-1444 branch from f3d9937 to db0cfff Compare May 21, 2026 00:01
@saratpoluri
Copy link
Copy Markdown
Contributor

@copilot Add tests for #1152 and then run the test to verify whether these changes you have made actually pass that test. Create the test without the actual code changes in the PR being in your context.

Add TestPruneInactiveTracks to test_uuid_manager.py (7 cases covering
reliable/unreliable/suspended track pruning behavior) and a new
test_uuid_continuity.py (4 cases covering GID reuse in
from_tracked_object when a track reappears after occlusion).

Agent-Logs-Url: https://github.com/open-edge-platform/scenescape/sessions/18491a6d-dd2c-41e5-be18-b1515b2f6ba0

Co-authored-by: saratpoluri <1325325+saratpoluri@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot Add tests for #1152 and then run the test to verify whether these changes you have made actually pass that test. Create the test without the actual code changes in the PR being in your contex...

Added 11 unit tests in commit 282b9e7 covering the two code paths introduced by this PR:

TestPruneInactiveTracks (7 cases in tests/sscape_tests/uuid_manager/test_uuid_manager.py):

  • Tracks absent from the active set are pruned
  • Tracks present in the active set are preserved with their GID intact
  • Suspended tracks are preserved when included in the active set
  • Unreliable tracks are preserved when included in the active set
  • All three states together preserve all mappings
  • Empty active set clears all mappings
  • Regression guard: documents that excluding suspended tracks from the prune set loses their GID (pre-fix behavior)

TestGIDReuseAcrossOcclusion (4 cases in new tests/sscape_tests/uuid_manager/test_uuid_continuity.py):

  • Existing GID from active_ids is reused when an rv_id reappears after occlusion
  • UUID is used as GID for genuinely new tracks (no existing active_ids entry)
  • Falls back to UUID when active_ids holds [None, None] (query pending)
  • setPrevious path (not setGID) is used when the previous object is found in all_tracker_objects

Both test classes run under make -C tests uuid-manager-unit (the existing $(IMAGE)-controller-test target). Runtime execution is blocked in this environment because the controller test image is not built; the exact command to verify once the image exists is:

make controller && make -C controller test-build && make -C tests uuid-manager-unit SUPASS=<password>

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.

4 participants