rollout fixups and correctly initialize unspecified controls

This commit is contained in:
Levi Burner
2024-11-27 07:02:16 -05:00
parent 41b325785a
commit e6eab04dd0
5 changed files with 29 additions and 23 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Python bindings
- Added ``bind`` method and removed id attribute from :ref:`mjSpec` objects. Using ids is error prone in scenarios of repeated attachment and - Added ``bind`` method and removed id attribute from :ref:`mjSpec` objects. Using ids is error prone in scenarios of repeated attachment and
detachment. Python users are encouraged to use names for unique identification of model elements. detachment. Python users are encouraged to use names for unique identification of model elements.
- Removed ``nroll`` argument from :ref:`rollout<PyRollout>` because its value can always be inferred. - Removed ``nroll`` argument from :ref:`rollout<PyRollout>` because its value can always be inferred.
- :ref:`rollout<PyRollout>` can now accept lists of MjModel of length ``nroll``. ``nroll`` argument deprecated because - :ref:`rollout<PyRollout>` can now accept sequences of MjModel of length ``nroll``. ``nroll`` argument deprecated because
its value can always be inferred. its value can always be inferred.
Bug fixes Bug fixes
+1 -1
View File
@@ -700,7 +700,7 @@ states and sensor values. The basic usage form is
state, sensordata = rollout.rollout(model, data, initial_state, control) state, sensordata = rollout.rollout(model, data, initial_state, control)
``model`` is either a single instance of MjModel or a list of compatible MjModel of length ``nroll``. ``model`` is either a single instance of MjModel or a sequence of compatible MjModel of length ``nroll``.
``initial_state`` is an ``nroll x nstate`` array, with ``nroll`` initial states of size ``nstate``, where ``initial_state`` is an ``nroll x nstate`` array, with ``nroll`` initial states of size ``nstate``, where
``nstate = mj_stateSize(model, mjtState.mjSTATE_FULLPHYSICS)`` is the size of the ``nstate = mj_stateSize(model, mjtState.mjSTATE_FULLPHYSICS)`` is the size of the
:ref:`full physics state<geFullPhysics>`. ``control`` is a ``nroll x nstep x ncontrol`` array of controls. Controls are :ref:`full physics state<geFullPhysics>`. ``control`` is a ``nroll x nstep x ncontrol`` array of controls. Controls are
+19 -17
View File
@@ -73,26 +73,28 @@ void _unsafe_rollout(std::vector<const mjModel*>& m, mjData* d, int nroll, int n
if (!(control_spec & mjSTATE_XFRC_APPLIED)) { if (!(control_spec & mjSTATE_XFRC_APPLIED)) {
mju_zero(d->xfrc_applied, 6*nbody); mju_zero(d->xfrc_applied, 6*nbody);
} }
if (!(control_spec & mjSTATE_MOCAP_POS)) {
for (int i = 0; i < nbody; i++) {
int id = m[0]->body_mocapid[i];
if (id >= 0) mju_copy3(d->mocap_pos+3*id, m[0]->body_pos+3*i);
}
}
if (!(control_spec & mjSTATE_MOCAP_QUAT)) {
for (int i = 0; i < nbody; i++) {
int id = m[0]->body_mocapid[i];
if (id >= 0) mju_copy4(d->mocap_quat+4*id, m[0]->body_quat+4*i);
}
}
if (!(control_spec & mjSTATE_EQ_ACTIVE)) {
for (int i = 0; i < neq; i++) {
d->eq_active[i] = m[0]->eq_active0[i];
}
}
// loop over rollouts // loop over rollouts
for (int r = 0; r < nroll; r++) { for (int r = 0; r < nroll; r++) {
// clear user inputs if unspecified
if (!(control_spec & mjSTATE_MOCAP_POS)) {
for (int i = 0; i < nbody; i++) {
int id = m[r]->body_mocapid[i];
if (id >= 0) mju_copy3(d->mocap_pos+3*id, m[r]->body_pos+3*i);
}
}
if (!(control_spec & mjSTATE_MOCAP_QUAT)) {
for (int i = 0; i < nbody; i++) {
int id = m[r]->body_mocapid[i];
if (id >= 0) mju_copy4(d->mocap_quat+4*id, m[r]->body_quat+4*i);
}
}
if (!(control_spec & mjSTATE_EQ_ACTIVE)) {
for (int i = 0; i < neq; i++) {
d->eq_active[i] = m[r]->eq_active0[i];
}
}
// set initial state // set initial state
mj_setState(m[r], d, state0 + r*nstate, mjSTATE_FULLPHYSICS); mj_setState(m[r], d, state0 + r*nstate, mjSTATE_FULLPHYSICS);
+7 -3
View File
@@ -14,6 +14,7 @@
# ============================================================================== # ==============================================================================
"""Roll out open-loop trajectories from initial states, get subsequent states and sensor values.""" """Roll out open-loop trajectories from initial states, get subsequent states and sensor values."""
from collections.abc import Sequence
from typing import Optional, Union from typing import Optional, Union
import mujoco import mujoco
@@ -22,7 +23,7 @@ import numpy as np
from numpy import typing as npt from numpy import typing as npt
def rollout(model: Union[mujoco.MjModel, list[mujoco.MjModel]], def rollout(model: Union[mujoco.MjModel, Sequence[mujoco.MjModel]],
data: mujoco.MjData, data: mujoco.MjData,
initial_state: npt.ArrayLike, initial_state: npt.ArrayLike,
control: Optional[npt.ArrayLike] = None, control: Optional[npt.ArrayLike] = None,
@@ -41,7 +42,7 @@ def rollout(model: Union[mujoco.MjModel, list[mujoco.MjModel]],
Allocates outputs if none are given. Allocates outputs if none are given.
Args: Args:
model: An mjModel or a list of MjModel with the same size signature. model: An mjModel or a sequence of MjModel with the same size signature.
data: An associated mjData instance. data: An associated mjData instance.
initial_state: Array of initial states from which to roll out trajectories. initial_state: Array of initial states from which to roll out trajectories.
([nroll or 1] x nstate) ([nroll or 1] x nstate)
@@ -76,6 +77,9 @@ def rollout(model: Union[mujoco.MjModel, list[mujoco.MjModel]],
initial_warmstart, control, state, sensordata) initial_warmstart, control, state, sensordata)
return state, sensordata return state, sensordata
if not isinstance(model, mujoco.MjModel):
model = list(model)
# check control_spec # check control_spec
if control_spec & ~mujoco.mjtState.mjSTATE_USER.value: if control_spec & ~mujoco.mjtState.mjSTATE_USER.value:
raise ValueError('control_spec can only contain bits in mjSTATE_USER') raise ValueError('control_spec can only contain bits in mjSTATE_USER')
@@ -151,7 +155,7 @@ def rollout(model: Union[mujoco.MjModel, list[mujoco.MjModel]],
_check_trailing_dimension(nsensordata, sensordata=sensordata) _check_trailing_dimension(nsensordata, sensordata=sensordata)
# tile input arrays/lists if required (singleton expansion) # tile input arrays/lists if required (singleton expansion)
model = model*nroll if len(model) == 1 else model model = model * nroll if len(model) == 1 else model
initial_state = _tile_if_required(initial_state, nroll) initial_state = _tile_if_required(initial_state, nroll)
initial_warmstart = _tile_if_required(initial_warmstart, nroll) initial_warmstart = _tile_if_required(initial_warmstart, nroll)
control = _tile_if_required(control, nroll, nstep) control = _tile_if_required(control, nroll, nstep)
+1 -1
View File
@@ -458,7 +458,7 @@ class MuJoCoRolloutTest(parameterized.TestCase):
def thread_initializer(): def thread_initializer():
thread_local.data = mujoco.MjData(model) thread_local.data = mujoco.MjData(model)
model_list = [model]*nroll model_list = [model] * nroll
def call_rollout(initial_state, control, state, sensordata): def call_rollout(initial_state, control, state, sensordata):
rollout.rollout(model_list, thread_local.data, initial_state, control, rollout.rollout(model_list, thread_local.data, initial_state, control,
skip_checks=True, skip_checks=True,