Skip to content

Commit 7418b85

Browse files
committed
Always abort on teleop activation, not just during execute
The user clicks Activate during planning (before execute starts), so is_executing() is False and abort never fired. Fix: always call request_abort. If a primitive is running, abort is checked when execute starts or on its next waypoint. If nothing is running, _do_activate clears it. Limitation: abort is global (affects all arms). Per-arm abort requires per-arm abort flags — tracked in #56 (arm ownership registry).
1 parent 4db35a7 commit 7418b85

1 file changed

Lines changed: 12 additions & 17 deletions

File tree

src/mj_viser/teleop_panel.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -318,39 +318,34 @@ def _on_record(event) -> None:
318318
# _teleop_loop thread to avoid recursion (step → sync → on_sync).
319319

320320
def _activate_teleop(self) -> None:
321-
# Only abort if THIS arm is executing a trajectory. If the other
322-
# arm is executing, let it continue — we can teleop in parallel.
323-
arm_name = self._arm.config.name
324-
if (
325-
self._event_loop is not None
326-
and self._event_loop.is_executing(arm_name)
327-
and self._request_abort_fn is not None
328-
):
321+
# Always abort — if a primitive is running (planning or executing),
322+
# the abort flag will be checked when execute() starts or on its
323+
# next waypoint. If nothing is running, _do_activate clears it.
324+
if self._request_abort_fn is not None:
329325
self._request_abort_fn()
330326

327+
# Immediate visual feedback (viser GUI calls are thread-safe)
328+
self._activate_btn.name = "Deactivate"
329+
self._activate_btn.color = "red"
330+
self._status_md.content = "🟡 **Activating...**"
331+
331332
def _do_activate():
332-
# Clear the abort we just triggered (or any stale one)
333333
if self._clear_abort_fn is not None:
334334
self._clear_abort_fn()
335335
ee_pose = self._controller.activate()
336336
self._is_teleop_active = True
337-
# Update gizmo (viser GUI calls are thread-safe even from main thread)
338337
self._gizmo.wxyz = xmat_to_wxyz(ee_pose[:3, :3].flatten())
339338
self._gizmo.position = tuple(ee_pose[:3, 3])
340339
self._gizmo.visible = True
341340
if self._ghost is not None:
342341
self._ghost.set_visible(True)
343-
self._activate_btn.name = "Deactivate"
344-
self._activate_btn.color = "red"
345-
# Register so tick() steps this controller
346342
if self._event_loop is not None:
347343
self._event_loop.register_teleop(self._controller, self)
348344

349345
if self._event_loop is not None:
350-
# Submit to run on the physics thread. execute() cooperatively
351-
# drains the queue between control cycles, so this runs promptly
352-
# even if another arm is mid-trajectory.
353-
self._event_loop.submit(_do_activate).result()
346+
# Fire and forget — don't block viser thread. Activation
347+
# completes when tick() processes it after execute yields.
348+
self._event_loop.submit(_do_activate)
354349
else:
355350
_do_activate()
356351

0 commit comments

Comments
 (0)