Skip to content

estop spec + fall and haywire conditions - #3691

Draft
AaryanAgrawal wants to merge 1 commit into
dimensionalOS:mainfrom
AaryanAgrawal:aaryan/estop
Draft

estop spec + fall and haywire conditions#3691
AaryanAgrawal wants to merge 1 commit into
dimensionalOS:mainfrom
AaryanAgrawal:aaryan/estop

Conversation

@AaryanAgrawal

@AaryanAgrawal AaryanAgrawal commented Aug 26, 2026

Copy link
Copy Markdown

Nothing in dimos notices when a robot falls over or when a policy drives a joint far faster than it should. ControlCoordinator.set_estop() already stops every task, but nothing automatic ever calls it, and no G1 task implements it, so on a G1 it reaches nothing.

This adds conditions at both ends and makes the shared path solid.

  a module           G1WholeBodyConnection sees the IMU at 500 Hz
    fall             tilt off gravity > max_tilt_deg
      |                _estop: EStop  ->  set_estop(True)
      |
  the coordinator    sees every joint each tick, in sim and on hardware
    flailing joint   |dq| > max_joint_speed_rad_s
      |                self.set_estop(True)
      v
  ControlCoordinator.set_estop   ->  every task inert  ->  GrootWBC disarms

Both routes end in the same call, so a condition can live wherever the data is.

The Spec

EStop in dimos/spec/control.py is how a module reaches the stop without importing the coordinator. Declared bare, _estop: EStop, it is a required ref: launching a stack with nothing able to stop the robot raises No module met that spec. before anything moves. Declaring the concrete class instead resolves to None and only fails at the fall; a topic never fails at all.

Note this is a launch-time guarantee, not a CI one. test_all_blueprints imports blueprints but never deploys them, so it does not exercise ref resolution.

set_estop was not actually a latch

Three fixes to the existing fan out, all reachable today:

  • It stored nothing. grep -c "_estopped" coordinator.py returned 0, so despite the docstring, a task registered after a trip came up live. The latch now lives on the coordinator and add_task applies it.
  • It skipped silently. getattr(task, "set_estop", None) with no else, which is why nobody noticed the G1 had zero coverage. It now logs the task it cannot stop.
  • One raising task aborted the loop, leaving every later task running. Each task is now dispatched in isolation.

Config

max_tilt_deg on the G1 connection defaults to 45 deg. max_joint_speed_rad_s on the coordinator defaults to None, off, because a robot agnostic coordinator has no safe default. No existing stack changes behaviour.

Results

Replaying a real recording of two deliberate G1 falls through the tilt check, 176,426 samples at 756 Hz:

stops issued 1
first trip t=118.36s at 45.10 deg
before the trip 86,691 samples, max tilt 44.99 deg, none tripped
after 89,735 samples including a recovery to 0.43 deg, still latched

For the joint limit: a settled standing G1 peaks at 0.77 rad/s and the whole fall reached 1.77 rad/s, against G1 URDF velocity limits of 20 to 37 rad/s.

dimos/control/, dimos/robot/unitree/g1/, dimos/spec/: 298 passed. The 3 failures are cartesian_ik failing to import GitPython, identical on a clean main worktree. test_all_blueprints: 20 failed / 78 passed, byte identical to main.

Simulated in MuJoCo

Both conditions driven by real physics through the shipped check functions. MuJoCo 3.10.0, the g1_29dof model with visual meshes replaced by boxes, since the STLs live in LFS. Simulated, not hardware.

Fall, free base tumbling under gravity. Trips at t=0.330s at 45.2 deg, tilt reaches 137.8 deg, one stop issued and then latched.

fall in sim

Flailing joint, left knee kicked to 30 rad/s against a 20 rad/s limit. Trips on the first tick past the limit.

flailing joint in sim

Not done

  • No blueprint sets max_joint_speed_rad_s yet, so flailing detection is not armed in tree.
  • Not run on hardware.
  • In a real sim blueprint the backend is MujocoSimModule, not the connection, so the fall check is not wired there. The clip above drives the check directly.

@github-actions github-actions Bot added the first-time-contributor PR opened by an author who had not previously committed to this repository label Aug 26, 2026
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.97080% with 11 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/control/coordinator.py 75.00% 4 Missing and 4 partials ⚠️
...ntrol/tasks/g1_groot_wbc_task/g1_groot_wbc_task.py 75.00% 1 Missing and 1 partial ⚠️
dimos/robot/unitree/g1/wholebody_connection.py 94.11% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main    #3691      +/-   ##
==========================================
+ Coverage   77.79%   77.83%   +0.04%     
==========================================
  Files        1289     1294       +5     
  Lines      123511   124064     +553     
  Branches    10823    10881      +58     
==========================================
+ Hits        96091    96571     +480     
- Misses      24278    24388     +110     
+ Partials     3142     3105      -37     
Flag Coverage Δ
OS-ubuntu-24.04-arm 72.84% <91.97%> (+0.06%) ⬆️
OS-ubuntu-latest 74.68% <91.97%> (+0.06%) ⬆️
Py-3.10 74.68% <91.97%> (+0.05%) ⬆️
Py-3.11 74.68% <91.97%> (+0.06%) ⬆️
Py-3.12 74.68% <91.97%> (+0.06%) ⬆️
Py-3.13 74.68% <91.97%> (+0.06%) ⬆️
Py-3.14 74.68% <91.97%> (+0.06%) ⬆️
Py-3.14t 74.68% <91.97%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../tasks/g1_groot_wbc_task/test_g1_groot_wbc_task.py 100.00% <100.00%> (ø)
dimos/control/test_estop.py 100.00% <100.00%> (ø)
dimos/control/tick_loop.py 81.81% <100.00%> (+9.00%) ⬆️
dimos/robot/unitree/g1/test_wholebody_estop.py 100.00% <100.00%> (ø)
dimos/spec/control.py 100.00% <100.00%> (ø)
dimos/robot/unitree/g1/wholebody_connection.py 36.63% <94.11%> (+9.66%) ⬆️
...ntrol/tasks/g1_groot_wbc_task/g1_groot_wbc_task.py 85.75% <75.00%> (+4.64%) ⬆️
dimos/control/coordinator.py 90.25% <75.00%> (+5.06%) ⬆️

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@AaryanAgrawal AaryanAgrawal changed the title Stop the robot on a fall or a flailing joint estop spec + fall and haywire conditions Aug 26, 2026
@AaryanAgrawal
AaryanAgrawal force-pushed the aaryan/estop branch 2 times, most recently from cb54fca to e6819ff Compare August 26, 2026 04:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first-time-contributor PR opened by an author who had not previously committed to this repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant