Add actuator and sensor delays. Fixes #1004

PiperOrigin-RevId: 866478839
Change-Id: Id21a6da0f98454c8fa39ea5af8a5e213d6eae497
This commit is contained in:
Yuval Tassa
2026-02-06 08:46:45 -08:00
committed by Copybara-Service
parent 84fa527723
commit 6419534bad
48 changed files with 6282 additions and 219 deletions
+13 -11
View File
@@ -29,20 +29,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 |
@@ -255,6 +256,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)
+8
View File
@@ -761,6 +761,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)
@@ -1180,6 +1181,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)
@@ -1211,6 +1215,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)
+9
View File
@@ -698,6 +698,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
@@ -724,6 +727,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
+9
View File
@@ -153,6 +153,7 @@
X( nuserdata ) \
X( nsensordata ) \
X( npluginstate ) \
X( nhistory ) \
X( narena ) \
X( nbuffer )
@@ -563,6 +564,9 @@
X ( int, actuator_actadr, nu, 1 ) \
X ( int, actuator_actnum, nu, 1 ) \
X ( int, actuator_group, nu, 1 ) \
X ( int, actuator_history, nu, 2 ) \
X ( int, actuator_historyadr, nu, 1 ) \
X ( mjtNum, actuator_delay, nu, 1 ) \
X ( mjtByte, actuator_ctrllimited, nu, 1 ) \
X ( mjtByte, actuator_forcelimited, nu, 1 ) \
X ( mjtByte, actuator_actlimited, nu, 1 ) \
@@ -594,6 +598,10 @@
X ( int, sensor_adr, nsensor, 1 ) \
X ( mjtNum, sensor_cutoff, nsensor, 1 ) \
X ( mjtNum, sensor_noise, nsensor, 1 ) \
X ( int, sensor_history, nsensor, 2 ) \
X ( int, sensor_historyadr, nsensor, 1 ) \
X ( mjtNum, sensor_delay, nsensor, 1 ) \
X ( mjtNum, sensor_interval, nsensor, 2 ) \
X ( mjtNum, sensor_user, nsensor, MJ_M(nuser_sensor) ) \
X ( int, sensor_plugin, nsensor, 1 )
@@ -708,6 +716,7 @@
X ( mjtNum, qpos, nq, 1 ) \
X ( mjtNum, qvel, nv, 1 ) \
X ( mjtNum, act, na, 1 ) \
X ( mjtNum, history, nhistory, 1 ) \
X ( mjtNum, qacc_warmstart, nv, 1 ) \
X ( mjtNum, plugin_state, npluginstate, 1 ) \
X ( mjtNum, ctrl, nu, 1 ) \
+23
View File
@@ -493,6 +493,29 @@ MJAPI void mj_setState(const mjModel* m, mjData* d, const mjtNum* state, int sig
// Copy state from src to dst.
MJAPI void mj_copyState(const mjModel* m, const mjData* src, mjData* dst, int sig);
// Read ctrl value for actuator at given time.
// Returns d->ctrl[id] if no history, otherwise reads from history buffer.
// interp: 0=zero-order-hold, 1=linear, 2=cubic spline.
MJAPI mjtNum mj_readCtrl(const mjModel* m, const mjData* d, int id, mjtNum time, int interp);
// Read sensor value from history buffer at given time.
// Returns pointer to sensordata (no history) or history buffer (exact match),
// or NULL if interpolation performed (writes to result).
// interp: 0=zero-order-hold, 1=linear, 2=cubic spline.
MJAPI const mjtNum* mj_readSensor(const mjModel* m, const mjData* d, int id, mjtNum time,
mjtNum* result, int interp);
// Initialize history buffer for actuator; if times is NULL, uses existing buffer timestamps.
// Nullable: times
MJAPI void mj_initCtrlHistory(const mjModel* m, mjData* d, int id,
const mjtNum* times, const mjtNum* values);
// Initialize history buffer for sensor; if times is NULL, uses existing buffer timestamps.
// phase sets the user slot (last computation time for interval sensors).
// Nullable: times
MJAPI void mj_initSensorHistory(const mjModel* m, mjData* d, int id,
const mjtNum* times, const mjtNum* values, mjtNum phase);
// Copy current state to the k-th model keyframe.
MJAPI void mj_setKeyframe(mjModel* m, const mjData* d, int k);