Add mj_geomDistance API function and sensors for geometric distance, normal direction and segment between between two geoms. Fixes #51
PiperOrigin-RevId: 632458301 Change-Id: I65b7e5bb0df59008028114cf4a07e0d6365ff3d3
This commit is contained in:
committed by
Copybara-Service
parent
31de1dad9d
commit
02d015458c
@@ -57,8 +57,8 @@ Main simulation
|
|||||||
|
|
||||||
These are the main entry points to the simulator. Most users will only need to call :ref:`mj_step`, which computes
|
These are the main entry points to the simulator. Most users will only need to call :ref:`mj_step`, which computes
|
||||||
everything and advanced the simulation state by one time step. Controls and applied forces must either be set in advance
|
everything and advanced the simulation state by one time step. Controls and applied forces must either be set in advance
|
||||||
(in mjData.{ctrl, qfrc_applied, xfrc_applied}), or a control callback :ref:`mjcb_control` must be installed which will be
|
(in mjData.{ctrl, qfrc_applied, xfrc_applied}), or a control callback :ref:`mjcb_control` must be installed which will
|
||||||
called just before the controls and applied forces are needed. Alternatively, one can use :ref:`mj_step1` and
|
be called just before the controls and applied forces are needed. Alternatively, one can use :ref:`mj_step1` and
|
||||||
:ref:`mj_step2` which break down the simulation pipeline into computations that are executed before and after the
|
:ref:`mj_step2` which break down the simulation pipeline into computations that are executed before and after the
|
||||||
controls are needed; in this way one can set controls that depend on the results from :ref:`mj_step1`. Keep in mind
|
controls are needed; in this way one can set controls that depend on the results from :ref:`mj_step1`. Keep in mind
|
||||||
though that the RK4 solver does not work with mj_step1/2.
|
though that the RK4 solver does not work with mj_step1/2.
|
||||||
@@ -72,8 +72,8 @@ be set before calling this function. Given the state (qpos, qvel, act), mj_forwa
|
|||||||
while mj_inverse maps from acceleration to force. Mathematically these functions are inverse of each other, but
|
while mj_inverse maps from acceleration to force. Mathematically these functions are inverse of each other, but
|
||||||
numerically this may not always be the case because the forward dynamics rely on a constraint optimization algorithm
|
numerically this may not always be the case because the forward dynamics rely on a constraint optimization algorithm
|
||||||
which is usually terminated early. The difference between the results of forward and inverse dynamics can be computed
|
which is usually terminated early. The difference between the results of forward and inverse dynamics can be computed
|
||||||
with the function :ref:`mj_compareFwdInv`, which can be thought of as another solver accuracy check (as well as a general
|
with the function :ref:`mj_compareFwdInv`, which can be thought of as another solver accuracy check (as well as a
|
||||||
sanity check).
|
general sanity check).
|
||||||
|
|
||||||
The skip version of :ref:`mj_forward` and :ref:`mj_inverse` are useful for example when qpos was unchanged but qvel was
|
The skip version of :ref:`mj_forward` and :ref:`mj_inverse` are useful for example when qpos was unchanged but qvel was
|
||||||
changed (usually in the context of finite differencing). Then there is no point repeating the computations that only
|
changed (usually in the context of finite differencing). Then there is no point repeating the computations that only
|
||||||
@@ -408,6 +408,28 @@ Compute object 6D acceleration (rot:lin) in object-centered frame, world/local o
|
|||||||
sensors are not present in the model, :ref:`mj_rnePostConstraint` must be manually called in order to calculate
|
sensors are not present in the model, :ref:`mj_rnePostConstraint` must be manually called in order to calculate
|
||||||
mjData.cacc -- the total body acceleration, including contributions from the constraint solver.
|
mjData.cacc -- the total body acceleration, including contributions from the constraint solver.
|
||||||
|
|
||||||
|
.. _mj_geomDistance:
|
||||||
|
|
||||||
|
mj_geomDistance
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. mujoco-include:: mj_geomDistance
|
||||||
|
|
||||||
|
Returns the smallest signed distance between two geoms and optionally the segment from ``geom1`` to ``geom2``.
|
||||||
|
Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is
|
||||||
|
found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0).
|
||||||
|
|
||||||
|
.. admonition:: Positive ``distmax`` values
|
||||||
|
:class: note
|
||||||
|
|
||||||
|
.. TODO: b/339596989 - Improve mjc_Convex.
|
||||||
|
|
||||||
|
For some colliders, a large, positive ``distmax`` will result in an accurate measurement. However, for collision
|
||||||
|
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely innacurate.
|
||||||
|
This is considered a bug to be fixed in a future release.
|
||||||
|
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
|
||||||
|
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
|
||||||
|
|
||||||
.. _mj_contactForce:
|
.. _mj_contactForce:
|
||||||
|
|
||||||
mj_contactForce
|
mj_contactForce
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ The model and all files referenced in it can be loaded from disk or from a VFS w
|
|||||||
|
|
||||||
These are the main entry points to the simulator. Most users will only need to call :ref:`mj_step`, which computes
|
These are the main entry points to the simulator. Most users will only need to call :ref:`mj_step`, which computes
|
||||||
everything and advanced the simulation state by one time step. Controls and applied forces must either be set in advance
|
everything and advanced the simulation state by one time step. Controls and applied forces must either be set in advance
|
||||||
(in mjData.{ctrl, qfrc_applied, xfrc_applied}), or a control callback :ref:`mjcb_control` must be installed which will be
|
(in mjData.{ctrl, qfrc_applied, xfrc_applied}), or a control callback :ref:`mjcb_control` must be installed which will
|
||||||
called just before the controls and applied forces are needed. Alternatively, one can use :ref:`mj_step1` and
|
be called just before the controls and applied forces are needed. Alternatively, one can use :ref:`mj_step1` and
|
||||||
:ref:`mj_step2` which break down the simulation pipeline into computations that are executed before and after the
|
:ref:`mj_step2` which break down the simulation pipeline into computations that are executed before and after the
|
||||||
controls are needed; in this way one can set controls that depend on the results from :ref:`mj_step1`. Keep in mind
|
controls are needed; in this way one can set controls that depend on the results from :ref:`mj_step1`. Keep in mind
|
||||||
though that the RK4 solver does not work with mj_step1/2.
|
though that the RK4 solver does not work with mj_step1/2.
|
||||||
@@ -48,8 +48,8 @@ be set before calling this function. Given the state (qpos, qvel, act), mj_forwa
|
|||||||
while mj_inverse maps from acceleration to force. Mathematically these functions are inverse of each other, but
|
while mj_inverse maps from acceleration to force. Mathematically these functions are inverse of each other, but
|
||||||
numerically this may not always be the case because the forward dynamics rely on a constraint optimization algorithm
|
numerically this may not always be the case because the forward dynamics rely on a constraint optimization algorithm
|
||||||
which is usually terminated early. The difference between the results of forward and inverse dynamics can be computed
|
which is usually terminated early. The difference between the results of forward and inverse dynamics can be computed
|
||||||
with the function :ref:`mj_compareFwdInv`, which can be thought of as another solver accuracy check (as well as a general
|
with the function :ref:`mj_compareFwdInv`, which can be thought of as another solver accuracy check (as well as a
|
||||||
sanity check).
|
general sanity check).
|
||||||
|
|
||||||
The skip version of :ref:`mj_forward` and :ref:`mj_inverse` are useful for example when qpos was unchanged but qvel was
|
The skip version of :ref:`mj_forward` and :ref:`mj_inverse` are useful for example when qpos was unchanged but qvel was
|
||||||
changed (usually in the context of finite differencing). Then there is no point repeating the computations that only
|
changed (usually in the context of finite differencing). Then there is no point repeating the computations that only
|
||||||
@@ -180,6 +180,23 @@ generalized velocities to subtree angular momentum. More precisely if :math:`h`
|
|||||||
body index ``body`` in ``mjData.subtree_angmom`` (reported by the :ref:`subtreeangmom<sensor-subtreeangmom>` sensor)
|
body index ``body`` in ``mjData.subtree_angmom`` (reported by the :ref:`subtreeangmom<sensor-subtreeangmom>` sensor)
|
||||||
and :math:`\dot q` is the generalized velocity ``mjData.qvel``, then :math:`h = H \dot q`.
|
and :math:`\dot q` is the generalized velocity ``mjData.qvel``, then :math:`h = H \dot q`.
|
||||||
|
|
||||||
|
.. _mj_geomDistance:
|
||||||
|
|
||||||
|
Returns the smallest signed distance between two geoms and optionally the segment from ``geom1`` to ``geom2``.
|
||||||
|
Returned distances are bounded from above by ``distmax``. |br| If no collision of distance smaller than ``distmax`` is
|
||||||
|
found, the function will return ``distmax`` and ``fromto``, if given, will be set to (0, 0, 0, 0, 0, 0).
|
||||||
|
|
||||||
|
.. admonition:: Positive ``distmax`` values
|
||||||
|
:class: note
|
||||||
|
|
||||||
|
.. TODO: b/339596989 - Improve mjc_Convex.
|
||||||
|
|
||||||
|
For some colliders, a large, positive ``distmax`` will result in an accurate measurement. However, for collision
|
||||||
|
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely innacurate.
|
||||||
|
This is considered a bug to be fixed in a future release.
|
||||||
|
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
|
||||||
|
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
|
||||||
|
|
||||||
.. _mj_mulM:
|
.. _mj_mulM:
|
||||||
|
|
||||||
This function multiplies the joint-space inertia matrix stored in mjData.qM by a vector. qM has a custom sparse format
|
This function multiplies the joint-space inertia matrix stored in mjData.qM by a vector. qM has a custom sparse format
|
||||||
|
|||||||
@@ -6723,6 +6723,186 @@ The presence of this sensor in a model triggers a call to :ref:`mj_subtreeVel` d
|
|||||||
:at:`body`: :at-val:`string, required`
|
:at:`body`: :at-val:`string, required`
|
||||||
Name of the body where the kinematic subtree is rooted.
|
Name of the body where the kinematic subtree is rooted.
|
||||||
|
|
||||||
|
.. _collision-sensors:
|
||||||
|
|
||||||
|
collision sensors
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following 3 sensor types, :ref:`sensor/distance<sensor-distance>`, :ref:`sensor/normal<sensor-normal>` and
|
||||||
|
:ref:`sensor/fromto<sensor-fromto>`, respectively measure the distance, normal direction and line segment of the
|
||||||
|
smallest signed distance between the surfaces of two geoms using the narrow-phase geom-geom colliders. The collision
|
||||||
|
computation is always performed, independently of the standard collision :ref:`selection and filtering<coSelection>`
|
||||||
|
pipeline. These 3 sensors share some common properties:
|
||||||
|
|
||||||
|
.. _collision-sensors-cutoff:
|
||||||
|
|
||||||
|
:at:`cutoff`
|
||||||
|
For most sensors, the :at:`cutoff` attribute simply defines a clipping operation on sensor values. For collision
|
||||||
|
sensors, it defines the maximum distance at which collisions will be detected, corresponding to the ``dismax``
|
||||||
|
argument of :ref:`mj_geomDistance`. For example, at the default value of 0, only negative distances (corresponding
|
||||||
|
to geom-geom penetration) will be reported by :ref:`sensor/distance<sensor-distance>`.
|
||||||
|
In order to determine collision properties of non-penetrating geom pairs, a positive :at:`cutoff` is required.
|
||||||
|
|
||||||
|
.. admonition:: Positive cutoff values
|
||||||
|
:class: note
|
||||||
|
|
||||||
|
.. TODO: b/339596989 - Improve mjc_Convex.
|
||||||
|
|
||||||
|
For some colliders, a positive :at:`cutoff` will result in an accurate measurement. However, for collision
|
||||||
|
pairs which use the general ``mjc_Convex`` collider, the result will be approximate and likely innacurate.
|
||||||
|
This is considered a bug to be fixed in a future release.
|
||||||
|
In order to determine whether a geom pair uses ``mjc_Convex``, inspect the table at the top of
|
||||||
|
`engine_collision_driver.c <https://github.com/google-deepmind/mujoco/blob/main/src/engine/engine_collision_driver.c>`__.
|
||||||
|
|
||||||
|
:at:`geom1`, :at:`geom2`, :at:`body1`, :at:`body2`
|
||||||
|
For all 3 collision sensor types, the two colliding geoms can be specified explicitly using the :at:`geom1` and
|
||||||
|
:at:`geom2` attributes or implicitly, using :at:`body1`, :at:`body2`. In the latter case the sensor will iterate over
|
||||||
|
all geoms of the specified body or bodies (mixed specification like :at:`geom1`, :at:`body2` are allowed), and
|
||||||
|
select the collision with the smallest signed distance.
|
||||||
|
|
||||||
|
sequential sensors
|
||||||
|
When multiple collision sensors are defined sequentially and have identical attributes (:at:`geom1`, :at:`body1`,
|
||||||
|
:at:`geom2`, :at:`body2`, :at:`cutoff`), for example when both distance and normal are queried for the same geom
|
||||||
|
pair, the collision functions will be called once for the whole sensor block, avoiding repeated computation.
|
||||||
|
|
||||||
|
.. _sensor-distance:
|
||||||
|
|
||||||
|
:el-prefix:`sensor/` |-| **distance** (*)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This element creates a sensor that returns the smallest signed distance between the surfaces of two geoms.
|
||||||
|
See :ref:`collision-sensors` for more details about sensors of this type.
|
||||||
|
|
||||||
|
.. _sensor-distance-cutoff:
|
||||||
|
|
||||||
|
:at:`cutoff`
|
||||||
|
See :ref:`collision-sensors` for the sematics of this attribute, which is different than for other sensor catagories.
|
||||||
|
If no collision is detected, the distance sensor returns the :at:`cutoff` value, so in this case
|
||||||
|
:at:`cutoff` acts as a maximum clipping value, in addition to the special semantics.
|
||||||
|
|
||||||
|
.. _sensor-distance-geom1:
|
||||||
|
|
||||||
|
:at:`geom1`: :at-val:`string, optional`
|
||||||
|
Name of the first geom. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-distance-geom2:
|
||||||
|
|
||||||
|
:at:`geom2`: :at-val:`string, optional`
|
||||||
|
Name of the second geom. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-distance-body1:
|
||||||
|
|
||||||
|
:at:`body1`: :at-val:`string, optional`
|
||||||
|
Name of the first body. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-distance-body2:
|
||||||
|
|
||||||
|
:at:`body2`: :at-val:`string, optional`
|
||||||
|
Name of the second body. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-distance-name:
|
||||||
|
|
||||||
|
.. _sensor-distance-noise:
|
||||||
|
|
||||||
|
.. _sensor-distance-user:
|
||||||
|
|
||||||
|
:at:`name`, :at:`noise`, :at:`user`
|
||||||
|
See :ref:`CSensor`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _sensor-normal:
|
||||||
|
|
||||||
|
:el-prefix:`sensor/` |-| **normal** (*)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This element creates a sensor that returns the normal direction of the smallest signed distance between the surfaces of
|
||||||
|
two geoms. It is guaranteed to point from the surface of geom1 to the surface of geom2, though note that in the case of
|
||||||
|
penetration, this direction is generally in the opposite direction to that of the centroids.
|
||||||
|
See :ref:`collision-sensors` for more details about sensors of this type.
|
||||||
|
|
||||||
|
.. _sensor-normal-cutoff:
|
||||||
|
|
||||||
|
:at:`cutoff`
|
||||||
|
See :ref:`collision-sensors` for the sematics of this attribute, which is different than for other sensor catagories.
|
||||||
|
If no collision is detected, the :ref:`normal<sensor-normal>` sensor returns (0, 0, 0), otherwise it returns a
|
||||||
|
normalized direction vector. For this sensor, :at:`cutoff` does not lead to any clamping.
|
||||||
|
|
||||||
|
.. _sensor-normal-geom1:
|
||||||
|
|
||||||
|
:at:`geom1`: :at-val:`string, optional`
|
||||||
|
Name of the first geom. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-normal-geom2:
|
||||||
|
|
||||||
|
:at:`geom2`: :at-val:`string, optional`
|
||||||
|
Name of the second geom. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-normal-body1:
|
||||||
|
|
||||||
|
:at:`body1`: :at-val:`string, optional`
|
||||||
|
Name of the first body. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-normal-body2:
|
||||||
|
|
||||||
|
:at:`body2`: :at-val:`string, optional`
|
||||||
|
Name of the second body. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-normal-name:
|
||||||
|
|
||||||
|
.. _sensor-normal-noise:
|
||||||
|
|
||||||
|
.. _sensor-normal-user:
|
||||||
|
|
||||||
|
:at:`name`, :at:`noise`, :at:`user`
|
||||||
|
See :ref:`CSensor`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _sensor-fromto:
|
||||||
|
|
||||||
|
:el-prefix:`sensor/` |-| **fromto** (*)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This element creates a sensor that returns the segment defining the smallest signed distance between the surfaces of two
|
||||||
|
geoms. The segment is defined by 6 numbers (x1, y1, z1, x2, y2, z2) corresponding to two points in the world frame.
|
||||||
|
(x1, y1, z1) is on the surface of geom1, (x2, y2, z2) is on the surface of geom2. When this sensor is present and the
|
||||||
|
:ref:`mjVIS_RANGEFINDER<mjtVisFlag>` visualization flag is set, segments will be visualized as rangefinder rays.
|
||||||
|
See :ref:`collision-sensors` for more details about sensors of this type.
|
||||||
|
|
||||||
|
.. _sensor-fromto-cutoff:
|
||||||
|
|
||||||
|
:at:`cutoff`
|
||||||
|
See :ref:`collision-sensors` for the sematics of this attribute, which is different than for other sensor catagories.
|
||||||
|
If no collision is detected, the :ref:`fromto<sensor-fromto>` sensor returns 6 zeros.
|
||||||
|
For this sensor, :at:`cutoff` does not lead to any clamping.
|
||||||
|
|
||||||
|
.. _sensor-fromto-geom1:
|
||||||
|
|
||||||
|
:at:`geom1`: :at-val:`string, optional`
|
||||||
|
Name of the first geom. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-fromto-geom2:
|
||||||
|
|
||||||
|
:at:`geom2`: :at-val:`string, optional`
|
||||||
|
Name of the second geom. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-fromto-body1:
|
||||||
|
|
||||||
|
:at:`body1`: :at-val:`string, optional`
|
||||||
|
Name of the first body. Exactly one of (:at:`geom1`, :at:`body1`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-fromto-body2:
|
||||||
|
|
||||||
|
:at:`body2`: :at-val:`string, optional`
|
||||||
|
Name of the second body. Exactly one of (:at:`geom2`, :at:`body2`) must be specified.
|
||||||
|
|
||||||
|
.. _sensor-fromto-name:
|
||||||
|
|
||||||
|
.. _sensor-fromto-noise:
|
||||||
|
|
||||||
|
.. _sensor-fromto-user:
|
||||||
|
|
||||||
|
:at:`name`, :at:`noise`, :at:`user`
|
||||||
|
See :ref:`CSensor`.
|
||||||
|
|
||||||
.. _sensor-clock:
|
.. _sensor-clock:
|
||||||
|
|
||||||
|
|||||||
@@ -1163,6 +1163,33 @@
|
|||||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| |_| sensor |br| |_| |L| | | .. table:: |
|
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||||
|
| :ref:`distance | \* | :class: mjcf-attributes |
|
||||||
|
| <sensor-distance>` | | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`name<sensor-distance-name>` | :ref:`geom1<sensor-distance-geom1>` | :ref:`geom2<sensor-distance-geom2>` | :ref:`body1<sensor-distance-body1>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`body2<sensor-distance-body2>` | :ref:`cutoff<sensor-distance-cutoff>` | :ref:`noise<sensor-distance-noise>` | :ref:`user<sensor-distance-user>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||||
|
| :ref:`normal | \* | :class: mjcf-attributes |
|
||||||
|
| <sensor-normal>` | | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`name<sensor-normal-name>` | :ref:`geom1<sensor-normal-geom1>` | :ref:`geom2<sensor-normal-geom2>` | :ref:`body1<sensor-normal-body1>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`body2<sensor-normal-body2>` | :ref:`cutoff<sensor-normal-cutoff>` | :ref:`noise<sensor-normal-noise>` | :ref:`user<sensor-normal-user>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||||
|
| :ref:`fromto | \* | :class: mjcf-attributes |
|
||||||
|
| <sensor-fromto>` | | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`name<sensor-fromto-name>` | :ref:`geom1<sensor-fromto-geom1>` | :ref:`geom2<sensor-fromto-geom2>` | :ref:`body1<sensor-fromto-body1>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
| | | | :ref:`body2<sensor-fromto-body2>` | :ref:`cutoff<sensor-fromto-cutoff>` | :ref:`noise<sensor-fromto-noise>` | :ref:`user<sensor-fromto-user>` | |
|
||||||
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| |_| sensor |br| |_| |L| | | .. table:: |
|
||||||
| :ref:`clock | \* | :class: mjcf-attributes |
|
| :ref:`clock | \* | :class: mjcf-attributes |
|
||||||
| <sensor-clock>` | | |
|
| <sensor-clock>` | | |
|
||||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||||
|
|||||||
+9
-2
@@ -5,12 +5,19 @@ Changelog
|
|||||||
Upcoming version (not yet released)
|
Upcoming version (not yet released)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
General
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
1. Added :ref:`mj_geomDistance` for computing the shortest signed distance between two geoms and optionally a segment
|
||||||
|
connecting them. Relatedly, added the 3 sensors: :ref:`distance<sensor-distance>`, :ref:`normal<sensor-normal>`,
|
||||||
|
:ref:`fromto<sensor-fromto>`. See the function and sensor documentation for details. Fixes :github:issue:`51`.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
1. Fixed a bug the could cause collisions to be missed when :ref:`fusestatic<compiler-fusestatic>` is enabled, as is
|
2. Fixed a bug the could cause collisions to be missed when :ref:`fusestatic<compiler-fusestatic>` is enabled, as is
|
||||||
often the case for URDF imports. Fixes :github:issue:`1069`, :github:issue:`1577`.
|
often the case for URDF imports. Fixes :github:issue:`1069`, :github:issue:`1577`.
|
||||||
2. Fixed a bug that was causing the visualization of SDF iterations to write outside the size of the vector storing
|
3. Fixed a bug that was causing the visualization of SDF iterations to write outside the size of the vector storing
|
||||||
them. Fixes :github:issue:`1539`.
|
them. Fixes :github:issue:`1539`.
|
||||||
|
|
||||||
Version 3.1.5 (May 7, 2024)
|
Version 3.1.5 (May 7, 2024)
|
||||||
|
|||||||
@@ -643,6 +643,11 @@ typedef enum mjtSensor_ { // type of sensor
|
|||||||
mjSENS_SUBTREELINVEL, // 3D linear velocity of subtree
|
mjSENS_SUBTREELINVEL, // 3D linear velocity of subtree
|
||||||
mjSENS_SUBTREEANGMOM, // 3D angular momentum of subtree
|
mjSENS_SUBTREEANGMOM, // 3D angular momentum of subtree
|
||||||
|
|
||||||
|
// sensors for geometric distance; attached to geoms or bodies
|
||||||
|
mjSENS_GEOMDIST, // signed distance between two geoms
|
||||||
|
mjSENS_GEOMNORMAL, // normal direction between two geoms
|
||||||
|
mjSENS_GEOMFROMTO, // segment between two geoms
|
||||||
|
|
||||||
// global sensors
|
// global sensors
|
||||||
mjSENS_CLOCK, // simulation time
|
mjSENS_CLOCK, // simulation time
|
||||||
|
|
||||||
@@ -2542,6 +2547,8 @@ void mj_objectVelocity(const mjModel* m, const mjData* d,
|
|||||||
int objtype, int objid, mjtNum res[6], int flg_local);
|
int objtype, int objid, mjtNum res[6], int flg_local);
|
||||||
void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
||||||
int objtype, int objid, mjtNum res[6], int flg_local);
|
int objtype, int objid, mjtNum res[6], int flg_local);
|
||||||
|
mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
||||||
|
mjtNum distmax, mjtNum fromto[6]);
|
||||||
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||||
void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
||||||
const mjtNum* qpos1, const mjtNum* qpos2);
|
const mjtNum* qpos1, const mjtNum* qpos2);
|
||||||
|
|||||||
@@ -327,6 +327,11 @@ typedef enum mjtSensor_ { // type of sensor
|
|||||||
mjSENS_SUBTREELINVEL, // 3D linear velocity of subtree
|
mjSENS_SUBTREELINVEL, // 3D linear velocity of subtree
|
||||||
mjSENS_SUBTREEANGMOM, // 3D angular momentum of subtree
|
mjSENS_SUBTREEANGMOM, // 3D angular momentum of subtree
|
||||||
|
|
||||||
|
// sensors for geometric distance; attached to geoms or bodies
|
||||||
|
mjSENS_GEOMDIST, // signed distance between two geoms
|
||||||
|
mjSENS_GEOMNORMAL, // normal direction between two geoms
|
||||||
|
mjSENS_GEOMFROMTO, // segment between two geoms
|
||||||
|
|
||||||
// global sensors
|
// global sensors
|
||||||
mjSENS_CLOCK, // simulation time
|
mjSENS_CLOCK, // simulation time
|
||||||
|
|
||||||
|
|||||||
@@ -464,6 +464,10 @@ MJAPI void mj_objectVelocity(const mjModel* m, const mjData* d,
|
|||||||
MJAPI void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
MJAPI void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
||||||
int objtype, int objid, mjtNum res[6], int flg_local);
|
int objtype, int objid, mjtNum res[6], int flg_local);
|
||||||
|
|
||||||
|
// Returns smallest signed distance between two geoms and optionally segment from geom1 to geom2.
|
||||||
|
MJAPI mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
||||||
|
mjtNum distmax, mjtNum fromto[6]);
|
||||||
|
|
||||||
// Extract 6D force:torque given contact id, in the contact frame.
|
// Extract 6D force:torque given contact id, in the contact frame.
|
||||||
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -338,9 +338,12 @@ ENUMS: Mapping[str, EnumDecl] = dict([
|
|||||||
('mjSENS_SUBTREECOM', 34),
|
('mjSENS_SUBTREECOM', 34),
|
||||||
('mjSENS_SUBTREELINVEL', 35),
|
('mjSENS_SUBTREELINVEL', 35),
|
||||||
('mjSENS_SUBTREEANGMOM', 36),
|
('mjSENS_SUBTREEANGMOM', 36),
|
||||||
('mjSENS_CLOCK', 37),
|
('mjSENS_GEOMDIST', 37),
|
||||||
('mjSENS_PLUGIN', 38),
|
('mjSENS_GEOMNORMAL', 38),
|
||||||
('mjSENS_USER', 39),
|
('mjSENS_GEOMFROMTO', 39),
|
||||||
|
('mjSENS_CLOCK', 40),
|
||||||
|
('mjSENS_PLUGIN', 41),
|
||||||
|
('mjSENS_USER', 42),
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
('mjtStage',
|
('mjtStage',
|
||||||
|
|||||||
@@ -2743,6 +2743,45 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
|||||||
),
|
),
|
||||||
doc='Compute object 6D acceleration (rot:lin) in object-centered frame, world/local orientation.', # pylint: disable=line-too-long
|
doc='Compute object 6D acceleration (rot:lin) in object-centered frame, world/local orientation.', # pylint: disable=line-too-long
|
||||||
)),
|
)),
|
||||||
|
('mj_geomDistance',
|
||||||
|
FunctionDecl(
|
||||||
|
name='mj_geomDistance',
|
||||||
|
return_type=ValueType(name='mjtNum'),
|
||||||
|
parameters=(
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='m',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjModel', is_const=True),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='d',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjData', is_const=True),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='geom1',
|
||||||
|
type=ValueType(name='int'),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='geom2',
|
||||||
|
type=ValueType(name='int'),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='distmax',
|
||||||
|
type=ValueType(name='mjtNum'),
|
||||||
|
),
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='fromto',
|
||||||
|
type=ArrayType(
|
||||||
|
inner_type=ValueType(name='mjtNum'),
|
||||||
|
extents=(6,),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
doc='Returns smallest signed distance between two geoms and optionally segment from geom1 to geom2.', # pylint: disable=line-too-long
|
||||||
|
)),
|
||||||
('mj_contactForce',
|
('mj_contactForce',
|
||||||
FunctionDecl(
|
FunctionDecl(
|
||||||
name='mj_contactForce',
|
name='mj_contactForce',
|
||||||
|
|||||||
@@ -1194,6 +1194,14 @@ Euler integrator, semi-implicit in velocity.
|
|||||||
mujoco.mjd_inverseFD(self.model, self.data, eps, flg_centered,
|
mujoco.mjd_inverseFD(self.model, self.data, eps, flg_centered,
|
||||||
None, None, None, None, None, None, None)
|
None, None, None, None, None, None, None)
|
||||||
|
|
||||||
|
def test_geom_distance(self):
|
||||||
|
mujoco.mj_forward(self.model, self.data)
|
||||||
|
fromto = np.empty(6, np.float64)
|
||||||
|
dist = mujoco.mj_geomDistance(self.model, self.data, 0, 2, 200, fromto)
|
||||||
|
self.assertEqual(dist, 41.9)
|
||||||
|
np.testing.assert_array_equal(fromto,
|
||||||
|
np.array((42., 0., 0., 42., 0., 41.9)))
|
||||||
|
|
||||||
def test_inverse_fd(self):
|
def test_inverse_fd(self):
|
||||||
eps = 1e-6
|
eps = 1e-6
|
||||||
flg_centered = 0
|
flg_centered = 0
|
||||||
|
|||||||
@@ -531,6 +531,18 @@ PYBIND11_MODULE(_functions, pymodule) {
|
|||||||
Def<traits::mj_objectVelocity>(pymodule);
|
Def<traits::mj_objectVelocity>(pymodule);
|
||||||
Def<traits::mj_objectAcceleration>(pymodule);
|
Def<traits::mj_objectAcceleration>(pymodule);
|
||||||
Def<traits::mj_contactForce>(pymodule);
|
Def<traits::mj_contactForce>(pymodule);
|
||||||
|
Def<traits::mj_geomDistance>(
|
||||||
|
pymodule,
|
||||||
|
[](const raw::MjModel* m, const raw::MjData* d,
|
||||||
|
int geom1, int geom2, mjtNum distmax,
|
||||||
|
std::optional<Eigen::Ref<EigenArrayXX>> fromto) {
|
||||||
|
if (fromto.has_value() && fromto->size() != 6) {
|
||||||
|
throw py::type_error("fromto should be of size 6");
|
||||||
|
}
|
||||||
|
return InterceptMjErrors(::mj_geomDistance)(
|
||||||
|
m, d, geom1, geom2, distmax,
|
||||||
|
fromto.has_value() ? fromto->data() : nullptr);
|
||||||
|
});
|
||||||
Def<traits::mj_differentiatePos>(
|
Def<traits::mj_differentiatePos>(
|
||||||
pymodule,
|
pymodule,
|
||||||
[](const raw::MjModel* m, Eigen::Ref<EigenVectorX> qvel,
|
[](const raw::MjModel* m, Eigen::Ref<EigenVectorX> qvel,
|
||||||
|
|||||||
@@ -1773,6 +1773,7 @@ static int sensorSize(mjtSensor sensor_type, int sensor_dim) {
|
|||||||
case mjSENS_TENDONLIMITPOS:
|
case mjSENS_TENDONLIMITPOS:
|
||||||
case mjSENS_TENDONLIMITVEL:
|
case mjSENS_TENDONLIMITVEL:
|
||||||
case mjSENS_TENDONLIMITFRC:
|
case mjSENS_TENDONLIMITFRC:
|
||||||
|
case mjSENS_GEOMDIST:
|
||||||
case mjSENS_CLOCK:
|
case mjSENS_CLOCK:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -1797,8 +1798,12 @@ static int sensorSize(mjtSensor sensor_type, int sensor_dim) {
|
|||||||
case mjSENS_SUBTREECOM:
|
case mjSENS_SUBTREECOM:
|
||||||
case mjSENS_SUBTREELINVEL:
|
case mjSENS_SUBTREELINVEL:
|
||||||
case mjSENS_SUBTREEANGMOM:
|
case mjSENS_SUBTREEANGMOM:
|
||||||
|
case mjSENS_GEOMNORMAL:
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
|
case mjSENS_GEOMFROMTO:
|
||||||
|
return 6;
|
||||||
|
|
||||||
case mjSENS_BALLQUAT:
|
case mjSENS_BALLQUAT:
|
||||||
case mjSENS_FRAMEQUAT:
|
case mjSENS_FRAMEQUAT:
|
||||||
return 4;
|
return 4;
|
||||||
|
|||||||
@@ -1137,14 +1137,13 @@ static int point_in_box(const mjtNum aabb[6], const mjtNum xpos[3],
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------- main entry point ---------------------------------------------------
|
//---------------------------- main entry point ----------------------------------------------------
|
||||||
|
|
||||||
// intersect ray (pnt+x*vec, x>=0) with visible geoms, except geoms on bodyexclude
|
// intersect ray (pnt+x*vec, x>=0) with visible geoms, except geoms on bodyexclude
|
||||||
// return geomid and distance (x) to nearest surface, or -1 if no intersection
|
// return geomid and distance (x) to nearest surface, or -1 if no intersection
|
||||||
// geomgroup, flg_static are as in mjvOption; geomgroup==NULL skips group exclusion
|
// geomgroup, flg_static are as in mjvOption; geomgroup==NULL skips group exclusion
|
||||||
mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum* vec,
|
mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum* vec,
|
||||||
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude,
|
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude, int geomid[1]) {
|
||||||
int geomid[1]) {
|
|
||||||
mjtNum dist, newdist;
|
mjtNum dist, newdist;
|
||||||
|
|
||||||
// check vector length
|
// check vector length
|
||||||
@@ -1154,7 +1153,7 @@ mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum
|
|||||||
|
|
||||||
// clear result
|
// clear result
|
||||||
dist = -1;
|
dist = -1;
|
||||||
*geomid = -1;
|
if (geomid) *geomid = -1;
|
||||||
|
|
||||||
// loop over geoms not eliminated by mask and bodyexclude
|
// loop over geoms not eliminated by mask and bodyexclude
|
||||||
for (int i=0; i < m->ngeom; i++) {
|
for (int i=0; i < m->ngeom; i++) {
|
||||||
@@ -1177,7 +1176,7 @@ mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum
|
|||||||
// update if closer intersection found
|
// update if closer intersection found
|
||||||
if (newdist >= 0 && (newdist < dist || dist < 0)) {
|
if (newdist >= 0 && (newdist < dist || dist < 0)) {
|
||||||
dist = newdist;
|
dist = newdist;
|
||||||
*geomid = i;
|
if (geomid) *geomid = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-16
@@ -40,6 +40,11 @@ static void apply_cutoff(const mjModel* m, mjData* d, mjtStage stage) {
|
|||||||
// process sensors matching stage and having positive cutoff
|
// process sensors matching stage and having positive cutoff
|
||||||
for (int i=0; i < m->nsensor; i++) {
|
for (int i=0; i < m->nsensor; i++) {
|
||||||
if (m->sensor_needstage[i] == stage && m->sensor_cutoff[i] > 0) {
|
if (m->sensor_needstage[i] == stage && m->sensor_cutoff[i] > 0) {
|
||||||
|
// skip fromto sensors
|
||||||
|
if (m->sensor_type[i] == mjSENS_GEOMFROMTO) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// get sensor info
|
// get sensor info
|
||||||
int adr = m->sensor_adr[i];
|
int adr = m->sensor_adr[i];
|
||||||
int dim = m->sensor_dim[i];
|
int dim = m->sensor_dim[i];
|
||||||
@@ -214,9 +219,8 @@ static void cam_project(mjtNum sensordata[2], const mjtNum target_xpos[3],
|
|||||||
|
|
||||||
// position-dependent sensors
|
// position-dependent sensors
|
||||||
void mj_sensorPos(const mjModel* m, mjData* d) {
|
void mj_sensorPos(const mjModel* m, mjData* d) {
|
||||||
int rgeomid, objtype, objid, reftype, refid, adr, offset, nusersensor = 0;
|
int ne = d->ne, nf = d->nf, nefc = d->nefc, nsensor = m->nsensor;
|
||||||
int ne = d->ne, nf = d->nf, nefc = d->nefc;
|
int nusersensor = 0;
|
||||||
mjtNum rvec[3], *xpos, *xmat, *xpos_ref, *xmat_ref;
|
|
||||||
|
|
||||||
// disabled sensors: return
|
// disabled sensors: return
|
||||||
if (mjDISABLED(mjDSBL_SENSOR)) {
|
if (mjDISABLED(mjDSBL_SENSOR)) {
|
||||||
@@ -224,22 +228,26 @@ void mj_sensorPos(const mjModel* m, mjData* d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process sensors matching stage
|
// process sensors matching stage
|
||||||
for (int i=0; i < m->nsensor; i++) {
|
for (int i=0; i < nsensor; i++) {
|
||||||
|
mjtSensor type = (mjtSensor) m->sensor_type[i];
|
||||||
|
|
||||||
// skip sensor plugins -- these are handled after builtin sensor types
|
// skip sensor plugins -- these are handled after builtin sensor types
|
||||||
if (m->sensor_type[i] == mjSENS_PLUGIN) {
|
if (type == mjSENS_PLUGIN) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->sensor_needstage[i] == mjSTAGE_POS) {
|
if (m->sensor_needstage[i] == mjSTAGE_POS) {
|
||||||
// get sensor info
|
// get sensor info
|
||||||
objtype = m->sensor_objtype[i];
|
int objtype = m->sensor_objtype[i];
|
||||||
objid = m->sensor_objid[i];
|
int objid = m->sensor_objid[i];
|
||||||
refid = m->sensor_refid[i];
|
int refid = m->sensor_refid[i];
|
||||||
reftype = m->sensor_reftype[i];
|
int reftype = m->sensor_reftype[i];
|
||||||
adr = m->sensor_adr[i];
|
int adr = m->sensor_adr[i];
|
||||||
|
|
||||||
|
mjtNum rvec[3], *xpos, *xmat, *xpos_ref, *xmat_ref;
|
||||||
|
|
||||||
// process according to type
|
// process according to type
|
||||||
switch ((mjtSensor) m->sensor_type[i]) {
|
switch (type) {
|
||||||
case mjSENS_MAGNETOMETER: // magnetometer
|
case mjSENS_MAGNETOMETER: // magnetometer
|
||||||
mju_mulMatTVec(d->sensordata+adr, d->site_xmat+9*objid, m->opt.magnetic, 3, 3);
|
mju_mulMatTVec(d->sensordata+adr, d->site_xmat+9*objid, m->opt.magnetic, 3, 3);
|
||||||
break;
|
break;
|
||||||
@@ -255,7 +263,8 @@ void mj_sensorPos(const mjModel* m, mjData* d) {
|
|||||||
rvec[1] = d->site_xmat[9*objid+5];
|
rvec[1] = d->site_xmat[9*objid+5];
|
||||||
rvec[2] = d->site_xmat[9*objid+8];
|
rvec[2] = d->site_xmat[9*objid+8];
|
||||||
d->sensordata[adr] = mj_ray(m, d, d->site_xpos+3*objid, rvec, NULL, 1,
|
d->sensordata[adr] = mj_ray(m, d, d->site_xpos+3*objid, rvec, NULL, 1,
|
||||||
m->site_bodyid[objid], &rgeomid);
|
m->site_bodyid[objid], NULL);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case mjSENS_JOINTPOS: // jointpos
|
case mjSENS_JOINTPOS: // jointpos
|
||||||
@@ -303,11 +312,11 @@ void mj_sensorPos(const mjModel* m, mjData* d) {
|
|||||||
|
|
||||||
// reference frame unspecified: global frame
|
// reference frame unspecified: global frame
|
||||||
if (refid == -1) {
|
if (refid == -1) {
|
||||||
if (m->sensor_type[i] == mjSENS_FRAMEPOS) {
|
if (type == mjSENS_FRAMEPOS) {
|
||||||
mju_copy3(d->sensordata+adr, xpos);
|
mju_copy3(d->sensordata+adr, xpos);
|
||||||
} else {
|
} else {
|
||||||
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
|
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
|
||||||
offset = m->sensor_type[i] - mjSENS_FRAMEXAXIS;
|
int offset = type - mjSENS_FRAMEXAXIS;
|
||||||
d->sensordata[adr] = xmat[offset];
|
d->sensordata[adr] = xmat[offset];
|
||||||
d->sensordata[adr+1] = xmat[offset+3];
|
d->sensordata[adr+1] = xmat[offset+3];
|
||||||
d->sensordata[adr+2] = xmat[offset+6];
|
d->sensordata[adr+2] = xmat[offset+6];
|
||||||
@@ -317,12 +326,12 @@ void mj_sensorPos(const mjModel* m, mjData* d) {
|
|||||||
// reference frame specified
|
// reference frame specified
|
||||||
else {
|
else {
|
||||||
get_xpos_xmat(d, reftype, refid, i, &xpos_ref, &xmat_ref);
|
get_xpos_xmat(d, reftype, refid, i, &xpos_ref, &xmat_ref);
|
||||||
if (m->sensor_type[i] == mjSENS_FRAMEPOS) {
|
if (type == mjSENS_FRAMEPOS) {
|
||||||
mju_sub3(rvec, xpos, xpos_ref);
|
mju_sub3(rvec, xpos, xpos_ref);
|
||||||
mju_rotVecMatT(d->sensordata+adr, rvec, xmat_ref);
|
mju_rotVecMatT(d->sensordata+adr, rvec, xmat_ref);
|
||||||
} else {
|
} else {
|
||||||
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
|
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
|
||||||
offset = m->sensor_type[i] - mjSENS_FRAMEXAXIS;
|
int offset = type - mjSENS_FRAMEXAXIS;
|
||||||
mjtNum axis[3] = {xmat[offset], xmat[offset+3], xmat[offset+6]};
|
mjtNum axis[3] = {xmat[offset], xmat[offset+3], xmat[offset+6]};
|
||||||
mju_rotVecMatT(d->sensordata+adr, axis, xmat_ref);
|
mju_rotVecMatT(d->sensordata+adr, axis, xmat_ref);
|
||||||
}
|
}
|
||||||
@@ -354,6 +363,99 @@ void mj_sensorPos(const mjModel* m, mjData* d) {
|
|||||||
mju_copy3(d->sensordata+adr, d->subtree_com+3*objid);
|
mju_copy3(d->sensordata+adr, d->subtree_com+3*objid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case mjSENS_GEOMDIST: // signed distance between two geoms
|
||||||
|
case mjSENS_GEOMNORMAL: // normal direction between two geoms
|
||||||
|
case mjSENS_GEOMFROMTO: // segment between two geoms
|
||||||
|
{
|
||||||
|
// use cutoff for collision margin
|
||||||
|
mjtNum margin = m->sensor_cutoff[i];
|
||||||
|
|
||||||
|
// initialize outputs
|
||||||
|
mjtNum dist = margin; // collision distance
|
||||||
|
mjtNum fromto[6] = {0}; // segment between geoms
|
||||||
|
|
||||||
|
// get lists of geoms to collide
|
||||||
|
int n1, id1;
|
||||||
|
if (objtype == mjOBJ_BODY) {
|
||||||
|
n1 = m->body_geomnum[objid];
|
||||||
|
id1 = m->body_geomadr[objid];
|
||||||
|
} else {
|
||||||
|
n1 = 1;
|
||||||
|
id1 = objid;
|
||||||
|
}
|
||||||
|
int n2, id2;
|
||||||
|
if (reftype == mjOBJ_BODY) {
|
||||||
|
n2 = m->body_geomnum[refid];
|
||||||
|
id2 = m->body_geomadr[refid];
|
||||||
|
} else {
|
||||||
|
n2 = 1;
|
||||||
|
id2 = refid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collide all pairs
|
||||||
|
for (int geom1=id1; geom1 < id1+n1; geom1++) {
|
||||||
|
for (int geom2=id2; geom2 < id2+n2; geom2++) {
|
||||||
|
mjtNum fromto_new[6] = {0};
|
||||||
|
mjtNum dist_new = mj_geomDistance(m, d, geom1, geom2, margin, fromto_new);
|
||||||
|
if (dist_new < dist) {
|
||||||
|
dist = dist_new;
|
||||||
|
mju_copy(fromto, fromto_new, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write sensordata for this sensor and all subsequent sensors with identical signature
|
||||||
|
int write_sensor = 1;
|
||||||
|
while (write_sensor) {
|
||||||
|
// write geom distance
|
||||||
|
if (type == mjSENS_GEOMDIST) {
|
||||||
|
d->sensordata[adr] = dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write distance normal
|
||||||
|
else if (type == mjSENS_GEOMNORMAL) {
|
||||||
|
mjtNum normal[3] = {fromto[3]-fromto[0], fromto[4]-fromto[1], fromto[5]-fromto[2]};
|
||||||
|
if (normal[0] || normal[1] || normal[2]) {
|
||||||
|
mju_normalize3(normal);
|
||||||
|
}
|
||||||
|
mju_copy3(d->sensordata + adr, normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write distance fromto
|
||||||
|
else {
|
||||||
|
mju_copy(d->sensordata + adr, fromto, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is the last sensor, break
|
||||||
|
if (i+1 == nsensor) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type of the next sensor
|
||||||
|
mjtSensor type_next = m->sensor_type[i+1];
|
||||||
|
|
||||||
|
// check if signature of next sensor matches this sensor
|
||||||
|
write_sensor = (type_next == mjSENS_GEOMDIST ||
|
||||||
|
type_next == mjSENS_GEOMNORMAL ||
|
||||||
|
type_next == mjSENS_GEOMFROMTO) &&
|
||||||
|
m->sensor_objtype[i+1] == objtype &&
|
||||||
|
m->sensor_objid[i+1] == objid &&
|
||||||
|
m->sensor_reftype[i+1] == reftype &&
|
||||||
|
m->sensor_refid[i+1] == refid &&
|
||||||
|
m->sensor_cutoff[i+1] == margin;
|
||||||
|
|
||||||
|
// if signature matches, increment external loop variable i
|
||||||
|
if (write_sensor) {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// update adr and type, everything else is the same
|
||||||
|
adr = m->sensor_adr[i];
|
||||||
|
type = type_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case mjSENS_CLOCK: // clock
|
case mjSENS_CLOCK: // clock
|
||||||
d->sensordata[adr] = d->time;
|
d->sensordata[adr] = d->time;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <mujoco/mjdata.h>
|
#include <mujoco/mjdata.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include "engine/engine_collision_driver.h"
|
||||||
#include "engine/engine_core_constraint.h"
|
#include "engine/engine_core_constraint.h"
|
||||||
#include "engine/engine_crossplatform.h"
|
#include "engine/engine_crossplatform.h"
|
||||||
#include "engine/engine_io.h"
|
#include "engine/engine_io.h"
|
||||||
@@ -1703,6 +1704,48 @@ void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
|||||||
|
|
||||||
//-------------------------- miscellaneous ---------------------------------------------------------
|
//-------------------------- miscellaneous ---------------------------------------------------------
|
||||||
|
|
||||||
|
// returns the smallest distance between two geoms
|
||||||
|
mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2, mjtNum distmax,
|
||||||
|
mjtNum fromto[6]) {
|
||||||
|
mjContact con[mjMAXCONPAIR];
|
||||||
|
mjtNum dist = distmax;
|
||||||
|
if (fromto) mju_zero(fromto, 6);
|
||||||
|
|
||||||
|
// flip geom order if required
|
||||||
|
int flip = m->geom_type[geom1] > m->geom_type[geom2];
|
||||||
|
int g1 = flip ? geom2 : geom1;
|
||||||
|
int g2 = flip ? geom1 : geom2;
|
||||||
|
int type1 = m->geom_type[g1];
|
||||||
|
int type2 = m->geom_type[g2];
|
||||||
|
|
||||||
|
// call collision function if it exists
|
||||||
|
if (!mjCOLLISIONFUNC[type1][type2]) {
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
int num = mjCOLLISIONFUNC[type1][type2](m, d, con, g1, g2, distmax);
|
||||||
|
|
||||||
|
// find smallest distance
|
||||||
|
int smallest = -1;
|
||||||
|
for (int i=0; i < num; i++) {
|
||||||
|
mjtNum dist_i = con[i].dist;
|
||||||
|
if (dist_i < dist) {
|
||||||
|
dist = dist_i;
|
||||||
|
smallest = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write fromto if given and a collision has been found
|
||||||
|
if (fromto && smallest >= 0) {
|
||||||
|
mjtNum sign = flip ? -1 : 1;
|
||||||
|
mju_addScl3(fromto+0, con[smallest].pos, con[smallest].frame, -0.5*sign*dist);
|
||||||
|
mju_addScl3(fromto+3, con[smallest].pos, con[smallest].frame, 0.5*sign*dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// extract 6D force:torque for one contact, in contact frame
|
// extract 6D force:torque for one contact, in contact frame
|
||||||
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]) {
|
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]) {
|
||||||
mjContact* con;
|
mjContact* con;
|
||||||
|
|||||||
@@ -189,6 +189,10 @@ MJAPI void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
|||||||
|
|
||||||
//-------------------------- miscellaneous ---------------------------------------------------------
|
//-------------------------- miscellaneous ---------------------------------------------------------
|
||||||
|
|
||||||
|
// returns the smallest distance between two geoms
|
||||||
|
MJAPI mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
||||||
|
mjtNum distmax, mjtNum fromto[6]);
|
||||||
|
|
||||||
// extract 6D force:torque for one contact, in contact frame
|
// extract 6D force:torque for one contact, in contact frame
|
||||||
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||||
|
|
||||||
|
|||||||
@@ -1974,6 +1974,20 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
|||||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, from, to);
|
mjv_connector(thisgeom, mjGEOM_LINE, 3, from, to);
|
||||||
f2f(thisgeom->rgba, m->vis.rgba.rangefinder, 4);
|
f2f(thisgeom->rgba, m->vis.rgba.rangefinder, 4);
|
||||||
FINISH
|
FINISH
|
||||||
|
} else if (m->sensor_type[i] == mjSENS_GEOMFROMTO) {
|
||||||
|
// sensor data
|
||||||
|
mjtNum* fromto = d->sensordata + m->sensor_adr[i];
|
||||||
|
|
||||||
|
// null output: nothing to render
|
||||||
|
if (mju_isZero(fromto, 6)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make ray
|
||||||
|
START
|
||||||
|
mjv_connector(thisgeom, mjGEOM_LINE, 3, fromto, fromto+3);
|
||||||
|
f2f(thisgeom->rgba, m->vis.rgba.rangefinder, 4);
|
||||||
|
FINISH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-11
@@ -5388,7 +5388,6 @@ void mjCSensor::ResolveReferences(const mjCModel* m) {
|
|||||||
((mjCGeom*)obj)->SetNotVisual();
|
((mjCGeom*)obj)->SetNotVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
// get sensorized object id
|
|
||||||
} else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN && type != mjSENS_USER) {
|
} else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN && type != mjSENS_USER) {
|
||||||
throw mjCError(this, "invalid type in sensor");
|
throw mjCError(this, "invalid type in sensor");
|
||||||
}
|
}
|
||||||
@@ -5402,7 +5401,7 @@ void mjCSensor::ResolveReferences(const mjCModel* m) {
|
|||||||
|
|
||||||
// find name
|
// find name
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
throw mjCError(this, "unrecognized name '%s' of reference frame object", refname_.c_str());
|
throw mjCError(this, "unrecognized name '%s' of object", refname_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be attached to object with spatial frame
|
// must be attached to object with spatial frame
|
||||||
@@ -5460,7 +5459,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_CAMPROJECTION:
|
case mjSENS_CAMPROJECTION:
|
||||||
// must be attached to site
|
// must be attached to site
|
||||||
if (objtype!=mjOBJ_SITE) {
|
if (objtype!=mjOBJ_SITE) {
|
||||||
throw mjCError(this, "sensor must be attached to site: sensor");
|
throw mjCError(this, "sensor must be attached to site");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set dim and datatype
|
// set dim and datatype
|
||||||
@@ -5498,7 +5497,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_JOINTACTFRC:
|
case mjSENS_JOINTACTFRC:
|
||||||
// must be attached to joint
|
// must be attached to joint
|
||||||
if (objtype!=mjOBJ_JOINT) {
|
if (objtype!=mjOBJ_JOINT) {
|
||||||
throw mjCError(this, "sensor must be attached to joint: sensor");
|
throw mjCError(this, "sensor must be attached to joint");
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure joint is slide or hinge
|
// make sure joint is slide or hinge
|
||||||
@@ -5522,7 +5521,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_TENDONVEL:
|
case mjSENS_TENDONVEL:
|
||||||
// must be attached to tendon
|
// must be attached to tendon
|
||||||
if (objtype!=mjOBJ_TENDON) {
|
if (objtype!=mjOBJ_TENDON) {
|
||||||
throw mjCError(this, "sensor must be attached to tendon: sensor");
|
throw mjCError(this, "sensor must be attached to tendon");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set
|
// set
|
||||||
@@ -5540,7 +5539,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_ACTUATORFRC:
|
case mjSENS_ACTUATORFRC:
|
||||||
// must be attached to actuator
|
// must be attached to actuator
|
||||||
if (objtype!=mjOBJ_ACTUATOR) {
|
if (objtype!=mjOBJ_ACTUATOR) {
|
||||||
throw mjCError(this, "sensor must be attached to actuator: sensor");
|
throw mjCError(this, "sensor must be attached to actuator");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set
|
// set
|
||||||
@@ -5559,7 +5558,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_BALLANGVEL:
|
case mjSENS_BALLANGVEL:
|
||||||
// must be attached to joint
|
// must be attached to joint
|
||||||
if (objtype!=mjOBJ_JOINT) {
|
if (objtype!=mjOBJ_JOINT) {
|
||||||
throw mjCError(this, "sensor must be attached to joint: sensor");
|
throw mjCError(this, "sensor must be attached to joint");
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure joint is ball
|
// make sure joint is ball
|
||||||
@@ -5584,7 +5583,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_JOINTLIMITFRC:
|
case mjSENS_JOINTLIMITFRC:
|
||||||
// must be attached to joint
|
// must be attached to joint
|
||||||
if (objtype!=mjOBJ_JOINT) {
|
if (objtype!=mjOBJ_JOINT) {
|
||||||
throw mjCError(this, "sensor must be attached to joint: sensor");
|
throw mjCError(this, "sensor must be attached to joint");
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure joint has limit
|
// make sure joint has limit
|
||||||
@@ -5609,7 +5608,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_TENDONLIMITFRC:
|
case mjSENS_TENDONLIMITFRC:
|
||||||
// must be attached to tendon
|
// must be attached to tendon
|
||||||
if (objtype!=mjOBJ_TENDON) {
|
if (objtype!=mjOBJ_TENDON) {
|
||||||
throw mjCError(this, "sensor must be attached to tendon: sensor");
|
throw mjCError(this, "sensor must be attached to tendon");
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure tendon has limit
|
// make sure tendon has limit
|
||||||
@@ -5675,7 +5674,7 @@ void mjCSensor::Compile(void) {
|
|||||||
case mjSENS_SUBTREEANGMOM:
|
case mjSENS_SUBTREEANGMOM:
|
||||||
// must be attached to body
|
// must be attached to body
|
||||||
if (objtype!=mjOBJ_BODY) {
|
if (objtype!=mjOBJ_BODY) {
|
||||||
throw mjCError(this, "sensor must be attached to body: sensor");
|
throw mjCError(this, "sensor must be attached to body");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set
|
// set
|
||||||
@@ -5688,6 +5687,34 @@ void mjCSensor::Compile(void) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case mjSENS_GEOMDIST:
|
||||||
|
case mjSENS_GEOMNORMAL:
|
||||||
|
case mjSENS_GEOMFROMTO:
|
||||||
|
// must be attached to body or geom
|
||||||
|
if ((objtype!=mjOBJ_BODY && objtype!=mjOBJ_GEOM) ||
|
||||||
|
(reftype!=mjOBJ_BODY && reftype!=mjOBJ_GEOM)) {
|
||||||
|
throw mjCError(this, "sensor must be attached to body or geom");
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects must be different
|
||||||
|
if (objtype == reftype && obj == ref) {
|
||||||
|
throw mjCError(this, "1st body/geom must be different from 2nd body/geom");
|
||||||
|
}
|
||||||
|
|
||||||
|
// set
|
||||||
|
needstage = mjSTAGE_POS;
|
||||||
|
if (type==mjSENS_GEOMDIST) {
|
||||||
|
dim = 1;
|
||||||
|
datatype = mjDATATYPE_POSITIVE;
|
||||||
|
} else if (type==mjSENS_GEOMNORMAL) {
|
||||||
|
dim = 3;
|
||||||
|
datatype = mjDATATYPE_AXIS;
|
||||||
|
} else {
|
||||||
|
dim = 6;
|
||||||
|
datatype = mjDATATYPE_REAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case mjSENS_CLOCK:
|
case mjSENS_CLOCK:
|
||||||
dim = 1;
|
dim = 1;
|
||||||
needstage = mjSTAGE_POS;
|
needstage = mjSTAGE_POS;
|
||||||
@@ -5737,7 +5764,8 @@ void mjCSensor::Compile(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check cutoff for incompatible data types
|
// check cutoff for incompatible data types
|
||||||
if (cutoff>0 && (datatype==mjDATATYPE_AXIS || datatype==mjDATATYPE_QUATERNION)) {
|
if (cutoff > 0 && (datatype == mjDATATYPE_QUATERNION ||
|
||||||
|
(datatype == mjDATATYPE_AXIS && type != mjSENS_GEOMNORMAL))) {
|
||||||
throw mjCError(this, "cutoff applied to axis or quaternion datatype in sensor");
|
throw mjCError(this, "cutoff applied to axis or quaternion datatype in sensor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -472,6 +472,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
|
|||||||
{"subtreecom", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
{"subtreecom", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
||||||
{"subtreelinvel", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
{"subtreelinvel", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
||||||
{"subtreeangmom", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
{"subtreeangmom", "*", "5", "name", "body", "cutoff", "noise", "user"},
|
||||||
|
{"distance", "*", "8", "name", "geom1", "geom2", "body1", "body2", "cutoff", "noise", "user"},
|
||||||
|
{"normal", "*", "8", "name", "geom1", "geom2", "body1", "body2", "cutoff", "noise", "user"},
|
||||||
|
{"fromto", "*", "8", "name", "geom1", "geom2", "body1", "body2", "cutoff", "noise", "user"},
|
||||||
{"clock", "*", "4", "name", "cutoff", "noise", "user"},
|
{"clock", "*", "4", "name", "cutoff", "noise", "user"},
|
||||||
{"user", "*", "9", "name", "objtype", "objname", "datatype", "needstage",
|
{"user", "*", "9", "name", "objtype", "objname", "datatype", "needstage",
|
||||||
"dim", "cutoff", "noise", "user"},
|
"dim", "cutoff", "noise", "user"},
|
||||||
@@ -3949,6 +3952,29 @@ void mjXReader::Sensor(XMLElement* section) {
|
|||||||
ReadAttrTxt(elem, "body", objname, true);
|
ReadAttrTxt(elem, "body", objname, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sensors for geometric distance; attached to geoms or bodies
|
||||||
|
else if (type=="distance" || type=="normal" || type=="fromto") {
|
||||||
|
bool has_body1 = ReadAttrTxt(elem, "body1", objname);
|
||||||
|
bool has_geom1 = ReadAttrTxt(elem, "geom1", objname);
|
||||||
|
if (has_body1 == has_geom1) {
|
||||||
|
throw mjXError(elem, "exactly one of (geom1, body1) must be specified");
|
||||||
|
}
|
||||||
|
psen->objtype = has_body1 ? mjOBJ_BODY : mjOBJ_GEOM;
|
||||||
|
bool has_body2 = ReadAttrTxt(elem, "body2", refname);
|
||||||
|
bool has_geom2 = ReadAttrTxt(elem, "geom2", refname);
|
||||||
|
if (has_body2 == has_geom2) {
|
||||||
|
throw mjXError(elem, "exactly one of (geom2, body2) must be specified");
|
||||||
|
}
|
||||||
|
psen->reftype = has_body2 ? mjOBJ_BODY : mjOBJ_GEOM;
|
||||||
|
if (type=="distance") {
|
||||||
|
psen->type = mjSENS_GEOMDIST;
|
||||||
|
} else if (type=="normal") {
|
||||||
|
psen->type = mjSENS_GEOMNORMAL;
|
||||||
|
} else {
|
||||||
|
psen->type = mjSENS_GEOMFROMTO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// global sensors
|
// global sensors
|
||||||
else if (type=="clock") {
|
else if (type=="clock") {
|
||||||
psen->type = mjSENS_CLOCK;
|
psen->type = mjSENS_CLOCK;
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class mjXReader : public mjXBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// MJCF schema
|
// MJCF schema
|
||||||
#define nMJCF 227
|
#define nMJCF 230
|
||||||
extern const char* MJCF[nMJCF][mjXATTRNUM];
|
extern const char* MJCF[nMJCF][mjXATTRNUM];
|
||||||
|
|
||||||
#endif // MUJOCO_SRC_XML_XML_NATIVE_READER_H_
|
#endif // MUJOCO_SRC_XML_XML_NATIVE_READER_H_
|
||||||
|
|||||||
@@ -1875,46 +1875,82 @@ void mjXWriter::Sensor(XMLElement* root) {
|
|||||||
elem = InsertEnd(section, "framepos");
|
elem = InsertEnd(section, "framepos");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEQUAT:
|
case mjSENS_FRAMEQUAT:
|
||||||
elem = InsertEnd(section, "framequat");
|
elem = InsertEnd(section, "framequat");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEXAXIS:
|
case mjSENS_FRAMEXAXIS:
|
||||||
elem = InsertEnd(section, "framexaxis");
|
elem = InsertEnd(section, "framexaxis");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEYAXIS:
|
case mjSENS_FRAMEYAXIS:
|
||||||
elem = InsertEnd(section, "frameyaxis");
|
elem = InsertEnd(section, "frameyaxis");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEZAXIS:
|
case mjSENS_FRAMEZAXIS:
|
||||||
elem = InsertEnd(section, "framezaxis");
|
elem = InsertEnd(section, "framezaxis");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMELINVEL:
|
case mjSENS_FRAMELINVEL:
|
||||||
elem = InsertEnd(section, "framelinvel");
|
elem = InsertEnd(section, "framelinvel");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEANGVEL:
|
case mjSENS_FRAMEANGVEL:
|
||||||
elem = InsertEnd(section, "frameangvel");
|
elem = InsertEnd(section, "frameangvel");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMELINACC:
|
case mjSENS_FRAMELINACC:
|
||||||
elem = InsertEnd(section, "framelinacc");
|
elem = InsertEnd(section, "framelinacc");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case mjSENS_FRAMEANGACC:
|
case mjSENS_FRAMEANGACC:
|
||||||
elem = InsertEnd(section, "frameangacc");
|
elem = InsertEnd(section, "frameangacc");
|
||||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||||
WriteAttrTxt(elem, "objname", psen->get_objname());
|
WriteAttrTxt(elem, "objname", psen->get_objname());
|
||||||
|
if (psen->reftype != mjOBJ_UNKNOWN) {
|
||||||
|
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
||||||
|
WriteAttrTxt(elem, "refname", psen->get_refname());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// sensors related to kinematic subtrees; attached to a body (which is the subtree root)
|
// sensors related to kinematic subtrees; attached to a body (which is the subtree root)
|
||||||
@@ -1930,6 +1966,21 @@ void mjXWriter::Sensor(XMLElement* root) {
|
|||||||
elem = InsertEnd(section, "subtreeangmom");
|
elem = InsertEnd(section, "subtreeangmom");
|
||||||
WriteAttrTxt(elem, "body", psen->get_objname());
|
WriteAttrTxt(elem, "body", psen->get_objname());
|
||||||
break;
|
break;
|
||||||
|
case mjSENS_GEOMDIST:
|
||||||
|
elem = InsertEnd(section, "distance");
|
||||||
|
WriteAttrTxt(elem, psen->objtype == mjOBJ_BODY ? "body1" : "geom1", psen->get_objname());
|
||||||
|
WriteAttrTxt(elem, psen->reftype == mjOBJ_BODY ? "body2" : "geom2", psen->get_refname());
|
||||||
|
break;
|
||||||
|
case mjSENS_GEOMNORMAL:
|
||||||
|
elem = InsertEnd(section, "normal");
|
||||||
|
WriteAttrTxt(elem, psen->objtype == mjOBJ_BODY ? "body1" : "geom1", psen->get_objname());
|
||||||
|
WriteAttrTxt(elem, psen->reftype == mjOBJ_BODY ? "body2" : "geom2", psen->get_refname());
|
||||||
|
break;
|
||||||
|
case mjSENS_GEOMFROMTO:
|
||||||
|
elem = InsertEnd(section, "fromto");
|
||||||
|
WriteAttrTxt(elem, psen->objtype == mjOBJ_BODY ? "body1" : "geom1", psen->get_objname());
|
||||||
|
WriteAttrTxt(elem, psen->reftype == mjOBJ_BODY ? "body2" : "geom2", psen->get_refname());
|
||||||
|
break;
|
||||||
|
|
||||||
// global sensors
|
// global sensors
|
||||||
case mjSENS_CLOCK:
|
case mjSENS_CLOCK:
|
||||||
@@ -1968,12 +2019,6 @@ void mjXWriter::Sensor(XMLElement* root) {
|
|||||||
WriteAttr(elem, "noise", 1, &psen->noise, &zero);
|
WriteAttr(elem, "noise", 1, &psen->noise, &zero);
|
||||||
}
|
}
|
||||||
WriteVector(elem, "user", psen->get_userdata());
|
WriteVector(elem, "user", psen->get_userdata());
|
||||||
|
|
||||||
// add reference if present
|
|
||||||
if (psen->reftype != mjOBJ_UNKNOWN && psen->type != mjSENS_CAMPROJECTION) {
|
|
||||||
WriteAttrTxt(elem, "reftype", mju_type2Str(psen->reftype));
|
|
||||||
WriteAttrTxt(elem, "refname", psen->get_refname());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove section if empty
|
// remove section if empty
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
// Tests for engine/engine_sensor.c.
|
// Tests for engine/engine_sensor.c.
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
@@ -44,7 +46,7 @@ using ::testing::StrEq;
|
|||||||
|
|
||||||
using SensorTest = MujocoTest;
|
using SensorTest = MujocoTest;
|
||||||
|
|
||||||
// --------------------- test sensor disableflag -----------------------------
|
// --------------------- test sensor disableflag ------------------------------
|
||||||
|
|
||||||
// hand-picked positions and orientations for simple expected values
|
// hand-picked positions and orientations for simple expected values
|
||||||
TEST_F(SensorTest, DisableSensors) {
|
TEST_F(SensorTest, DisableSensors) {
|
||||||
@@ -255,7 +257,7 @@ TEST_F(RelativeFrameSensorTest, FrameVelLinearFixed) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// object and reference in the same body, expect angular velocites to be zero
|
// object and reference in the same body, expect angular velocities to be zero
|
||||||
TEST_F(RelativeFrameSensorTest, FrameVelAngFixed) {
|
TEST_F(RelativeFrameSensorTest, FrameVelAngFixed) {
|
||||||
constexpr char xml[] = R"(
|
constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
@@ -279,7 +281,7 @@ TEST_F(RelativeFrameSensorTest, FrameVelAngFixed) {
|
|||||||
data->qvel[0] = 1;
|
data->qvel[0] = 1;
|
||||||
mj_forward(model, data);
|
mj_forward(model, data);
|
||||||
|
|
||||||
// obj and ref rotate together, relative angular velocites should be zero
|
// obj and ref rotate together, relative angular velocities should be zero
|
||||||
std::vector angvel = GetSensor(model, data, 0);
|
std::vector angvel = GetSensor(model, data, 0);
|
||||||
EXPECT_THAT(angvel, Pointwise(DoubleNear(tol), {0, 0, 0}));
|
EXPECT_THAT(angvel, Pointwise(DoubleNear(tol), {0, 0, 0}));
|
||||||
|
|
||||||
@@ -422,7 +424,7 @@ TEST_F(SensorTest, Clock) {
|
|||||||
EXPECT_EQ(data->sensordata[1], mju_min(data->time, 3e-3));
|
EXPECT_EQ(data->sensordata[1], mju_min(data->time, 3e-3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// chack names
|
// check names
|
||||||
const char* name0 = mj_id2name(model, mjOBJ_SENSOR, 0);
|
const char* name0 = mj_id2name(model, mjOBJ_SENSOR, 0);
|
||||||
EXPECT_EQ(name0, nullptr);
|
EXPECT_EQ(name0, nullptr);
|
||||||
const char* name1 = mj_id2name(model, mjOBJ_SENSOR, 1);
|
const char* name1 = mj_id2name(model, mjOBJ_SENSOR, 1);
|
||||||
@@ -432,7 +434,80 @@ TEST_F(SensorTest, Clock) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------- camera sensor tests -----------------------------
|
// test clock sensor
|
||||||
|
TEST_F(SensorTest, CollisionSequential) {
|
||||||
|
constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<geom name="plane" type="plane" size="1 1 1"/>
|
||||||
|
<geom name="sphere1" pos="0 0 1" size="0.2"/>
|
||||||
|
<geom name="sphere2" pos="1 0 1" size="0.3"/>
|
||||||
|
</worldbody>
|
||||||
|
<sensor>
|
||||||
|
<distance name="0" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<distance name="1" geom1="sphere2" geom2="plane" cutoff="1"/>
|
||||||
|
<distance name="2" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
<normal name="3" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<normal name="4" geom1="sphere2" geom2="plane" cutoff="1"/>
|
||||||
|
<normal name="5" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
<fromto name="6" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<fromto name="7" geom1="sphere2" geom2="plane" cutoff="1"/>
|
||||||
|
<fromto name="8" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
<!-- sequential sensors with identical signature -->
|
||||||
|
<distance name="9" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<fromto name="10" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<normal name="11" geom1="plane" geom2="sphere1" cutoff="1"/>
|
||||||
|
<normal name="12" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
<fromto name="13" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
<distance name="14" geom1="sphere1" geom2="sphere2" cutoff="1"/>
|
||||||
|
|
||||||
|
</sensor>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
mjModel* model = LoadModelFromString(xml);
|
||||||
|
mjData* data = mj_makeData(model);
|
||||||
|
mj_forward(model, data);
|
||||||
|
|
||||||
|
EXPECT_DOUBLE_EQ(data->sensordata[0], 0.8);
|
||||||
|
EXPECT_DOUBLE_EQ(data->sensordata[1], 0.7);
|
||||||
|
EXPECT_DOUBLE_EQ(data->sensordata[2], 0.5);
|
||||||
|
|
||||||
|
mjtNum eps = 1e-14;
|
||||||
|
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 3),
|
||||||
|
Pointwise(DoubleNear(eps), std::vector<mjtNum>{0, 0, 1}));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 4),
|
||||||
|
Pointwise(DoubleNear(eps), std::vector<mjtNum>{0, 0, -1}));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 5),
|
||||||
|
Pointwise(DoubleNear(eps), std::vector<mjtNum>{1, 0, 0}));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 6),
|
||||||
|
Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{0, 0, 0, 0, 0, .8}));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 7),
|
||||||
|
Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{1, 0, .7, 1, 0, 0}));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 8),
|
||||||
|
Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{.2, 0, 1, .7, 0, 1}));
|
||||||
|
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 9),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 0)));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 10),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 6)));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 11),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 3)));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 12),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 5)));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 13),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 8)));
|
||||||
|
EXPECT_THAT(GetSensor(model, data, 14),
|
||||||
|
Pointwise(DoubleNear(eps), GetSensor(model, data, 2)));
|
||||||
|
|
||||||
|
mj_deleteData(data);
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------- camera sensor tests ------------------------------
|
||||||
|
|
||||||
// test clock sensor
|
// test clock sensor
|
||||||
TEST_F(SensorTest, CameraProjection) {
|
TEST_F(SensorTest, CameraProjection) {
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ std::vector<mjtNum> AsVector(const mjtNum* array, int n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
using ::testing::DoubleNear;
|
using ::testing::DoubleNear;
|
||||||
using ::testing::ContainsRegex;
|
using ::testing::Eq;
|
||||||
|
using ::testing::ContainsRegex; // NOLINT
|
||||||
using ::testing::MatchesRegex;
|
using ::testing::MatchesRegex;
|
||||||
using ::testing::Pointwise;
|
using ::testing::Pointwise;
|
||||||
using ::testing::ElementsAreArray;
|
using ::testing::ElementsAreArray;
|
||||||
@@ -647,5 +648,79 @@ TEST_F(SupportTest, MulMIsland) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr char GeomDistanceTestingModel[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="box" scale=".1 .1 .1" vertex="0 0 0 1 0 0 0 1 0 1 1 0
|
||||||
|
0 0 1 1 0 1 0 1 1 1 1 1"/>
|
||||||
|
</asset>
|
||||||
|
|
||||||
|
<worldbody>
|
||||||
|
<geom type="plane" size="1 1 1"/>
|
||||||
|
<geom pos="0 0 1" size="0.2"/>
|
||||||
|
<geom pos="1 0 1" size="0.3"/>
|
||||||
|
<geom type="mesh" mesh="box"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
|
||||||
|
TEST_F(SupportTest, GeomDistance) {
|
||||||
|
mjModel* model = LoadModelFromString(GeomDistanceTestingModel);
|
||||||
|
mjData* data = mj_makeData(model);
|
||||||
|
mj_kinematics(model, data);
|
||||||
|
|
||||||
|
// plane-sphere, distmax too small
|
||||||
|
mjtNum distmax = 0.5;
|
||||||
|
EXPECT_EQ(mj_geomDistance(model, data, 0, 1, distmax, nullptr), 0.5);
|
||||||
|
mjtNum fromto[6];
|
||||||
|
EXPECT_EQ(mj_geomDistance(model, data, 0, 1, distmax, fromto), 0.5);
|
||||||
|
EXPECT_THAT(fromto, Pointwise(Eq(), std::vector<mjtNum>{0, 0, 0, 0, 0, 0}));
|
||||||
|
|
||||||
|
// plane-sphere
|
||||||
|
distmax = 1.0;
|
||||||
|
EXPECT_DOUBLE_EQ(mj_geomDistance(model, data, 0, 1, 1.0, fromto), 0.8);
|
||||||
|
mjtNum eps = 1e-12;
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{0, 0, 0, 0, 0, 0.8}));
|
||||||
|
|
||||||
|
// sphere-plane
|
||||||
|
EXPECT_DOUBLE_EQ(mj_geomDistance(model, data, 1, 0, 1.0, fromto), 0.8);
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{0, 0, 0.8, 0, 0, 0}));
|
||||||
|
|
||||||
|
// sphere-sphere
|
||||||
|
EXPECT_DOUBLE_EQ(mj_geomDistance(model, data, 1, 2, 1.0, fromto), 0.5);
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{.2, 0, 1, .7, 0, 1}));
|
||||||
|
|
||||||
|
// sphere-sphere, flipped order
|
||||||
|
EXPECT_DOUBLE_EQ(mj_geomDistance(model, data, 2, 1, 1.0, fromto), 0.5);
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{.7, 0, 1, .2, 0, 1}));
|
||||||
|
|
||||||
|
// TODO: b/339596989 - Improve the bounds below (mjc_Convex).
|
||||||
|
|
||||||
|
// mesh-sphere (close distmax)
|
||||||
|
distmax = 0.701;
|
||||||
|
eps = 1e-5;
|
||||||
|
EXPECT_THAT(mj_geomDistance(model, data, 3, 1, distmax, fromto),
|
||||||
|
DoubleNear(0.7, eps));
|
||||||
|
eps = 1e-3;
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{0, 0, .1, 0, 0, .8}));
|
||||||
|
|
||||||
|
// mesh-sphere (far distmax)
|
||||||
|
distmax = 1.0;
|
||||||
|
eps = 1e-3;
|
||||||
|
EXPECT_THAT(mj_geomDistance(model, data, 3, 1, distmax, fromto),
|
||||||
|
DoubleNear(0.7, eps));
|
||||||
|
eps = 2e-2;
|
||||||
|
EXPECT_THAT(fromto, Pointwise(DoubleNear(eps),
|
||||||
|
std::vector<mjtNum>{0, 0, .1, 0, 0, .8}));
|
||||||
|
|
||||||
|
mj_deleteData(data);
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<mujoco model="Body-Body collisions fromto">
|
||||||
|
<option>
|
||||||
|
<flag gravity="disable"/>
|
||||||
|
</option>
|
||||||
|
|
||||||
|
<worldbody>
|
||||||
|
<light pos=".5 0 3"/>
|
||||||
|
<light pos="-.5 0 3"/>
|
||||||
|
<geom type="plane" size="2 2 .01"/>
|
||||||
|
|
||||||
|
<body name="1" pos="0 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<replicate count="50" offset="0 0 0.01" euler="0 0 25">
|
||||||
|
<geom type="capsule" size=".01 .05" pos="0 -.23 -.2" euler="0 84.5 0" rgba="0 0 1 1"/>
|
||||||
|
</replicate>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body name="2" pos=".6 0 .3" euler="0 0 90">
|
||||||
|
<freejoint/>
|
||||||
|
<replicate count="50" offset="0 0 0.01" euler="0 0 15">
|
||||||
|
<geom type="capsule" pos=".025 -.2 -.2" size=".032 .04" euler="-24 -12 2" rgba=".8 0 0 1"/>
|
||||||
|
</replicate>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
|
||||||
|
<sensor>
|
||||||
|
<fromto body1="1" body2="2" cutoff="1"/>
|
||||||
|
</sensor>
|
||||||
|
</mujoco>
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
<mujoco model="Convex-Convex collisions">
|
||||||
|
<option>
|
||||||
|
<flag gravity="disable"/>
|
||||||
|
</option>
|
||||||
|
|
||||||
|
<asset>
|
||||||
|
<mesh name="halfbox1" scale=".2 .3 .1"
|
||||||
|
vertex="0 0 0 1 0 0 0 1 0 1 1 0
|
||||||
|
0 0 1 1 0 1 0 1 1 "/>
|
||||||
|
<mesh name="halfbox2" scale=".2 .3 .1"
|
||||||
|
vertex="1 0 0 0 1 0 1 1 0
|
||||||
|
0 0 1 1 0 1 0 1 1 "/>
|
||||||
|
<hfield name="hfield" nrow="3" ncol="4" size="1 1 .1 .08"
|
||||||
|
elevation="1 0 1 2
|
||||||
|
0 2 1 1
|
||||||
|
1 0 1 0"/>
|
||||||
|
</asset>
|
||||||
|
|
||||||
|
<default>
|
||||||
|
<geom rgba=".2 .2 .6 1"/>
|
||||||
|
</default>
|
||||||
|
|
||||||
|
<worldbody>
|
||||||
|
<light pos=".5 0 3"/>
|
||||||
|
<light pos="-.5 0 3"/>
|
||||||
|
|
||||||
|
<geom name="hfield" type="hfield" hfield="hfield" rgba="0.5 0.5 0.5 1"/>
|
||||||
|
|
||||||
|
<body pos=".2 0 .6">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="sphere" type="sphere" size=".15"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="0 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="ellipsoid" type="ellipsoid" size=".1 .1 .2"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos=".4 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="cylinder" type="cylinder" size=".1 .15"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="box" type="box" size=".2 .15 .1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 0 .6">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="mesh1" type="mesh" mesh="halfbox1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 -.6 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="mesh2" type="mesh" mesh="halfbox2"/>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
|
||||||
|
<sensor>
|
||||||
|
<fromto geom1="ellipsoid" geom2="box" cutoff="1"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="cylinder" cutoff="1"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="mesh2" cutoff="1"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="hfield" cutoff="1"/>
|
||||||
|
<fromto geom1="mesh1" geom2="sphere" cutoff="1"/>
|
||||||
|
<fromto geom1="mesh1" geom2="box" cutoff="1"/>
|
||||||
|
<fromto geom1="mesh1" geom2="mesh2" cutoff="1"/>
|
||||||
|
<fromto geom1="hfield" geom2="sphere" cutoff="1"/>
|
||||||
|
<fromto geom1="hfield" geom2="box" cutoff="1"/>
|
||||||
|
<fromto geom1="hfield" geom2="mesh1" cutoff="1"/>
|
||||||
|
|
||||||
|
<fromto geom1="ellipsoid" geom2="box" cutoff="0.2"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="cylinder" cutoff="0.2"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="mesh2" cutoff="0.2"/>
|
||||||
|
<fromto geom1="ellipsoid" geom2="hfield" cutoff="0.2"/>
|
||||||
|
<fromto geom1="mesh1" geom2="sphere" cutoff="0.2"/>
|
||||||
|
<fromto geom1="mesh1" geom2="box" cutoff="0.2"/>
|
||||||
|
<fromto geom1="mesh1" geom2="mesh2" cutoff="0.2"/>
|
||||||
|
<fromto geom1="hfield" geom2="sphere" cutoff="0.2"/>
|
||||||
|
<fromto geom1="hfield" geom2="box" cutoff="0.2"/>
|
||||||
|
<fromto geom1="hfield" geom2="mesh1" cutoff="0.2"/>
|
||||||
|
</sensor>
|
||||||
|
</mujoco>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<mujoco model="Primitive collisions">
|
||||||
|
<asset>
|
||||||
|
<mesh name="halfbox" scale=".2 .3 .1"
|
||||||
|
vertex="0 0 0 1 0 0 0 1 0 1 1 0
|
||||||
|
0 0 1 1 0 1 0 1 1 "/>
|
||||||
|
</asset>
|
||||||
|
|
||||||
|
|
||||||
|
<default>
|
||||||
|
<geom rgba=".2 .2 .6 1"/>
|
||||||
|
</default>
|
||||||
|
|
||||||
|
<worldbody>
|
||||||
|
<light pos=".5 0 3"/>
|
||||||
|
<light pos="-.5 0 3"/>
|
||||||
|
<geom name="plane" type="plane" size="2 2 .01" rgba="0.5 0.5 0.5 1"/>
|
||||||
|
|
||||||
|
<body pos=".2 0 .6">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="sphere" type="sphere" size=".15"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="0 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="capsule1" type="capsule" size=".1 .1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.2 0 .4" euler="90 0 0">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="capsule2" type="capsule" size=".05 .1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos=".4 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="cylinder" type="cylinder" size=".1 .15"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 0 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="box1" type="box" size=".2 .15 .1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 0 .6">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="box2" type="box" size=".1 .05 .1"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<body pos="-.5 -.6 .3">
|
||||||
|
<freejoint/>
|
||||||
|
<geom name="mesh" type="mesh" mesh="halfbox"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</worldbody>
|
||||||
|
|
||||||
|
<sensor>
|
||||||
|
<fromto geom1="plane" geom2="sphere" cutoff="1"/>
|
||||||
|
<fromto geom1="plane" geom2="capsule1" cutoff="1"/>
|
||||||
|
<fromto geom1="plane" geom2="box1" cutoff="1"/>
|
||||||
|
<fromto geom1="plane" geom2="cylinder" cutoff="1"/>
|
||||||
|
<fromto geom1="plane" geom2="mesh" cutoff="1"/>
|
||||||
|
|
||||||
|
<fromto geom1="capsule2" geom2="capsule1" cutoff="1"/>
|
||||||
|
<fromto geom1="capsule1" geom2="sphere" cutoff="1"/>
|
||||||
|
|
||||||
|
<fromto geom1="sphere" geom2="cylinder" cutoff="1"/>
|
||||||
|
|
||||||
|
<fromto geom1="box1" geom2="sphere" cutoff="1"/>
|
||||||
|
<fromto geom1="box2" geom2="capsule2" cutoff="1"/>
|
||||||
|
<fromto geom1="box1" geom2="box2" cutoff="1"/>
|
||||||
|
</sensor>
|
||||||
|
</mujoco>
|
||||||
@@ -542,7 +542,7 @@ TEST_F(RelativeFrameSensorParsingTest, BadRefName) {
|
|||||||
std::array<char, 1024> error;
|
std::array<char, 1024> error;
|
||||||
LoadModelFromString(xml, error.data(), error.size());
|
LoadModelFromString(xml, error.data(), error.size());
|
||||||
EXPECT_THAT(error.data(),
|
EXPECT_THAT(error.data(),
|
||||||
HasSubstr("unrecognized name 'wrong_name' of reference frame"));
|
HasSubstr("unrecognized name 'wrong_name' of object"));
|
||||||
EXPECT_THAT(error.data(), HasSubstr("line 8"));
|
EXPECT_THAT(error.data(), HasSubstr("line 8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +601,7 @@ TEST_F(RelativeFrameSensorParsingTest, BadObjRefName) {
|
|||||||
ASSERT_THAT(model, IsNull());
|
ASSERT_THAT(model, IsNull());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
error.data(),
|
error.data(),
|
||||||
HasSubstr("unrecognized name 'alessio' of reference frame object"));
|
HasSubstr("unrecognized name 'alessio' of object"));
|
||||||
EXPECT_THAT(error.data(), HasSubstr("name 'tom'"));
|
EXPECT_THAT(error.data(), HasSubstr("name 'tom'"));
|
||||||
EXPECT_THAT(error.data(), HasSubstr("line 7"));
|
EXPECT_THAT(error.data(), HasSubstr("line 7"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,9 +358,12 @@ public enum mjtSensor : int{
|
|||||||
mjSENS_SUBTREECOM = 34,
|
mjSENS_SUBTREECOM = 34,
|
||||||
mjSENS_SUBTREELINVEL = 35,
|
mjSENS_SUBTREELINVEL = 35,
|
||||||
mjSENS_SUBTREEANGMOM = 36,
|
mjSENS_SUBTREEANGMOM = 36,
|
||||||
mjSENS_CLOCK = 37,
|
mjSENS_GEOMDIST = 37,
|
||||||
mjSENS_PLUGIN = 38,
|
mjSENS_GEOMNORMAL = 38,
|
||||||
mjSENS_USER = 39,
|
mjSENS_GEOMFROMTO = 39,
|
||||||
|
mjSENS_CLOCK = 40,
|
||||||
|
mjSENS_PLUGIN = 41,
|
||||||
|
mjSENS_USER = 42,
|
||||||
}
|
}
|
||||||
public enum mjtStage : int{
|
public enum mjtStage : int{
|
||||||
mjSTAGE_NONE = 0,
|
mjSTAGE_NONE = 0,
|
||||||
@@ -6668,6 +6671,9 @@ public static unsafe extern void mj_objectVelocity(mjModel_* m, mjData_* d, int
|
|||||||
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static unsafe extern void mj_objectAcceleration(mjModel_* m, mjData_* d, int objtype, int objid, double* res, int flg_local);
|
public static unsafe extern void mj_objectAcceleration(mjModel_* m, mjData_* d, int objtype, int objid, double* res, int flg_local);
|
||||||
|
|
||||||
|
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static unsafe extern double mj_geomDistance(mjModel_* m, mjData_* d, int geom1, int geom2, double distmax, double* fromto);
|
||||||
|
|
||||||
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static unsafe extern void mj_contactForce(mjModel_* m, mjData_* d, int id, double* result);
|
public static unsafe extern void mj_contactForce(mjModel_* m, mjData_* d, int id, double* result);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user