google-deepmind / google-deepmind/mujoco
Delayed multi-input actuators use uninitialized control slots
- Dominant language
- C++
- Stars
- 15.2k
- Forks
- 1.8k
- Avg merge
- 10d 16h
- Merged PRs (30d)
- 25
Description
### Intro
Hi!
I am testing the interaction between current multi-input actuators and actuator history/delay. The control-history path remains scalar and leaves the remaining inputs of a delayed actuator uninitialized.
### My setup
- MuJoCo source build from `main` at `10124d5d9dca411ec3c8988aa1e3b619103d71bb`
- C API, double precision
- Ubuntu 24.04.5 LTS, Linux 7.0.0-28-generic x86_64
- CMake 3.28.3 and GCC 11.5.0
### What's happening? What did you expect?
A PID actuator with `input="pos vel ff"` owns three control values. With live controls `[1, 2, 3]`, its force is `6`. I expected the delayed path to record and later use the same complete control block.
Actuator history allocation, insertion, `mj_readCtrl`, and `mj_initCtrlHistory` are hard-coded to dimension one. During actuation, a local array of all `nu` controls is allocated, but a delayed actuator assigns only `ctrl[adr]`. The remaining two slots are consumed by the PID gain without initialization.
The minimal reproduction reuses MuJoCo's stack allocation with canaries so the omitted slots are deterministic:
```text
nu=3 delayed-force=501.0 direct-force=6.0
```
The delayed path uses the recorded first input `1` plus stale stack values `200` and `300`. Disabling only delay uses the live complete block and produces `6`.
Relevant source and documentation:
- https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/user/user_model.cc#L2213-L2224
- https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/user/user_model.cc#L3863-L3872
- https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/engine/engine_forward.c#L386-L400
- https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/engine/engine_forward.c#L1344-L1360
- https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/engine/engine_support.c#L934-L1013
### Steps for reproduction
1. Check out and build the commit above.
2. Compile `poc/reproducer.cc` using `poc/README.md`.
3. Run it and compare delayed force with the direct-control causal control.
Reproduction package:
[multi-input-actuator-delay-history-poc.zip](https://github.com/user-attachments/files/32260502/multi-input-actuator-delay-history-poc.zip)
### Minimal model for reproduction
The complete MJCF is embedded in the C++ file and contains one slider, one sphere, and one delayed three-input PID. No assets are required.
### Code required for reproduction
See `poc/reproducer.cc`. A complete patch needs a block-aware history layout and API decision, so I have included a fix design rather than a partial one-line change.
### Confirmations
- [x] I searched the [latest documentation](https://mujoco.readthedocs.io/en/latest/overview.html) thoroughly before posting.
- [x] I searched previous [Issues](https://github.com/google-deepmind/mujoco/issues) and [Discussions](https://github.com/google-deepmind/mujoco/discussions), I am certain this has not been raised before.
Contributor guide
Research direction
Compile and run poc/reproducer.cc using poc/README.md to confirm the delayed and direct force values. Then trace the history allocation, insertion, mj_readCtrl, and mj_initCtrlHistory paths in src/user/user_model.cc and src/engine/engine_support.c, along with delayed actuation in src/engine/engine_forward.c. Done means a delayed multi-input actuator preserves its complete control block and the history API decision is resolved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- robotics
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100