Particles with arbitrary geoms and joints.
Fixes #553 PiperOrigin-RevId: 486614769 Change-Id: I9a09bf73da97b6b9746800a72ac6c97cce2efa92
This commit is contained in:
committed by
Copybara-Service
parent
fd4d47e4a5
commit
3b89b0fd30
+11
-6
@@ -2296,10 +2296,11 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
|
||||
This attribute determines the type of composite object. The remaining attributes and sub-elements are then
|
||||
interpreted according to the type. Default settings are also adjusted depending on the type.
|
||||
|
||||
The **particle** type creates a 1D, 2D or 3D grid of equally-spaced bodies. Each body has a single sphere geom and 3
|
||||
orthogonal sliding joints, allowing translation but not rotation. The geom condim and priority attributes are set to
|
||||
1 by default. This makes the spheres have frictionless contacts with all other geoms (unless the priority of some
|
||||
frictional geom is higher).
|
||||
The **particle** type creates a 1D, 2D or 3D grid of equally-spaced bodies. By default, each body has a single sphere
|
||||
geom and 3 orthogonal sliding joints, allowing translation but not rotation. The geom condim and priority attributes
|
||||
are set to 1 by default. This makes the spheres have frictionless contacts with all other geoms (unless the priority
|
||||
of some frictional geom is higher). The user can replace the default sliders with multiple joints of kind="particle"
|
||||
and replace the default sphere with a custom geom.
|
||||
|
||||
The **grid** type creates a 1D or 2D grid of bodies, each having a sphere geom, a sphere site, and 3 orthogonal
|
||||
sliding joints by default. The :el:`pin` sub-element can be used to specify that some bodies should not have joints,
|
||||
@@ -2403,7 +2404,7 @@ Depending on the composite type, some joints are created automatically (e.g. the
|
||||
joints are optional (e.g. the stretch and twist joints in rope). This sub-element is used to specify which optional
|
||||
joints should be created, as well as to adjust the attributes of both automatic and optional joints.
|
||||
|
||||
:at:`kind`: :at-val:`[main, twist, stretch], required`
|
||||
:at:`kind`: :at-val:`[main, twist, stretch, particle], required`
|
||||
The joint kind here is orthogonal to the joint type in the rest of MJCF. The joint kind refers to the function of the
|
||||
joint within the mechanism comprising the composite body, while the joint type (hinge or slide) is implied by the
|
||||
joint kind and composite body type.
|
||||
@@ -2420,6 +2421,10 @@ joints should be created, as well as to adjust the attributes of both automatic
|
||||
The **stretch** kind corresponds to slide joints enabling rope, loop and cloth objects to stretch. These are optional
|
||||
joints and are only created if this sub-element is present. This sub-element is also used to adjust the attributes of
|
||||
the optional stretch joints. For other composite object types this sub-element has no effect.
|
||||
|
||||
The **particle** kind can only be used with the particle composite type. As opposed to all previous kinds, this kind
|
||||
*replaces* the default 3 sliders with user-defined joints. User-defined joints can be repeated, for example
|
||||
to create planar particles with two sliders and a hinge.
|
||||
:at:`solreffix`, :at:`solimpfix`
|
||||
These are the solref and solimp attributes used to equality-constrain the joint. Whether or not a given joint is
|
||||
quality-constrained depends on the joint kind and composite object type as explained above. For joints that are not
|
||||
@@ -2429,7 +2434,7 @@ joints should be created, as well as to adjust the attributes of both automatic
|
||||
|
||||
.. |body/composite/joint attrib list| replace::
|
||||
:at:`group`, :at:`stiffness`, :at:`damping`, :at:`armature`, :at:`limited`, :at:`range`, :at:`margin`,
|
||||
:at:`solreflimit`, :at:`solimplimit`, :at:`frictionloss`, :at:`solreffriction`, :at:`solimpfriction`
|
||||
:at:`solreflimit`, :at:`solimplimit`, :at:`frictionloss`, :at:`solreffriction`, :at:`solimpfriction`, :at:`type`
|
||||
|
||||
|body/composite/joint attrib list|
|
||||
Same meaning as regular :ref:`joint <body-joint>` attributes.
|
||||
|
||||
+5
-3
@@ -746,11 +746,13 @@
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
| | | | :at:`damping` | :at:`armature` | :at:`solreffix` | |
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
| | | | :at:`solimpfix` | :at:`limited` | :at:`range` | |
|
||||
| | | | :at:`solimpfix` | :at:`type` | :at:`axis` | |
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
| | | | :at:`margin` | :at:`solreflimit` | :at:`solimplimit` | |
|
||||
| | | | :at:`limited` | :at:`range` | :at:`margin` | |
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
| | | | :at:`frictionloss` | :at:`solreffriction` | :at:`solimpfriction` | |
|
||||
| | | | :at:`solreflimit` | :at:`solimplimit` | :at:`frictionloss` | |
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
| | | | :at:`solreffriction` | :at:`solimpfriction` | | |
|
||||
| | | +-------------------------+-------------------------+-------------------------+ |
|
||||
+--------------------------+----+------------------------------------------------------------------------------------+
|
||||
| |_3|:ref:`tendon | \* | .. table:: |
|
||||
|
||||
@@ -34,6 +34,11 @@ General
|
||||
for dyntype :at-val:`user`, as native activation dynamics are all scalar. Added example test implementing 2nd-order
|
||||
activation dynamics to
|
||||
`engine_forward_test.cc <https://github.com/deepmind/mujoco/blob/main/test/engine/engine_forward_test.cc>`_.
|
||||
- Improved particle :ref:`composite<body-composite>` type, which now permits a user-specified geometry and multiple
|
||||
joints. See the two new examples:
|
||||
`particle_free.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free.xml>`_ and
|
||||
`particle_free2d.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free2d.xml>`_
|
||||
|
||||
Version 2.3.0 (October 18, 2022)
|
||||
--------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<!-- Copyright 2021 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Particle">
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".01"/>
|
||||
|
||||
<size nconmax="6000" njmax="6000" nstack="50000000"/>
|
||||
|
||||
<visual>
|
||||
<map stiffness="100"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<default class="wall">
|
||||
<geom type="plane" size=".5 .5 .05"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom name="+x" class="wall" zaxis="1 0 0" pos="-.5 0 -.25"/>
|
||||
<geom name="-x" class="wall" zaxis="-1 0 0" pos=".5 0 -.25"/>
|
||||
<geom name="+y" class="wall" zaxis="0 1 0" pos="0 -.5 -.25"/>
|
||||
<geom name="-y" class="wall" zaxis="0 -1 0" pos="0 .5 -.25"/>
|
||||
<composite type="particle" count="3 3 3" spacing="0.07" offset="0 0 1">
|
||||
<geom type="box" size=".025 .025 .025" rgba=".8 .2 .1 1" condim="3"/>
|
||||
<joint kind="particle" type="free"/>
|
||||
</composite>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!-- Copyright 2021 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<mujoco model="Particle">
|
||||
<include file="scene.xml"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".01"/>
|
||||
|
||||
<size nconmax="6000" njmax="6000" nstack="50000000"/>
|
||||
|
||||
<visual>
|
||||
<map stiffness="100"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<default class="wall">
|
||||
<geom type="plane" size=".5 .5 .05"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom name="+x" class="wall" zaxis="1 0 0" pos="-.5 0 -.25"/>
|
||||
<geom name="-x" class="wall" zaxis="-1 0 0" pos=".5 0 -.25"/>
|
||||
<geom name="+y" class="wall" zaxis="0 1 0" pos="0 -.5 -.25"/>
|
||||
<geom name="-y" class="wall" zaxis="0 -1 0" pos="0 .5 -.25"/>
|
||||
<composite type="particle" count="15 15 1" spacing="0.06" offset="0 0 .1">
|
||||
<geom type="ellipsoid" size=".025 .02 .015" rgba=".8 .2 .1 1"/>
|
||||
<joint kind="particle" type="slide" axis="1 0 0"/>
|
||||
<joint kind="particle" type="slide" axis="0 1 0"/>
|
||||
<joint kind="particle" type="hinge" axis="0 0 1"/>
|
||||
</composite>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
+48
-20
@@ -102,6 +102,23 @@ void mjCComposite::AdjustSoft(mjtNum* solref, mjtNum* solimp, int level) {
|
||||
|
||||
|
||||
|
||||
// create the array of default joint options, append new elements only for particles type
|
||||
bool mjCComposite::AddDefaultJoint(char* error, int error_sz) {
|
||||
for (int i=0; i<mjNCOMPKINDS; i++) {
|
||||
if (!defjoint[(mjtCompKind)i].empty() && type!=mjCOMPTYPE_PARTICLE) {
|
||||
comperr(error, "Only particles are allowed to have multiple joints", error_sz);
|
||||
return false;
|
||||
} else {
|
||||
mjCDef jnt;
|
||||
jnt.joint.group = 3;
|
||||
defjoint[(mjtCompKind)i].push_back(jnt);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// set defaults, after reading top-level info and skin
|
||||
void mjCComposite::SetDefault(void) {
|
||||
int i;
|
||||
@@ -118,10 +135,12 @@ void mjCComposite::SetDefault(void) {
|
||||
for (int i=0; i<mjNCOMPKINDS; i++) {
|
||||
def[i].geom.group = 3;
|
||||
def[i].site.group = 3;
|
||||
def[i].joint.group = 3;
|
||||
def[i].tendon.group = 3;
|
||||
}
|
||||
|
||||
// set default joint
|
||||
AddDefaultJoint();
|
||||
|
||||
// set default geom and tendon group to 0 if needed to be visible
|
||||
if (!skin ||
|
||||
type==mjCOMPTYPE_PARTICLE ||
|
||||
@@ -204,7 +223,7 @@ bool mjCComposite::Make(mjCModel* model, mjCBody* body, char* error, int error_s
|
||||
if ((def[0].geom.type!=mjGEOM_SPHERE &&
|
||||
def[0].geom.type!=mjGEOM_CAPSULE &&
|
||||
def[0].geom.type!=mjGEOM_ELLIPSOID) &&
|
||||
type!=mjCOMPTYPE_CABLE) {
|
||||
type!=mjCOMPTYPE_PARTICLE && type!=mjCOMPTYPE_CABLE) {
|
||||
return comperr(error, "Composite geom type must be sphere, capsule or ellipsoid", error_sz);
|
||||
}
|
||||
|
||||
@@ -303,20 +322,29 @@ bool mjCComposite::MakeParticle(mjCModel* model, mjCBody* body, char* error, int
|
||||
b->pos[1] = offset[1] + spacing*(iy - 0.5*count[1]);
|
||||
b->pos[2] = offset[2] + spacing*(iz - 0.5*count[2]);
|
||||
|
||||
// add slider joints
|
||||
for (int i=0; i<3; i++) {
|
||||
mjCJoint* jnt = b->AddJoint(def + mjCOMPKIND_JOINT, false);
|
||||
jnt->def = body->def;
|
||||
jnt->type = mjJNT_SLIDE;
|
||||
mjuu_setvec(jnt->pos, 0, 0, 0);
|
||||
mjuu_setvec(jnt->axis, 0, 0, 0);
|
||||
jnt->axis[i] = 1;
|
||||
// add slider joints if none defined
|
||||
if (!add[mjCOMPKIND_PARTICLE]) {
|
||||
for (int i=0; i<3; i++) {
|
||||
mjCJoint* jnt = b->AddJoint(&defjoint[mjCOMPKIND_JOINT][0], false);
|
||||
jnt->def = body->def;
|
||||
jnt->type = mjJNT_SLIDE;
|
||||
mjuu_setvec(jnt->pos, 0, 0, 0);
|
||||
mjuu_setvec(jnt->axis, 0, 0, 0);
|
||||
jnt->axis[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// add user-specified joints
|
||||
else {
|
||||
for (auto defjnt : defjoint[mjCOMPKIND_PARTICLE]) {
|
||||
mjCJoint* jnt = b->AddJoint(&defjnt, false);
|
||||
jnt->def = body->def;
|
||||
}
|
||||
}
|
||||
|
||||
// add geom
|
||||
mjCGeom* g = b->AddGeom(def);
|
||||
g->def = body->def;
|
||||
g->type = mjGEOM_SPHERE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,7 +415,7 @@ bool mjCComposite::MakeGrid(mjCModel* model, mjCBody* body, char* error, int err
|
||||
// add slider joint
|
||||
mjCJoint* jnt[3];
|
||||
for (int i=0; i<3; i++) {
|
||||
jnt[i] = b->AddJoint(def + mjCOMPKIND_JOINT);
|
||||
jnt[i] = b->AddJoint(&defjoint[mjCOMPKIND_JOINT][0]);
|
||||
jnt[i]->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJ%d_%d_%d", prefix.c_str(), i, ix, iy);
|
||||
jnt[i]->name = txt;
|
||||
@@ -611,7 +639,7 @@ mjCBody* mjCComposite::AddCableBody(mjCModel* model, mjCBody* body, int ix, mjtN
|
||||
|
||||
// add curvature joint
|
||||
if (!first || strcmp(initial.c_str(), "none")) {
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_JOINT);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_JOINT][0]);
|
||||
jnt->def = body->def;
|
||||
jnt->type = (first && strcmp(initial.c_str(), "free")==0) ? mjJNT_FREE : mjJNT_BALL;
|
||||
jnt->damping = jnt->type==mjJNT_FREE ? 0 : jnt->damping;
|
||||
@@ -754,7 +782,7 @@ mjCBody* mjCComposite::AddRopeBody(mjCModel* model, mjCBody* body, int ix, int i
|
||||
// add main joint
|
||||
for (int i=0; i<2; i++) {
|
||||
// add joint
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_JOINT);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_JOINT][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJ%d_%d", prefix.c_str(), i, ix1);
|
||||
jnt->name = txt;
|
||||
@@ -767,7 +795,7 @@ mjCBody* mjCComposite::AddRopeBody(mjCModel* model, mjCBody* body, int ix, int i
|
||||
// add twist joint
|
||||
if (add[mjCOMPKIND_TWIST]) {
|
||||
// add joint
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_TWIST);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_TWIST][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJT%d", prefix.c_str(), ix1);
|
||||
jnt->name = txt;
|
||||
@@ -785,7 +813,7 @@ mjCBody* mjCComposite::AddRopeBody(mjCModel* model, mjCBody* body, int ix, int i
|
||||
// add stretch joint
|
||||
if (add[mjCOMPKIND_STRETCH]) {
|
||||
// add joint
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_STRETCH);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_STRETCH][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJS%d", prefix.c_str(), ix1);
|
||||
jnt->name = txt;
|
||||
@@ -1008,7 +1036,7 @@ mjCBody* mjCComposite::AddClothBody(mjCModel* model, mjCBody* body,
|
||||
// add main joint
|
||||
for (int i=0; i<2; i++) {
|
||||
// add joint
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_JOINT);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_JOINT][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJ%d_%d_%d", prefix.c_str(), i, ix1, iy1);
|
||||
jnt->name = txt;
|
||||
@@ -1026,7 +1054,7 @@ mjCBody* mjCComposite::AddClothBody(mjCModel* model, mjCBody* body,
|
||||
|
||||
// add twist joint
|
||||
if (add[mjCOMPKIND_TWIST]) {
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_TWIST);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_TWIST][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJT%d_%d", prefix.c_str(), ix1, iy1);
|
||||
jnt->name = txt;
|
||||
@@ -1048,7 +1076,7 @@ mjCBody* mjCComposite::AddClothBody(mjCModel* model, mjCBody* body,
|
||||
// add stretch joint
|
||||
if (add[mjCOMPKIND_STRETCH]) {
|
||||
// add joint
|
||||
mjCJoint* jnt = body->AddJoint(def + mjCOMPKIND_STRETCH);
|
||||
mjCJoint* jnt = body->AddJoint(&defjoint[mjCOMPKIND_STRETCH][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJS%d_%d", prefix.c_str(), ix1, iy1);
|
||||
jnt->name = txt;
|
||||
@@ -1174,7 +1202,7 @@ bool mjCComposite::MakeBox(mjCModel* model, mjCBody* body, char* error, int erro
|
||||
}
|
||||
|
||||
// add slider joint
|
||||
mjCJoint* jnt = b->AddJoint(def + mjCOMPKIND_JOINT);
|
||||
mjCJoint* jnt = b->AddJoint(&defjoint[mjCOMPKIND_JOINT][0]);
|
||||
jnt->def = body->def;
|
||||
mju::sprintf_arr(txt, "%sJ%d_%d_%d", prefix.c_str(), ix, iy, iz);
|
||||
jnt->name = txt;
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#ifndef MUJOCO_SRC_USER_USER_COMPOSITE_H_
|
||||
#define MUJOCO_SRC_USER_USER_COMPOSITE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/mjmodel.h>
|
||||
@@ -44,6 +44,7 @@ typedef enum _mjtCompKind {
|
||||
mjCOMPKIND_STRETCH,
|
||||
mjCOMPKIND_TENDON,
|
||||
mjCOMPKIND_SHEAR,
|
||||
mjCOMPKIND_PARTICLE,
|
||||
|
||||
mjNCOMPKINDS
|
||||
} mjtCompKind;
|
||||
@@ -64,6 +65,7 @@ class mjCComposite {
|
||||
mjCComposite(void);
|
||||
|
||||
void SetDefault(void);
|
||||
bool AddDefaultJoint(char* error = NULL, int error_sz = 0);
|
||||
void AdjustSoft(mjtNum* solref, mjtNum* solimp, int level);
|
||||
|
||||
bool Make(mjCModel* model, mjCBody* body, char* error, int error_sz);
|
||||
@@ -123,8 +125,9 @@ class mjCComposite {
|
||||
int skingroup; // skin group of the composite object
|
||||
|
||||
// element options
|
||||
bool add[mjNCOMPKINDS]; // add element
|
||||
mjCDef def[mjNCOMPKINDS]; // defaults
|
||||
bool add[mjNCOMPKINDS]; // add element
|
||||
mjCDef def[mjNCOMPKINDS]; // default geom, site, tendon
|
||||
std::unordered_map<mjtCompKind, std::vector<mjCDef> > defjoint; // default joints
|
||||
|
||||
// computed internally
|
||||
int dim; // dimensionality
|
||||
|
||||
@@ -263,8 +263,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
{"<"},
|
||||
{"config", "*", "2", "key", "value"},
|
||||
{">"},
|
||||
{"joint", "*", "15", "kind", "group", "stiffness", "damping", "armature",
|
||||
"solreffix", "solimpfix",
|
||||
{"joint", "*", "17", "kind", "group", "stiffness", "damping", "armature",
|
||||
"solreffix", "solimpfix", "type", "axis",
|
||||
"limited", "range", "margin", "solreflimit", "solimplimit",
|
||||
"frictionloss", "solreffriction", "solimpfriction"},
|
||||
{"tendon", "*", "17", "kind", "group", "stiffness", "damping",
|
||||
@@ -670,10 +670,11 @@ const mjMap comp_map[mjNCOMPTYPES] = {
|
||||
|
||||
|
||||
// composite joint kind
|
||||
const mjMap jkind_map[3] = {
|
||||
const mjMap jkind_map[4] = {
|
||||
{"main", mjCOMPKIND_JOINT},
|
||||
{"twist", mjCOMPKIND_TWIST},
|
||||
{"stretch", mjCOMPKIND_STRETCH}
|
||||
{"stretch", mjCOMPKIND_STRETCH},
|
||||
{"particle", mjCOMPKIND_PARTICLE}
|
||||
};
|
||||
|
||||
|
||||
@@ -1978,28 +1979,45 @@ void mjXReader::OneComposite(XMLElement* elem, mjCBody* pbody, mjCDef* def) {
|
||||
while (ejnt) {
|
||||
// kind
|
||||
int kind;
|
||||
MapValue(ejnt, "kind", &kind, jkind_map, 3, true);
|
||||
MapValue(ejnt, "kind", &kind, jkind_map, 4, true);
|
||||
|
||||
// create a new element if this kind already exists
|
||||
if (comp.add[kind]) {
|
||||
char error[200];
|
||||
if (!comp.AddDefaultJoint(error, 200)) {
|
||||
throw mjXError(elem, error);
|
||||
}
|
||||
}
|
||||
comp.add[kind] = true;
|
||||
|
||||
// get element
|
||||
mjCDef *el = &comp.defjoint[(mjtCompKind)kind].back();
|
||||
|
||||
// particle joint
|
||||
if (MapValue(ejnt, "type", &n, joint_map, joint_sz)) {
|
||||
el->joint.type = (mjtJoint)n;
|
||||
}
|
||||
ReadAttr(ejnt, "axis", 3, el->joint.axis, text);
|
||||
|
||||
// solreffix, solimpfix
|
||||
ReadAttr(ejnt, "solreffix", mjNREF, comp.def[kind].equality.solref, text, false, false);
|
||||
ReadAttr(ejnt, "solimpfix", mjNIMP, comp.def[kind].equality.solimp, text, false, false);
|
||||
ReadAttr(ejnt, "solreffix", mjNREF, el->equality.solref, text, false, false);
|
||||
ReadAttr(ejnt, "solimpfix", mjNIMP, el->equality.solimp, text, false, false);
|
||||
|
||||
// joint attributes
|
||||
MapValue(elem, "limited", &comp.def[kind].joint.limited, TFAuto_map, 3);
|
||||
ReadAttrInt(ejnt, "group", &comp.def[kind].joint.group);
|
||||
ReadAttr(ejnt, "solreflimit", mjNREF, comp.def[kind].joint.solref_limit, text, false, false);
|
||||
ReadAttr(ejnt, "solimplimit", mjNIMP, comp.def[kind].joint.solimp_limit, text, false, false);
|
||||
MapValue(elem, "limited", &el->joint.limited, TFAuto_map, 3);
|
||||
ReadAttrInt(ejnt, "group", &el->joint.group);
|
||||
ReadAttr(ejnt, "solreflimit", mjNREF, el->joint.solref_limit, text, false, false);
|
||||
ReadAttr(ejnt, "solimplimit", mjNIMP, el->joint.solimp_limit, text, false, false);
|
||||
ReadAttr(ejnt,
|
||||
"solreffriction", mjNREF, comp.def[kind].joint.solref_friction, text, false, false);
|
||||
"solreffriction", mjNREF, el->joint.solref_friction, text, false, false);
|
||||
ReadAttr(ejnt,
|
||||
"solimpfriction", mjNIMP, comp.def[kind].joint.solimp_friction, text, false, false);
|
||||
ReadAttr(ejnt, "stiffness", 1, &comp.def[kind].joint.stiffness, text);
|
||||
ReadAttr(ejnt, "range", 2, comp.def[kind].joint.range, text);
|
||||
ReadAttr(ejnt, "margin", 1, &comp.def[kind].joint.margin, text);
|
||||
ReadAttr(ejnt, "armature", 1, &comp.def[kind].joint.armature, text);
|
||||
ReadAttr(ejnt, "damping", 1, &comp.def[kind].joint.damping, text);
|
||||
ReadAttr(ejnt, "frictionloss", 1, &comp.def[kind].joint.frictionloss, text);
|
||||
"solimpfriction", mjNIMP, el->joint.solimp_friction, text, false, false);
|
||||
ReadAttr(ejnt, "stiffness", 1, &el->joint.stiffness, text);
|
||||
ReadAttr(ejnt, "range", 2, el->joint.range, text);
|
||||
ReadAttr(ejnt, "margin", 1, &el->joint.margin, text);
|
||||
ReadAttr(ejnt, "armature", 1, &el->joint.armature, text);
|
||||
ReadAttr(ejnt, "damping", 1, &el->joint.damping, text);
|
||||
ReadAttr(ejnt, "frictionloss", 1, &el->joint.frictionloss, text);
|
||||
|
||||
// advance
|
||||
ejnt = ejnt->NextSiblingElement("joint");
|
||||
|
||||
@@ -20,3 +20,6 @@ target_link_libraries(user_objects_test fixture gmock)
|
||||
|
||||
mujoco_test(user_mesh_test)
|
||||
target_link_libraries(user_mesh_test fixture gmock absl::str_format)
|
||||
|
||||
mujoco_test(user_composite_test)
|
||||
target_link_libraries(user_composite_test fixture gmock)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2021 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Tests for user/user_model.cc.
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <absl/strings/str_format.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "test/fixture.h"
|
||||
|
||||
namespace mujoco {
|
||||
namespace {
|
||||
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::HasSubstr;
|
||||
using UserCompositeTest = MujocoTest;
|
||||
|
||||
// ------------- test automatic inference of nuser_xxx -------------------------
|
||||
|
||||
TEST_F(UserCompositeTest, MultipleJointsNotAllowedUnlessParticle) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<freejoint/>
|
||||
<composite type="box" count="7 7 7" spacing="0.04">
|
||||
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
||||
<joint kind="main" solreffix="0.03 1" solimpfix="0 .1 .01"/>
|
||||
</composite>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(m, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("Only particles are allowed to have multiple joints"));
|
||||
}
|
||||
|
||||
TEST_F(UserCompositeTest, StretchAndTwistAllowed) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body name="B10">
|
||||
<freejoint/>
|
||||
<composite type="rope" count="21 1 1" spacing="0.04">
|
||||
<joint kind="main" damping="0.005"/>
|
||||
<joint kind="stretch" damping="0.005"/>
|
||||
<joint kind="twist" damping="0.005"/>
|
||||
<geom type="capsule" size=".01 .015"/>
|
||||
</composite>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(m, NotNull());
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
Reference in New Issue
Block a user