You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(opennav_docking): add on-demand detector client & unit tests for dock plugins (#5015) (#5218)
* feat(opennav_docking): Add dynamic lifecycle for external detectors
This change introduces an API to dynamically enable and disable external
docking detectors, such as those based on AI, to conserve system
resources when not actively docking.
Key changes:
- Added `startDetectionProcess()` and `stopDetectionProcess()` to the
docking plugin interface (`ChargingDock` and `NonChargingDock`).
- Implemented this logic in `SimpleChargingDock` and
`SimpleNonChargingDock` using a `std_srvs/Trigger` service call and
dynamic topic subscription to manage the detector's lifecycle.
- The `DockingServer` now ensures `stopDetectionProcess()` is called
on all exit paths, including success, failure, and cancellation,
to prevent dangling resources.
- Added new parameters (`detector_service_name`, `subscribe_toggle`, etc.)
to configure this behavior and updated the README documentation.
- Added comprehensive C++ unit tests to verify the new detector
lifecycle logic, specifically covering service failure cases and
proper cleanup on action termination.
Closes#5015
Signed-off-by: Koensgen Benjamin <[email protected]>
-
updtae
Signed-off-by: Koensgen Benjamin <[email protected]>
Signed-off-by: bkoensgen <[email protected]>
* fix: Address review feedback and fix unit tests
Signed-off-by: Koensgen Benjamin <[email protected]>
fix(docking): Change subscribe_toggle default to false
Signed-off-by: Koensgen Benjamin <[email protected]>
-
Signed-off-by: bkoensgen <[email protected]>
* refactor(docking): Improve plugin lifecycle and resource management
This commit addresses several code review suggestions to improve the design and robustness of the docking plugins.
- Replaced the `DetectorState` enum with a simpler `bool detector_enabled_` for clarity.
- Encapsulated the detection process lifecycle within the plugin itself. The `deactivate` method now ensures `stopDetectionProcess` is called, improving encapsulation and simplifying the `DockDatabase`.
- Refactored the `configure` method in plugins to group related logic, improving readability.
- Cleaned up redundant checks and calls in both the plugins and the `DockingServer` for more robust and intentional code.
Signed-off-by: Koensgen Benjamin <[email protected]>
Signed-off-by: bkoensgen <[email protected]>
* fix(plugins): Resolve race condition by setting enabled state in callback
The previous refactoring introduced a race condition where the detector
was marked as 'enabled' in startDetection() before a message was
actually received.
This caused tests to fail because getRefinedPose() would be called
with an enabled state but no valid/recent data.
This commit fixes the issue by moving the `detector_enabled_ = true`
assignment into the subscription callback. This ensures the plugin's
state accurately reflects that it has received at least one valid
data point before being considered active.
Signed-off-by: Koensgen Benjamin <[email protected]>
Signed-off-by: bkoensgen <[email protected]>
* refactor(docking): migrate to nav2_ros_common (node_utils, LifecycleNode, QoS)
Signed-off-by: bkoensgen <[email protected]>
* build(opennav_docking): update deps (std_srvs, package.xml + CMakeLists)
Signed-off-by: bkoensgen <[email protected]>
* refactor(Docking): migrate to nav2::LifecycleNode
Signed-off-by: bkoensgen <[email protected]>
* refactor(docking): use nav2::qos::StandardTopicQoS for subscriptions
Signed-off-by: bkoensgen <[email protected]>
* refactor(opennav_docking): replace raw queue size with rclcpp::QoS(1) in pubs/subs
Signed-off-by: bkoensgen <[email protected]>
* refactor(tests): migrate nav2_util::NodeThread to nav2::NodeThread
Signed-off-by: bkoensgen <[email protected]>
* refactor(tests): migrate to 3-args service callbacks
Signed-off-by: bkoensgen <[email protected]>
* style(test): apply ament_uncrustify
Signed-off-by: bkoensgen <[email protected]>
* refactor(opennav_docking) migrate detector Trigger service to node_->create_client()
Signed-off-by: bkoensgen <[email protected]>
* docking: use nav2 types for pubs/subs in SimpleChargingDock and add explicit detection state flags
Signed-off-by: bkoensgen <[email protected]>
* docking: use nav2 types for pubs/subs in SimpleNonChargingDock and add explicit detection state flags
Signed-off-by: bkoensgen <[email protected]>
* docking: split detection state in SimpleChargingDock (detection_started_ vs initial_pose_received_) and warn before first pose
Signed-off-by: bkoensgen <[email protected]>
* docking: split detection state in SimpleNonChargingDock (detection_started_ vs initial_pose_received_) and warn before first pose
Signed-off-by: bkoensgen <[email protected]>
* tests: adapt SimpleChargingDockTestable to initial_pose_received_ state
Signed-off-by: bkoensgen <[email protected]>
* tests: adapt SimpleNonChargingDockTestable to initial_pose_received_ state
Signed-off-by: bkoensgen <[email protected]>
* docs(docking): clarify external detection gating and subscribe_toggle behavior (default=false)
Signed-off-by: bkoensgen <[email protected]>
* fix(docking): keep SimpleNonChargingDock registered as ChargingDock (stay in-scope)
Signed-off-by: bkoensgen <[email protected]>
* docs(docking): revert README note to pre-e881de19 state
Signed-off-by: bkoensgen <[email protected]>
* fix(docking_server): remove redundant null-check before stopDetectionProcess() in terminal cleanup
Signed-off-by: bkoensgen <[email protected]>
* style(docking_server): unify terminal order (stash->publishZeroVelocity->stopDetection->terminate)
Signed-off-by: bkoensgen <[email protected]>
* lint
Signed-off-by: bkoensgen <[email protected]>
* fix(docking): inline detection process
Signed-off-by: bkoensgen <[email protected]>
* chore(docking): drop redundant detector comment
Signed-off-by: bkoensgen <[email protected]>
* chore(docking): clarify detector logs
Signed-off-by: bkoensgen <[email protected]>
* fix(docking): activate lifecycle publishers
Signed-off-by: bkoensgen <[email protected]>
* chore(docking): reuse dock pose subscription
Signed-off-by: bkoensgen <[email protected]>
* lint
Signed-off-by: bkoensgen <[email protected]>
* fix(docking_server): drop redundant DB deactivate on cleanup
Signed-off-by: bkoensgen <[email protected]>
* refactor(docking): rename detection state flag to detection_active
Signed-off-by: bkoensgen <[email protected]>
* fix(docking): reset detection flags on cleanup
Signed-off-by: bkoensgen <[email protected]>
---------
Signed-off-by: Koensgen Benjamin <[email protected]>
Signed-off-by: bkoensgen <[email protected]>
Copy file name to clipboardExpand all lines: nav2_docking/README.md
+12-6Lines changed: 12 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,12 +36,14 @@ The `ChargingDock` and `NonChargingDock` plugins are the heart of the customizab
36
36
The docking procedure is as follows:
37
37
1. Take action request and obtain the dock's plugin and its pose
38
38
2. If the robot is not within the prestaging tolerance of the dock's staging pose, navigate to the staging pose
39
-
3. Use the dock's plugin to initially detect the dock and return the docking pose
40
-
4. Enter a vision-control loop where the robot attempts to reach the docking pose while its actively being refined by the vision system
41
-
5. Exit the vision-control loop once contact has been detected or charging has started
42
-
6. Wait until charging starts (if applicable) and return success.
39
+
3. Call the dock's plugin `startDetectionProcess()` method to activate any external detection mechanisms.
40
+
4. Use the dock's plugin to initially detect the dock (`getRefinedPose`) and return the docking pose.
41
+
5. Enter a vision-control loop where the robot attempts to reach the docking pose while it's actively being refined by the vision system.
42
+
6. Exit the vision-control loop once contact has been detected or charging has started (if applicable).
43
+
7. Wait until charging starts (if applicable) and return success.
44
+
8. Call the dock's plugin `stopDetectionProcess()` method to deactivate any external detection mechanisms.
43
45
44
-
If anywhere this procedure is unsuccessful, `N` retries may be made by driving back to the dock's staging pose and trying again. If still unsuccessful, it will return a failure code to indicate what kind of failure occurred to the client.
46
+
If anywhere this procedure is unsuccessful (before step 8), `N` retries may be made, driving back to the dock's staging pose, and then restarting the process from step 3. If still unsuccessful after retries, it will return a failure code to indicate what kind of failure occurred to the client.
45
47
46
48
Undocking works more simply:
47
49
1. If previously docked, use the known dock information to get the dock type. If not, use the undock action request's indicated dock type
@@ -175,8 +177,9 @@ some robots.
175
177
`getStagingPose` applies a parameterized translational and rotational offset to the dock pose to obtain the staging pose.
176
178
177
179
`getRefinedPose` can be used in two ways.
178
-
1. A blind approach where the returned dock pose will simply be equal to whatever was passed in from the dock database. This may work with a reduced success rate on a real robot (due to global localization error), but is useful for initial testing and simulation.
180
+
1. A blind approach where the returned dock pose will simply be equal to whatever was passed in from the dock database. This may work with a reduced success rate on a real robot (due to global localization error), but is useful for initial testing and simulation. The `start/stopDetectionProcess` methods would typically do nothing in this case.
179
181
2. The more realistic use case is to use an AR marker, dock pose detection algorithm, etc. The plugin will subscribe to a `geometry_msgs/PoseStamped` topic `detected_dock_pose`. This can be used with the `image_proc/TrackMarkerNode` for Apriltags or other custom detectors for your dock. It is unlikely the detected pose is actually the pose you want to dock with, so several parameters are supplied to represent your docked pose relative to the detected feature's pose.
182
+
The subscription to `detected_dock_pose` can be managed dynamically (see `subscribe_toggle` parameter). Additionally, an external detector process can be triggered via a service call (see `detector_service_name`). The `DockingServer` calls `startDetectionProcess()` before `getRefinedPose` (which is called in a loop) and `stopDetectionProcess()` after the docking action concludes (successfully or not). This allows plugins to manage resources like GPU usage by only running detection when needed.
180
183
181
184
During the docking approach, there are two options for detecting `isDocked`:
182
185
1. We can check the joint states of the wheels if the current has spiked above a set threshold to indicate that the robot has made contact with the dock or other physical object.
@@ -257,6 +260,9 @@ Note: `dock_plugins` and either `docks` or `dock_database` are required.
257
260
| staging_yaw_offset | Staging pose angle relative to dock pose (rad) | double | 0.0 |
258
261
| dock_direction | Whether the robot is docking with the dock forward or backward in motion | string | "forward" or "backward" |
259
262
| rotate_to_dock | Enables backward docking without requiring a sensor for detection during the final approach. When enabled, the robot approaches the staging pose facing forward with sensor coverage for dock detection; after detection, it rotates and backs into the dock using only the initially detected pose for dead reckoning. | bool | false |
263
+
| detector_service_name | Trigger service name to start/stop the detector (e.g., a camera node or an AprilTag detector node). Leave empty to disable service calls. | string | "" |
264
+
| detector_service_timeout | Timeout (seconds) to wait for the detector service (if `detector_service_name` is set) to become available or respond. | double | 5.0 |
265
+
| subscribe_toggle | If true, dynamically subscribe/unsubscribe to `detected_dock_pose` topic when `start/stopDetectionProcess` are called. If false, the subscription is kept alive throughout the plugin's lifecycle if `use_external_detection_pose` is true. This can be useful if the detector node is always running and publishing. | bool | false |
260
266
261
267
Note: The external detection rotation angles are setup to work out of the box with Apriltags detectors in `image_proc` and `isaac_ros`.
0 commit comments