Implicitfast integration for MJX.

PiperOrigin-RevId: 664889120
Change-Id: Id3bd3916fe821ab2af79c52e53e14837ae829b2a
This commit is contained in:
Erik Frey
2024-08-19 11:11:34 -07:00
committed by Copybara-Service
parent 5d91231d31
commit a68141eeff
9 changed files with 75 additions and 9 deletions
+5 -3
View File
@@ -23,17 +23,19 @@ MJX
7. Added velocity-dependent sensors: ``JOINTVEL``, ``ACTUATORVEL``, ``BALLANGVEL``. 7. Added velocity-dependent sensors: ``JOINTVEL``, ``ACTUATORVEL``, ``BALLANGVEL``.
8. Changed default policy to avoid placing unused (MuJoCo-only) arrays on device. 8. Changed default policy to avoid placing unused (MuJoCo-only) arrays on device.
9. Added ``device`` parameter to ``mjx.make_data`` to bring it to parity with ``mjx.put_model`` and ``mjx.put_data``. 9. Added ``device`` parameter to ``mjx.make_data`` to bring it to parity with ``mjx.put_model`` and ``mjx.put_data``.
10. Added support for :ref:`implicitfast integration<geIntegration>` for all cases except
:doc:`fluid drag <computation/fluid>`.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
10. Fixed a performance regression introduced in 3.1.7 in mesh Bounding Volume Hierarchies (:github:issue:`1875`, 11. Fixed a performance regression introduced in 3.1.7 in mesh Bounding Volume Hierarchies (:github:issue:`1875`,
contribution by :github:user:`michael-ahn`). contribution by :github:user:`michael-ahn`).
11. Fixed a bug wherein, for models that have both muscles and stateless actuators and used one of the implicit 12. Fixed a bug wherein, for models that have both muscles and stateless actuators and used one of the implicit
integrators, wrong derivatives would be computed. integrators, wrong derivatives would be computed.
Python bindings Python bindings
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
12. Added support for engine plugins in :ref:`mjSpec` (:github:issue:`1903`). 13. Added support for engine plugins in :ref:`mjSpec` (:github:issue:`1903`).
Version 3.2.2 (Aug 8, 2024) Version 3.2.2 (Aug 8, 2024)
+2 -2
View File
@@ -202,7 +202,7 @@ The following features are **fully supported** in MJX:
* - :ref:`Equality <mjtEq>` * - :ref:`Equality <mjtEq>`
- ``CONNECT``, ``WELD``, ``JOINT``, ``TENDON`` - ``CONNECT``, ``WELD``, ``JOINT``, ``TENDON``
* - :ref:`Integrator <mjtIntegrator>` * - :ref:`Integrator <mjtIntegrator>`
- ``EULER``, ``RK4`` - ``EULER``, ``RK4``, ``IMPLICITFAST`` (``IMPLICITFAST`` not supported with :doc:`fluid drag <computation/fluid>`)
* - :ref:`Cone <mjtCone>` * - :ref:`Cone <mjtCone>`
- ``PYRAMIDAL``, ``ELLIPTIC`` - ``PYRAMIDAL``, ``ELLIPTIC``
* - :ref:`Condim <coContact>` * - :ref:`Condim <coContact>`
@@ -229,7 +229,7 @@ The following features are **in development** and coming soon:
* - :ref:`Constraint <mjtConstraint>` * - :ref:`Constraint <mjtConstraint>`
- :ref:`Frictionloss <coFriction>`, ``FRICTION_DOF`` - :ref:`Frictionloss <coFriction>`, ``FRICTION_DOF``
* - :ref:`Integrator <mjtIntegrator>` * - :ref:`Integrator <mjtIntegrator>`
- ``IMPLICIT``, ``IMPLICITFAST`` - ``IMPLICIT``
* - Dynamics * - Dynamics
- :ref:`Inverse <mj_inverse>` - :ref:`Inverse <mj_inverse>`
* - :ref:`Actuator Dynamics <mjtDyn>` * - :ref:`Actuator Dynamics <mjtDyn>`
+2 -1
View File
@@ -23,6 +23,7 @@ from mujoco.mjx._src.forward import fwd_acceleration
from mujoco.mjx._src.forward import fwd_actuation from mujoco.mjx._src.forward import fwd_actuation
from mujoco.mjx._src.forward import fwd_position from mujoco.mjx._src.forward import fwd_position
from mujoco.mjx._src.forward import fwd_velocity from mujoco.mjx._src.forward import fwd_velocity
from mujoco.mjx._src.forward import implicit
from mujoco.mjx._src.forward import rungekutta4 from mujoco.mjx._src.forward import rungekutta4
from mujoco.mjx._src.forward import step from mujoco.mjx._src.forward import step
from mujoco.mjx._src.io import get_data from mujoco.mjx._src.io import get_data
@@ -32,9 +33,9 @@ from mujoco.mjx._src.io import put_data
from mujoco.mjx._src.io import put_model from mujoco.mjx._src.io import put_model
from mujoco.mjx._src.passive import passive from mujoco.mjx._src.passive import passive
from mujoco.mjx._src.ray import ray from mujoco.mjx._src.ray import ray
from mujoco.mjx._src.sensor import sensor_acc
from mujoco.mjx._src.sensor import sensor_pos from mujoco.mjx._src.sensor import sensor_pos
from mujoco.mjx._src.sensor import sensor_vel from mujoco.mjx._src.sensor import sensor_vel
from mujoco.mjx._src.sensor import sensor_acc
from mujoco.mjx._src.smooth import camlight from mujoco.mjx._src.smooth import camlight
from mujoco.mjx._src.smooth import com_pos from mujoco.mjx._src.smooth import com_pos
from mujoco.mjx._src.smooth import com_vel from mujoco.mjx._src.smooth import com_vel
+42
View File
@@ -348,6 +348,46 @@ def rungekutta4(m: Model, d: Data) -> Data:
return d return d
@named_scope
def implicit(m: Model, d: Data) -> Data:
"""Integrates fully implicit in velocity."""
qderiv = None
# qDeriv += d qfrc_actuator / d qvel
if not m.opt.disableflags & DisableBit.ACTUATION:
affine_bias = m.actuator_biastype == BiasType.AFFINE
bias_vel = m.actuator_biasprm[:, 2] * affine_bias
affine_gain = m.actuator_gaintype == GainType.AFFINE
gain_vel = m.actuator_gainprm[:, 2] * affine_gain
ctrl = d.ctrl.at[m.actuator_dyntype != DynType.NONE].set(d.act)
vel = bias_vel + gain_vel * ctrl
qderiv = d.actuator_moment.T @ jp.diag(vel) @ d.actuator_moment
# qDeriv += d qfrc_passive / d qvel
if not m.opt.disableflags & DisableBit.PASSIVE:
if qderiv is None:
qderiv = -jp.diag(m.dof_damping)
else:
qderiv -= jp.diag(m.dof_damping)
if m.ntendon:
qderiv -= d.ten_J.T @ jp.diag(m.tendon_damping) @ d.ten_J
# TODO(robotics-simulation): fluid drag model
if m.opt.has_fluid_params:
raise NotImplementedError('fluid drag not supported for implicitfast')
qacc = d.qacc
if qderiv is not None:
# TODO(robotics-simulation): use smooth.factor_m / solve_m here:
qm = support.full_m(m, d) if support.is_sparse(m) else d.qM
qm -= m.opt.timestep * qderiv
qh, _ = jax.scipy.linalg.cho_factor(qm)
qfrc = d.qfrc_smooth + d.qfrc_constraint
qacc = jax.scipy.linalg.cho_solve((qh, False), qfrc)
return _advance(m, d, d.act_dot, qacc)
@named_scope @named_scope
def forward(m: Model, d: Data) -> Data: def forward(m: Model, d: Data) -> Data:
"""Forward dynamics.""" """Forward dynamics."""
@@ -377,6 +417,8 @@ def step(m: Model, d: Data) -> Data:
d = euler(m, d) d = euler(m, d)
elif m.opt.integrator == IntegratorType.RK4: elif m.opt.integrator == IntegratorType.RK4:
d = rungekutta4(m, d) d = rungekutta4(m, d)
elif m.opt.integrator == IntegratorType.IMPLICITFAST:
d = implicit(m, d)
else: else:
raise NotImplementedError(f'integrator {m.opt.integrator} not implemented.') raise NotImplementedError(f'integrator {m.opt.integrator} not implemented.')
+6
View File
@@ -68,6 +68,12 @@ class ForwardTest(absltest.TestCase):
_assert_attr_eq(d, dx, 'qpos') _assert_attr_eq(d, dx, 'qpos')
_assert_attr_eq(d, dx, 'time') _assert_attr_eq(d, dx, 'time')
# implicitfast
m.opt.integrator = mujoco.mjtIntegrator.mjINT_IMPLICITFAST
dx = jax.jit(mjx.implicit)(mx, mjx.put_data(m, d))
mujoco.mj_implicit(m, d)
_assert_attr_eq(d, dx, 'qpos')
def test_step(self): def test_step(self):
m = test_util.load_test_file('constraints.xml') m = test_util.load_test_file('constraints.xml')
d = mujoco.MjData(m) d = mujoco.MjData(m)
+6 -1
View File
@@ -47,13 +47,18 @@ def _make_option(o: mujoco.MjOption) -> types.Option:
if o.enableflags & 2**i: if o.enableflags & 2**i:
raise NotImplementedError(f'{mujoco.mjtEnableBit(2 ** i)}') raise NotImplementedError(f'{mujoco.mjtEnableBit(2 ** i)}')
has_fluid_params = o.density > 0 or o.viscosity > 0 or o.wind.any()
implicitfast = o.integrator == mujoco.mjtIntegrator.mjINT_IMPLICITFAST
if implicitfast and has_fluid_params:
raise NotImplementedError('implicitfast not implemented for fluid drag.')
fields = {f.name: getattr(o, f.name, None) for f in types.Option.fields()} fields = {f.name: getattr(o, f.name, None) for f in types.Option.fields()}
fields['integrator'] = types.IntegratorType(o.integrator) fields['integrator'] = types.IntegratorType(o.integrator)
fields['cone'] = types.ConeType(o.cone) fields['cone'] = types.ConeType(o.cone)
fields['jacobian'] = types.JacobianType(o.jacobian) fields['jacobian'] = types.JacobianType(o.jacobian)
fields['solver'] = types.SolverType(o.solver) fields['solver'] = types.SolverType(o.solver)
fields['disableflags'] = types.DisableBit(o.disableflags) fields['disableflags'] = types.DisableBit(o.disableflags)
fields['has_fluid_params'] = o.density > 0 or o.viscosity > 0 or o.wind.any() fields['has_fluid_params'] = has_fluid_params
return types.Option(**fields) return types.Option(**fields)
+8
View File
@@ -208,6 +208,14 @@ class ModelIOTest(parameterized.TestCase):
</worldbody> </worldbody>
</mujoco>""")) </mujoco>"""))
def test_implicitfast_fluid_not_implemented(self):
with self.assertRaises(NotImplementedError):
mjx.put_model(mujoco.MjModel.from_xml_string("""
<mujoco>
<option viscosity="3.0" integrator="implicitfast"/>
<worldbody/>
</mujoco>"""))
class DataIOTest(parameterized.TestCase): class DataIOTest(parameterized.TestCase):
"""IO tests for mjx.Data.""" """IO tests for mjx.Data."""
+3 -1
View File
@@ -90,10 +90,12 @@ class IntegratorType(enum.IntEnum):
Members: Members:
EULER: semi-implicit Euler EULER: semi-implicit Euler
RK4: 4th-order Runge Kutta RK4: 4th-order Runge Kutta
IMPLICITFAST: implicit in velocity, no rne derivative
""" """
EULER = mujoco.mjtIntegrator.mjINT_EULER EULER = mujoco.mjtIntegrator.mjINT_EULER
RK4 = mujoco.mjtIntegrator.mjINT_RK4 RK4 = mujoco.mjtIntegrator.mjINT_RK4
# unsupported: IMPLICIT, IMPLICITFAST IMPLICITFAST = mujoco.mjtIntegrator.mjINT_IMPLICITFAST
# unsupported: IMPLICIT
class GeomType(enum.IntEnum): class GeomType(enum.IntEnum):
+1 -1
View File
@@ -83,7 +83,7 @@
</equality> </equality>
<actuator> <actuator>
<position ctrlrange="-20 20" gear="500" joint="joint1" name="act1"/> <position ctrlrange="-20 20" gear="500" joint="joint1" kv="0.5" name="act1"/>
<motor gear="50000" joint="joint3" name="act2"/> <motor gear="50000" joint="joint3" name="act2"/>
<motor gear="75000" joint="joint4" name="act3"/> <motor gear="75000" joint="joint4" name="act3"/>
</actuator> </actuator>