Skip to content

oomwoo_recovery_safety: recovery twist published once, not held — maneuvers > cmd_vel watchdog are truncated #32

Description

@thejesh23

oomwoo_recovery_safety: recovery twist is published once, not held — maneuvers longer than the base cmd_vel watchdog are silently truncated

Opening this as a conversation-first bug report (per CONTRIBUTING.md) rather than a code PR, since the module lives in a contributor's folder (contributions/recovery-safety/xbattlax/). Flagging it for the maintainer / module owner to decide the fix.

Where

contributions/recovery-safety/xbattlax/oomwoo_recovery_safety/oomwoo_recovery_safety/recovery_node.py

  • _execute (~L110–121) — publishes a twist step once
  • _timer_cb (~L97–103) — 20 Hz timer, only stops/escalates once the deadline passes; never re-publishes

What happens

A twist recovery step (back_up 0.8s, rotate_in_place 1.2s, rotate_to_collect_scans 1.5s, etc.) sets:

twist.linear.x = step.linear_x
twist.angular.z = step.angular_z
self._cmd_pub.publish(twist)          # published ONCE
...
self._active_deadline = monotonic() + step.duration_sec

The 20 Hz _timer_cb then does nothing until the deadline — it only stops + escalates. But a velocity command has to be re-sent continuously to keep a base moving: ROS 2 diff-drive controllers (ros2_control DiffDriveController, and most real bases) enforce a cmd_vel_timeout (default 0.5s) and halt if no fresh command arrives.

So any step with duration_sec > cmd_vel_timeout stops after ~0.5s while the node holds the deadline open for the full duration and then reports the maneuver as attempted.

Reproduce

On a base with the default 0.5s cmd_vel_timeout: trigger WEDGEDrotate_in_place (1.2s, angular_z=0.7). The base spins ~0.5s, watchdog-halts, then the node escalates at 1.2s. The robot performed ~40% of the intended rotation. back_up and the rotate_* steps are all affected.

Impact

Recovery maneuvers are silently truncated for exactly the stuck situations this node exists to handle — reduced recovery effectiveness, no error surfaced. Severity is minor and depends on the base's cmd_vel_timeout; a base with no watchdog is unaffected.

Suggested direction (owner's call)

Re-publish the active twist on each 20 Hz tick until the deadline — the timer already runs at the right rate. Roughly: hold self._active_twist when starting a twist step, re-publish it each tick while monotonic() < self._active_deadline, and clear it wherever _active_deadline is cleared (stop/command steps, _stop_motion sites).

Also worth a look (not the main bug)

External-command steps (e.g. clear_costmap) reuse duration_sec (0.1s) as the behavior_result callback timeout (recovery_node.py ~L121). 100ms may be too tight for an external service round-trip, so delegated steps could routinely time out into RECOVERY_EXHAUSTED. A separate, longer timeout for delegated commands vs. self-driven twist durations may be warranted.

Happy to help however is useful. Thanks for the project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions