Add actuator and sensor delays. Fixes #1004
PiperOrigin-RevId: 866478839 Change-Id: Id21a6da0f98454c8fa39ea5af8a5e213d6eae497
This commit is contained in:
committed by
Copybara-Service
parent
84fa527723
commit
6419534bad
@@ -300,6 +300,89 @@ Copy concatenated state components specified by ``sig`` from ``state`` into ``d
|
||||
|
||||
Copy state from src to dst.
|
||||
|
||||
.. _mj_readCtrl:
|
||||
|
||||
`mj_readCtrl <#mj_readCtrl>`__
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. mujoco-include:: mj_readCtrl
|
||||
|
||||
Read the control value for an actuator at a given time, taking delays into account. If no history buffer exists, return
|
||||
``mjData.ctrl[id]``. If a history buffer exists (:ref:`nsample<actuator-general-nsample>` > 0), read from the delay
|
||||
buffer at ``time - actuator_delay[id]`` using the requested interpolation order:
|
||||
|
||||
- ``interp = 0``: Zero-order hold (piecewise constant)
|
||||
- ``interp = 1``: Piecewise Linear
|
||||
- ``interp = 2``: Cubic Spline (Catmull-Rom)
|
||||
- ``interp = -1``: Use the actuator's :ref:`interp<actuator-general-interp>` value.
|
||||
|
||||
In all three cases, constant extrapolation outside of buffer bounds.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_readSensor:
|
||||
|
||||
`mj_readSensor <#mj_readSensor>`__
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. mujoco-include:: mj_readSensor
|
||||
|
||||
Read a sensor value at a given time, taking delays into account. If no history buffer exists, return a pointer to the
|
||||
sensor's slice of ``mjData.sensordata``. If a history buffer exists (:ref:`nsample<sensor-nsample>` > 0), read from the
|
||||
history buffer at ``time - sensor_delay[id]``. See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
**Return value semantics:**
|
||||
|
||||
- If no history buffer exists (:ref:`nsample<sensor-nsample>` = 0), returns a pointer to the sensor's slice of
|
||||
``mjData.sensordata``.
|
||||
- If a history buffer exists (:ref:`nsample<sensor-nsample>` > 0) and the requested time matches a stored sample
|
||||
(always true for ``interp = 0``), returns a pointer to the data in the history buffer.
|
||||
- If interpolation is required (``interp = 1 or 2``), returns ``NULL`` and writes the interpolated result to
|
||||
``result`` (must be of size ``dim``).
|
||||
|
||||
**Interpolation:**
|
||||
|
||||
- ``interp = 0``: Zero-order hold (piecewise constant)
|
||||
- ``interp = 1``: Piecewise Linear
|
||||
- ``interp = 2``: Cubic Spline (Catmull-Rom)
|
||||
- ``interp = -1``: Use the value in :ref:`interp<sensor-interp>`
|
||||
|
||||
In all three cases, constant extrapolation outside of buffer bounds.
|
||||
|
||||
|
||||
**Usage:**
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
// read sensor 0 of data size `dim` at time t
|
||||
mjtNum result[dim];
|
||||
const mjtNum* ptr = mj_readSensor(m, d, 0, t, result, /* interp = */ 1);
|
||||
const mjtNum* data = ptr ? ptr : result;
|
||||
|
||||
.. _mj_initCtrlHistory:
|
||||
|
||||
`mj_initCtrlHistory <#mj_initCtrlHistory>`__
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. mujoco-include:: mj_initCtrlHistory
|
||||
|
||||
Initialize the history buffer for an actuator with custom values. The ``times`` array specifies the timestamps for each
|
||||
sample (must be length :ref:`nsample<actuator-general-nsample>`), and ``values`` specifies the control values. If
|
||||
``times`` is ``NULL``, the existing timestamps in the buffer are used, and only the values are updated.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_initSensorHistory:
|
||||
|
||||
`mj_initSensorHistory <#mj_initSensorHistory>`__
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. mujoco-include:: mj_initSensorHistory
|
||||
|
||||
Initialize the history buffer for a sensor with custom values. The ``times`` array specifies the timestamps for each
|
||||
sample (must be length :ref:`nsample<sensor-nsample>`), and ``values`` specifies the sensor values (must be of size
|
||||
``nsample * dim``). If ``times`` is ``NULL``, the existing timestamps in the buffer are used.
|
||||
The ``phase`` argument sets the user slot, which stores the last computation time for interval sensors.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_setKeyframe:
|
||||
|
||||
`mj_setKeyframe <#mj_setKeyframe>`__
|
||||
|
||||
@@ -205,6 +205,69 @@ is not a subset of the bits set in ``srcsig``.
|
||||
Copy concatenated state components specified by ``sig`` from ``state`` into ``d``. The bits of the integer
|
||||
``sig`` correspond to element fields of :ref:`mjtState`. Fails with :ref:`mju_error` if ``sig`` is invalid.
|
||||
|
||||
.. _mj_readCtrl:
|
||||
|
||||
Read the control value for an actuator at a given time, taking delays into account. If no history buffer exists, return
|
||||
``mjData.ctrl[id]``. If a history buffer exists (:ref:`nsample<actuator-general-nsample>` > 0), read from the delay
|
||||
buffer at ``time - actuator_delay[id]`` using the requested interpolation order:
|
||||
|
||||
- ``interp = 0``: Zero-order hold (piecewise constant)
|
||||
- ``interp = 1``: Piecewise Linear
|
||||
- ``interp = 2``: Cubic Spline (Catmull-Rom)
|
||||
- ``interp = -1``: Use the actuator's :ref:`interp<actuator-general-interp>` value.
|
||||
|
||||
In all three cases, constant extrapolation outside of buffer bounds.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_readSensor:
|
||||
|
||||
Read a sensor value at a given time, taking delays into account. If no history buffer exists, return a pointer to the
|
||||
sensor's slice of ``mjData.sensordata``. If a history buffer exists (:ref:`nsample<sensor-nsample>` > 0), read from the
|
||||
history buffer at ``time - sensor_delay[id]``. See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
**Return value semantics:**
|
||||
|
||||
- If no history buffer exists (:ref:`nsample<sensor-nsample>` = 0), returns a pointer to the sensor's slice of
|
||||
``mjData.sensordata``.
|
||||
- If a history buffer exists (:ref:`nsample<sensor-nsample>` > 0) and the requested time matches a stored sample
|
||||
(always true for ``interp = 0``), returns a pointer to the data in the history buffer.
|
||||
- If interpolation is required (``interp = 1 or 2``), returns ``NULL`` and writes the interpolated result to
|
||||
``result`` (must be of size ``dim``).
|
||||
|
||||
**Interpolation:**
|
||||
|
||||
- ``interp = 0``: Zero-order hold (piecewise constant)
|
||||
- ``interp = 1``: Piecewise Linear
|
||||
- ``interp = 2``: Cubic Spline (Catmull-Rom)
|
||||
- ``interp = -1``: Use the value in :ref:`interp<sensor-interp>`
|
||||
|
||||
In all three cases, constant extrapolation outside of buffer bounds.
|
||||
|
||||
|
||||
**Usage:**
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
// read sensor 0 of data size `dim` at time t
|
||||
mjtNum result[dim];
|
||||
const mjtNum* ptr = mj_readSensor(m, d, 0, t, result, /* interp = */ 1);
|
||||
const mjtNum* data = ptr ? ptr : result;
|
||||
|
||||
.. _mj_initCtrlHistory:
|
||||
|
||||
Initialize the history buffer for an actuator with custom values. The ``times`` array specifies the timestamps for each
|
||||
sample (must be length :ref:`nsample<actuator-general-nsample>`), and ``values`` specifies the control values. If
|
||||
``times`` is ``NULL``, the existing timestamps in the buffer are used, and only the values are updated.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_initSensorHistory:
|
||||
|
||||
Initialize the history buffer for a sensor with custom values. The ``times`` array specifies the timestamps for each
|
||||
sample (must be length :ref:`nsample<sensor-nsample>`), and ``values`` specifies the sensor values (must be of size
|
||||
``nsample * dim``). If ``times`` is ``NULL``, the existing timestamps in the buffer are used.
|
||||
The ``phase`` argument sets the user slot, which stores the last computation time for interval sensors.
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _mj_mulJacVec:
|
||||
|
||||
This function multiplies the constraint Jacobian mjData.efc_J by a vector. Note that the Jacobian can be either dense or
|
||||
|
||||
+592
-75
File diff suppressed because it is too large
Load Diff
+753
-18
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,9 @@ Upcoming version (not yet released)
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
- Actuators and sensors now support arbitrary delays, see :ref:`Delays<CDelay>` for details. Adding
|
||||
delays introduces a new ``mjData.history`` variable to the :ref:`Physics state<siPhysicsState>`.
|
||||
|
||||
.. image:: images/changelog/poncho.png
|
||||
:width: 45%
|
||||
:align: right
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 30 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 30 KiB |
+37
-11
@@ -21,20 +21,21 @@ typedef enum mjtState_ { // state elements
|
||||
mjSTATE_QPOS = 1<<1, // position
|
||||
mjSTATE_QVEL = 1<<2, // velocity
|
||||
mjSTATE_ACT = 1<<3, // actuator activation
|
||||
mjSTATE_WARMSTART = 1<<4, // acceleration used for warmstart
|
||||
mjSTATE_CTRL = 1<<5, // control
|
||||
mjSTATE_QFRC_APPLIED = 1<<6, // applied generalized force
|
||||
mjSTATE_XFRC_APPLIED = 1<<7, // applied Cartesian force/torque
|
||||
mjSTATE_EQ_ACTIVE = 1<<8, // enable/disable constraints
|
||||
mjSTATE_MOCAP_POS = 1<<9, // positions of mocap bodies
|
||||
mjSTATE_MOCAP_QUAT = 1<<10, // orientations of mocap bodies
|
||||
mjSTATE_USERDATA = 1<<11, // user data
|
||||
mjSTATE_PLUGIN = 1<<12, // plugin state
|
||||
mjSTATE_HISTORY = 1<<4, // history buffers (control, sensor)
|
||||
mjSTATE_WARMSTART = 1<<5, // acceleration used for warmstart
|
||||
mjSTATE_CTRL = 1<<6, // control
|
||||
mjSTATE_QFRC_APPLIED = 1<<7, // applied generalized force
|
||||
mjSTATE_XFRC_APPLIED = 1<<8, // applied Cartesian force/torque
|
||||
mjSTATE_EQ_ACTIVE = 1<<9, // enable/disable constraints
|
||||
mjSTATE_MOCAP_POS = 1<<10, // positions of mocap bodies
|
||||
mjSTATE_MOCAP_QUAT = 1<<11, // orientations of mocap bodies
|
||||
mjSTATE_USERDATA = 1<<12, // user data
|
||||
mjSTATE_PLUGIN = 1<<13, // plugin state
|
||||
|
||||
mjNSTATE = 13, // number of state elements
|
||||
mjNSTATE = 14, // number of state elements
|
||||
|
||||
// convenience values for commonly used state specifications
|
||||
mjSTATE_PHYSICS = mjSTATE_QPOS | mjSTATE_QVEL | mjSTATE_ACT,
|
||||
mjSTATE_PHYSICS = mjSTATE_QPOS | mjSTATE_QVEL | mjSTATE_ACT | mjSTATE_HISTORY,
|
||||
mjSTATE_FULLPHYSICS = mjSTATE_TIME | mjSTATE_PHYSICS | mjSTATE_PLUGIN,
|
||||
mjSTATE_USER = mjSTATE_CTRL | mjSTATE_QFRC_APPLIED | mjSTATE_XFRC_APPLIED |
|
||||
mjSTATE_EQ_ACTIVE | mjSTATE_MOCAP_POS | mjSTATE_MOCAP_QUAT |
|
||||
@@ -221,6 +222,7 @@ struct mjData_ {
|
||||
mjtNum* qpos; // position (nq x 1)
|
||||
mjtNum* qvel; // velocity (nv x 1)
|
||||
mjtNum* act; // actuator activation (na x 1)
|
||||
mjtNum* history; // history buffer (nhistory x 1)
|
||||
mjtNum* qacc_warmstart; // acceleration used for warmstart (nv x 1)
|
||||
mjtNum* plugin_state; // plugin state (npluginstate x 1)
|
||||
|
||||
@@ -1101,6 +1103,7 @@ struct mjModel_ {
|
||||
mjtSize nuserdata; // number of mjtNums reserved for the user
|
||||
mjtSize nsensordata; // number of mjtNums in sensor data vector
|
||||
mjtSize npluginstate; // number of mjtNums in plugin state vector
|
||||
mjtSize nhistory; // number of mjtNums in history buffer
|
||||
|
||||
// buffer sizes
|
||||
mjtSize narena; // number of bytes in the mjData arena (inclusive of stack)
|
||||
@@ -1520,6 +1523,9 @@ struct mjModel_ {
|
||||
int* actuator_actadr; // first activation address; -1: stateless (nu x 1)
|
||||
int* actuator_actnum; // number of activation variables (nu x 1)
|
||||
int* actuator_group; // group for visibility (nu x 1)
|
||||
int* actuator_history; // history buffer: [nsample, interp] (nu x 2)
|
||||
int* actuator_historyadr; // address in history buffer; -1: none (nu x 1)
|
||||
mjtNum* actuator_delay; // delay time in seconds; 0: no delay (nu x 1)
|
||||
mjtByte* actuator_ctrllimited; // is control limited (nu x 1)
|
||||
mjtByte* actuator_forcelimited;// is force limited (nu x 1)
|
||||
mjtByte* actuator_actlimited; // is activation limited (nu x 1)
|
||||
@@ -1551,6 +1557,10 @@ struct mjModel_ {
|
||||
int* sensor_adr; // address in sensor array (nsensor x 1)
|
||||
mjtNum* sensor_cutoff; // cutoff for real and positive; 0: ignore (nsensor x 1)
|
||||
mjtNum* sensor_noise; // noise standard deviation (nsensor x 1)
|
||||
int* sensor_history; // history buffer: [nsample, interp] (nsensor x 2)
|
||||
int* sensor_historyadr; // address in history buffer; -1: none (nsensor x 1)
|
||||
mjtNum* sensor_delay; // delay time in seconds; 0: no delay (nsensor x 1)
|
||||
mjtNum* sensor_interval; // interval: [period, phase] in seconds (nsensor x 2)
|
||||
mjtNum* sensor_user; // user data (nsensor x nuser_sensor)
|
||||
int* sensor_plugin; // plugin instance id; -1: not a plugin (nsensor x 1)
|
||||
|
||||
@@ -2445,6 +2455,9 @@ typedef struct mjsActuator_ { // actuator specification
|
||||
|
||||
// other
|
||||
int group; // group
|
||||
int nsample; // number of samples in history buffer
|
||||
int interp; // interpolation order (0=ZOH, 1=linear, 2=cubic)
|
||||
double delay; // delay time in seconds; 0: no delay
|
||||
mjDoubleVec* userdata; // user data
|
||||
mjsPlugin plugin; // actuator plugin
|
||||
mjString* info; // message appended to compiler errors
|
||||
@@ -2469,6 +2482,12 @@ typedef struct mjsSensor_ { // sensor specification
|
||||
double cutoff; // cutoff for real and positive datatypes
|
||||
double noise; // noise stdev
|
||||
|
||||
// history buffer
|
||||
int nsample; // number of samples in history buffer
|
||||
int interp; // interpolation order (0=ZOH, 1=linear, 2=cubic)
|
||||
double delay; // delay time in seconds
|
||||
double interval[2]; // [period, time_prev] in seconds
|
||||
|
||||
// other
|
||||
mjDoubleVec* userdata; // user data
|
||||
mjsPlugin plugin; // sensor plugin
|
||||
@@ -3234,6 +3253,13 @@ void mj_extractState(const mjModel* m, const mjtNum* src, int srcsig,
|
||||
mjtNum* dst, int dstsig);
|
||||
void mj_setState(const mjModel* m, mjData* d, const mjtNum* state, int sig);
|
||||
void mj_copyState(const mjModel* m, const mjData* src, mjData* dst, int sig);
|
||||
mjtNum mj_readCtrl(const mjModel* m, const mjData* d, int id, mjtNum time, int interp);
|
||||
const mjtNum* mj_readSensor(const mjModel* m, const mjData* d, int id, mjtNum time,
|
||||
mjtNum* result, int interp);
|
||||
void mj_initCtrlHistory(const mjModel* m, mjData* d, int id,
|
||||
const mjtNum* times, const mjtNum* values);
|
||||
void mj_initSensorHistory(const mjModel* m, mjData* d, int id,
|
||||
const mjtNum* times, const mjtNum* values, mjtNum phase);
|
||||
void mj_setKeyframe(mjModel* m, const mjData* d, int k);
|
||||
int mj_addContact(const mjModel* m, mjData* d, const mjContact* con);
|
||||
int mj_isPyramidal(const mjModel* m);
|
||||
|
||||
@@ -1081,20 +1081,186 @@ copied in the array mjData.sensordata and are available for user processing.
|
||||
|
||||
Here we describe the XML attributes common to all sensor types, so as to avoid repetition later.
|
||||
|
||||
.. _sensor-name:
|
||||
|
||||
:at:`name`: :at-val:`string, optional`
|
||||
Name of the sensor.
|
||||
|
||||
.. _sensor-noise:
|
||||
|
||||
:at:`noise`: :at-val:`real, "0"`
|
||||
The standard deviation of the noise model of this sensor. In versions prior to 3.1.4, this would lead to noise being
|
||||
added to the sensors. In release 3.1.4 this feature was removed, see :doc:`3.1.4 changelog <changelog>` for a
|
||||
detailed justification. As of subsequent versions, this attrbute serves as a convenient location for saving standard
|
||||
deviation information for later use.
|
||||
|
||||
.. _sensor-cutoff:
|
||||
|
||||
:at:`cutoff`: :at-val:`real, "0"`
|
||||
When this value is positive, it limits the absolute value of the sensor output. It is also used to normalize the
|
||||
sensor data plots in :ref:`simulate.cc <saSimulate>`. Note that :at:`cutoff` has a different meaning for
|
||||
:ref:`collision sensors<collision-sensors>`.
|
||||
|
||||
.. _sensor-nsample:
|
||||
|
||||
:at:`nsample`: :at-val:`int, "0"`
|
||||
If :at-val:`nsample` is greater than 0, creates a time-indexed ring buffer with :at:`nsample` slots of sensor data.
|
||||
During state advancement, the current sensor data is appended to the buffer with timestamp ``time``, and the oldest
|
||||
sample is removed. Values in the history buffer can be read via :ref:`mj_readSensor`. A positive :at-val:`nsample`
|
||||
is required for both :ref:`delay<sensor-delay>` and :ref:`interval<sensor-interval>` features.
|
||||
|
||||
See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _sensor-interp:
|
||||
|
||||
:at:`interp`: :at-val:`[zoh, linear, cubic], "zoh"`
|
||||
The interpolation method used when reading from the history buffer. Corresponds to the ``interp`` argument in
|
||||
:ref:`mj_readSensor`.
|
||||
|
||||
- ``zoh``: Zero-order hold (piecewise constant).
|
||||
- ``linear``: Piecewise linear interpolation.
|
||||
- ``cubic``: Cubic spline interpolation (Catmull-Rom).
|
||||
|
||||
The :at:`interp` value is for advanced use-cases, see :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _sensor-delay:
|
||||
|
||||
:at:`delay`: :at-val:`real, "0"`
|
||||
If greater than 0, sensor values in ``mjData.sensordata`` are read from the history buffer at ``time - delay`` rather
|
||||
than computed directly. Requires positive :ref:`nsample<sensor-nsample>`, cannot be negative.
|
||||
|
||||
In the most common case, ``delay = nsample * timestep``, see :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _sensor-interval:
|
||||
|
||||
:at:`interval`: :at-val:`real, "0 0"`
|
||||
This attribute controls how often sensor values are recomputed. It is useful for modeling sensors that have a larger
|
||||
sampling period than the simulation timestep. Requires a history buffer (:ref:`nsample <sensor-nsample>` > 0).
|
||||
|
||||
This attribute is defined by two real-valued numbers, both in units of time, called :at:`interval` =
|
||||
":at-val:`period` :at-val:`phase`". It is possible to only specify the :at-val:`period`, in which case the
|
||||
:at-val:`phase` is assumed to be 0.
|
||||
|
||||
The :at-val:`period` specifies the interval period between recomputations. The default value of 0 has the special
|
||||
meaning "every simulation timestep". Note that the period is not required to be an integer multiple of the timestep.
|
||||
For example, if the simulation timestep is 1.0, and :at-val:`period` is 2.5, the sensor will be computed at times
|
||||
0.0, 3.0, 5.0, 8.0, 10.0, 13.0, ... with the actual interval alternating between 2 and 3 timesteps. :at-val:`period`
|
||||
cannot be negative. Note that only ``period > timestep`` values make sense; values smaller than or equal to the
|
||||
timestep will not lead to an error but merely cause the sensor to be recomputed at every timestep.
|
||||
|
||||
The :at-val:`phase` only takes effect during history buffer initialization in :ref:`mj_resetData`. It specifies the
|
||||
last time that the sensor was computed "before the simulation started" in continuous time (i.e., disregarding the
|
||||
quantization of timesteps). It is useful for precisely controlling the *relative phase* of sensor computation and
|
||||
simulation time, when interval is used. The default value of 0 has the special meaning ":at-val:`-period`", i.e.
|
||||
specifying that the sensor should be computed at the first timestep of the simulation. Continuing our example from
|
||||
earlier, if the timestep is 1.0 and interval is ":at-val:`2.5 -1.5`", the sensor will be computed at times 1.0, 4.0,
|
||||
6.0, 9.0, 11.0, 14.0, etc. :at-val:`phase` must be in the range :math:`(-\text{period}, 0]`.
|
||||
|
||||
:at:`user`: :at-val:`real(nuser_sensor), "0 0 ..."`
|
||||
See :ref:`User parameters <CUser>`.
|
||||
|
||||
.. _CDelay:
|
||||
|
||||
Delays
|
||||
~~~~~~
|
||||
|
||||
Both actuators and sensors support time delays via a ring buffer that stores timestamped samples. When the integer
|
||||
attribute :at:`nsample` (:ref:`actuators<actuator-general-nsample>`, :ref:`sensors<sensor-nsample>`) is positive, a
|
||||
buffer with :at:`nsample` slots is included in the :ref:`physics state<siPhysicsState>` component ``mjData.history``
|
||||
and the samples and current timestamps are written into the buffer upon state advancement.
|
||||
|
||||
If additionally the real-valued :at:`delay` attribute (:ref:`actuators<actuator-general-delay>`,
|
||||
:ref:`sensors<sensor-delay>`) is positive, then during the forward dynamics the control or sensor values are read from
|
||||
the history buffer (instead of being read from ``ctrl`` or recomputed, respectively). Positive :at:`delay` requires
|
||||
positive :at:`nsample`.
|
||||
|
||||
Note that since reading happens before writing, the minimum positive delay is effectively one timestep, despite
|
||||
:at:`delay` being real-valued.
|
||||
|
||||
Delayed reading in the engine is triggered by positive :at:`delay`, and performed by the API functions
|
||||
:ref:`mj_readCtrl` and :ref:`mj_readSensor`, which read from the buffer at ``time - delay``, effectively implementing
|
||||
the requested delay. These functions take ``time`` as an argument and can be used whenever :at:`nsample` is positive,
|
||||
allowing the user to inspect the contents of the history buffer at any time, including in a "history-only" mode
|
||||
(:at:`nsample` > 0, :at:`delay` = 0), where past values are accessible via the API but the simulation is unaffected.
|
||||
|
||||
**Sensor Modes**
|
||||
|
||||
Sensors support both :ref:`delay<sensor-delay>` and :ref:`interval<sensor-interval>` attributes.
|
||||
The combination determines behavior:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 10 10 80
|
||||
|
||||
* - delay
|
||||
- interval
|
||||
- Write / Read behavior
|
||||
* - = 0
|
||||
- = 0
|
||||
- History-only: computed every step, written to ``sensordata``, pushed into history buffer
|
||||
* - > 0
|
||||
- = 0
|
||||
- Delayed: computed every step, ``sensordata`` contains delayed reading (read from buffer)
|
||||
* - = 0
|
||||
- > 0
|
||||
- Periodic: computed on interval, ``sensordata`` contains last computed value (no delay)
|
||||
* - > 0
|
||||
- > 0
|
||||
- Periodic + Delayed: computed on interval, ``sensordata`` contains delayed reading (read from buffer)
|
||||
|
||||
**Initialization**
|
||||
|
||||
History buffers are initialized by :ref:`mj_resetData` as follows:
|
||||
|
||||
- **Values**: Always initialized to zero. For custom initialization after reset, call :ref:`mj_initCtrlHistory`
|
||||
and :ref:`mj_initSensorHistory`.
|
||||
|
||||
- **Actuator timestamps**: ``[..., -2*dt, -dt]``.
|
||||
|
||||
- **Sensor timestamps** without :ref:`interval<sensor-interval>`: ``[..., -2*dt, -dt]``.
|
||||
|
||||
- **Sensor timestamps** with :ref:`interval<sensor-interval>`: Samples are spaced at ``period`` intervals rather than
|
||||
``dt``. The continuous-time timestamps ``[..., phase-2*period, phase-period, phase]`` are rounded up to the nearest
|
||||
multiple of ``dt``, since that is when samples could have been computed. If ``phase = 0`` (the default), it is
|
||||
interpreted as ``-period``, meaning the first sample will be computed at ``t = 0``.
|
||||
|
||||
**Causality and interpolation**
|
||||
|
||||
The most common positive delay value is ``delay = timestep * nsample``, which implements a simple
|
||||
history buffer, with no causality issues.
|
||||
|
||||
.. warning::
|
||||
|
||||
If ``delay > timestep * nsample``, then data will be read before the earliest buffer bound, resulting in non-causal
|
||||
extrapolation: using a value from before it was actually recorded. This scenario will not lead to a runtime error,
|
||||
so it is up to the user to avoid it.
|
||||
|
||||
Setting ``delay < timestep * nsample`` is not problematic and can be useful for system identification and stochastic
|
||||
delays. In these use cases, one should choose a maximum possible ``delay_max`` and set ``nsample = ceil(delay_max /
|
||||
timestep)``. Then at run-time or sysID-time, the :ref:`mjModel` fields ``actuator_delay`` or ``sensor_delay`` can be
|
||||
safely modified, so long as ``delay_max`` is not exceeded.
|
||||
|
||||
.. image:: images/modeling/delay_buffer_light.svg
|
||||
:width: 50%
|
||||
:align: right
|
||||
:class: only-light
|
||||
|
||||
.. image:: images/modeling/delay_buffer_dark.svg
|
||||
:width: 50%
|
||||
:align: right
|
||||
:class: only-dark
|
||||
|
||||
These two use cases are the reason for including the :at:`interp` attribute (:ref:`actuators<actuator-general-interp>`,
|
||||
:ref:`sensors<sensor-interp>`). While real-world exogenous delays are generally a zero-order-hold phenomenon, this
|
||||
implies discontinuity: a small change in the delay has no effect, until the timestep threshold is crossed. For example
|
||||
with ``dt = 0.1`` and ``nsample = 2``, there is no functional difference between ``delay = 0.2`` and ``delay = 0.101``
|
||||
(both read from the oldest sample), but stepping from ``delay = 0.101`` to ``delay = 0.1`` crosses a threshold and
|
||||
changes behavior. By allowing higher order interpolation, the effect of delays becomes continuous (``interp = linear``)
|
||||
and differentiable (``interp = cubic``).
|
||||
|
||||
Note that interpolation does not makes sense for some types of sensors, for example sensors that report integer values
|
||||
(e.g. :ref:`insidesite<sensor-insidesite>`).
|
||||
|
||||
.. _CCamera:
|
||||
|
||||
Cameras
|
||||
|
||||
@@ -264,7 +264,7 @@ individual components and combinations of components. These are:
|
||||
Physics state
|
||||
"""""""""""""
|
||||
The *physics state* (:ref:`mjSTATE_PHYSICS<mjtState>`) contains the main quantities which are time-integrated during
|
||||
stepping. These are ``mjData.{qpos, qvel, act}``:
|
||||
stepping. These are ``mjData.{qpos, qvel, act, history}``:
|
||||
|
||||
Position: ``qpos``
|
||||
The configuration in generalized coodinates, denoted in the :ref:`Numerical Integration<geIntegration>` section as
|
||||
@@ -281,6 +281,11 @@ Actuator activation: ``act``
|
||||
actuators (such as biological muscles) that have their own activation states assembled in ``mjData.act``, denoted
|
||||
as :math:`w` in the :ref:`Numerical Integration<geIntegration>` section.
|
||||
|
||||
History buffer: ``history``
|
||||
When actuators or sensors have a positive :at:`nsample` attribute (:ref:`actuators<actuator-general-nsample>`,
|
||||
:ref:`sensors<sensor-nsample>`), this buffer stores timestamped samples of previous
|
||||
control or sensor values. See :ref:`Delays<CDelay>` for details.
|
||||
|
||||
.. _siFullPhysics:
|
||||
|
||||
Full physics state
|
||||
|
||||
Reference in New Issue
Block a user