Add circle to flexcomp and remove rope and loop from composite.

Add pulley example to show a circle use case.

Switch on flex edge rendering by default (no effect when flex skin is rendered).

PiperOrigin-RevId: 700738379
Change-Id: I8469bbf194de01c8dab66c9a51c49879013df141
This commit is contained in:
Alessio Quaglino
2024-11-27 10:13:28 -08:00
committed by Copybara-Service
parent 85c6fc1bdb
commit 300450f8b8
9 changed files with 98 additions and 198 deletions
+8 -15
View File
@@ -2981,20 +2981,6 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
define the tendons). The "main" tendons are parallel to the axes of the grid. In addition one can create diagonal
"shear" tendons, using the :el:`tendon` sub-element. This type is suitable for simulating strings as well as cloth.
The **rope** type creates a 1D grid of bodies, each having a geom with user-defined type (sphere, capsule or
ellipsoid) and 2 hinge joints with axes orthogonal to the grid, creating a universal joint with the previous body.
This corresponds to a kinematic chain which can bend but cannot stretch or twist. In addition, one can specify
stretch and twist joints (slide and hinge respectively) with the :el:`joint` sub-element. When specified, these extra
joints are equality-constrained, but the constraint is soft by default so that some stretch and twist are possible.
The rope can extend in one or both directions from the parent body. To specify the origin of the rope, the parent
body *must* be named so that it fits the automatic naming convention. For example, to make the parent be the first
body in the chain, and assuming we have prefix="C", the parent body should be named "CB0". When the parent is not at
the end, the rope consists of two kinematic chains starting at the parent and extending in opposite directions.
The **loop** type is the same as the rope type except the elements are arranged in a circle, and the first and last
elements are equality-constrained to remain connected (using the "connect" constraint type). The softness of this
equality constraint is adjusted with the attributes solrefsmooth and solimpsmooth.
The **cable** type creates a 1D chain of bodies connected with ball joints, each having a geom with user-defined type
(cylinder, capsule or box). The geometry can either be defined with an array of 3D vertex coordinates :at:`vertex`
or with prescribed functions with the option :at:`curve`. Currently, only linear and trigonometric functions are
@@ -3489,7 +3475,7 @@ saving the XML:
.. _body-flexcomp-type:
:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, mesh, gmsh, direct], "grid"`
:at:`type`: :at-val:`[grid, box, cylinder, ellipsoid, disc, circle, mesh, gmsh, direct], "grid"`
This attribute determines the type of :el:`flexcomp` object. The remaining attributes and sub-elements are then
interpreted according to the type. Default settings are also adjusted depending on the type. Different types
correspond to different methods for specifying the flexcomp points and the stretchable elements that connect them.
@@ -3513,6 +3499,13 @@ saving the XML:
**ellipsoid** is the same as **box**, except the points are projected on the surface of an ellipsoid.
**disc** is the same as **box**, except the points are projected on the surface of a disc. It is only compatible
with :at:`dim=2`.
**circle** is the same as **grid**, except the points are sampled along a circle so that the first and last points
are the same. The radius of the circle is computed such that each segment has the requested spacing. It is only
compatible with :at:`dim=1`.
**mesh** loads the flexcomp points and elements (i.e. triangles) from a mesh file, in the same file formats as mesh
assets. A mesh asset is not actually added to the model. Instead the vertex and face data from the mesh file are used
to populate the point and element data of the flexcomp. :at:`dim` is automatically set to 2. Recall that a mesh asset
+5
View File
@@ -6,6 +6,11 @@ Changelog
Upcoming version (not yet released)
-----------------------------------
General
^^^^^^^
- Removed rope and loop from :ref:`composite<body-composite>`. The user is encouraged to instead use the :at:`cable`
plugin or :ref:`flexcomp<body-flexcomp>`, respectively.
MJX
^^^
- Added muscle actuators.
+51
View File
@@ -0,0 +1,51 @@
<!-- 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="Pulley">
<include file="scene.xml"/>
<option>
<flag gravity="disable"/>
</option>
<statistic center="0 -.3 1" extent="2.5"/>
<worldbody>
<flexcomp name="cable" type="circle" count="40 1 1" spacing=".15 1 1" dim="1"
radius="0.02" pos="0 0 1" rgba="1 0 0 1">
<edge equality="true"/>
</flexcomp>
<body name="left" pos="-.7 0 1">
<joint name="expand_left" type="slide" axis="-1 0 0" range="0 2" damping="20"/>
<joint name="rotate" type="hinge" axis="0 0 1"/>
<geom type="cylinder" size=".2 .2" density="1"/>
</body>
<body name="right" pos=".7 0 1">
<joint name="expand_right" type="slide" axis="1 0 0" range="0 2" damping="20"/>
<joint type="hinge" axis="0 0 1"/>
<geom type="cylinder" size=".2 .2" density="1"/>
</body>
</worldbody>
<actuator>
<position name="expand_left" joint="expand_left" ctrlrange="0 2" kp="100"/>
<position name="expand_right" joint="expand_right" ctrlrange="0 2" kp="100"/>
<velocity name="rotate" joint="rotate" ctrlrange="0 8"/>
</actuator>
<keyframe>
<key ctrl=".7 .7 8"/>
</keyframe>
</mujoco>
+1 -1
View File
@@ -93,7 +93,7 @@ const char* mjVISSTRING[mjNVISFLAG][3] = {
{"Static Body", "1", "D"},
{"Skin", "1", ";"},
{"Flex Vert", "0", ""},
{"Flex Edge", "0", ""},
{"Flex Edge", "1", ""},
{"Flex Face", "0", ""},
{"Flex Skin", "1", ""},
{"Body Tree", "0", "`"},
+7 -172
View File
@@ -309,15 +309,15 @@ bool mjCComposite::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz)
case mjCOMPTYPE_ROPE:
return comperr(error,
"The \"rope\" composite type is deprecated. Please use "
"\"cable\" instead.",
error_sz);
"The \"rope\" composite type is deprecated. Please use "
"\"cable\" instead.",
error_sz);
case mjCOMPTYPE_LOOP:
mju_warning(
"The \"loop\" composite type is deprecated. Please use \"cable\" "
"instead.");
return MakeRope(model, body, error, error_sz);
return comperr(error,
"The \"loop\" composite type is deprecated. Please use "
"\"flexcomp\" instead.",
error_sz);
case mjCOMPTYPE_CABLE:
return MakeCable(model, body, error, error_sz);
@@ -729,171 +729,6 @@ mjsBody* mjCComposite::AddCableBody(mjCModel* model, mjsBody* body, int ix,
}
// make rope
bool mjCComposite::MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz) {
// check dim
if (dim!=1) {
return comperr(error, "Rope must be one-dimensional", error_sz);
}
// check root body name prefix
char txt[200];
mju::sprintf_arr(txt, "%sB", prefix.c_str());
std::string body_name = mjs_getString(body->name);
if (std::strncmp(txt, body_name.substr(0, strlen(txt)).c_str(), mju::sizeof_arr(txt))) {
mju::strcat_arr(txt, " must be the beginning of root body name");
return comperr(error, txt, error_sz);
}
// read origin coordinate from root body
mju::strcpy_arr(txt, body_name.substr(strlen(txt)).c_str());
int ox = -1;
if (sscanf(txt, "%d", &ox)!=1) {
return comperr(error, "Root body name must contain X coordinate", error_sz);
}
if (ox<0 || ox>=count[0]) {
return comperr(error, "Root body coordinate out of range", error_sz);
}
// add origin
AddRopeBody(model, body, ox, ox);
// add elements: right
mjsBody* pbody = body;
for (int ix=ox; ix<count[0]-1; ix++) {
pbody = AddRopeBody(model, pbody, ix, ix+1);
}
// add elements: left
pbody = body;
for (int ix=ox; ix>0; ix--) {
pbody = AddRopeBody(model, pbody, ix, ix-1);
}
// close loop
if (type==mjCOMPTYPE_LOOP) {
char txt2[200];
// add equality constraint
mjsEquality* eq = mjs_addEquality(&model->spec, 0);
eq->type = mjEQ_CONNECT;
mju::sprintf_arr(txt, "%sB0", prefix.c_str());
mju::sprintf_arr(txt2, "%sB%d", prefix.c_str(), count[0]-1);
mjs_setString(eq->name1, txt);
mjs_setString(eq->name2, txt2);
mjuu_setvec(eq->data, -0.5*spacing, 0, 0);
mju_copy(eq->solref, solrefsmooth, mjNREF);
mju_copy(eq->solimp, solimpsmooth, mjNIMP);
// remove contact between connected bodies
mjsExclude* pair = mjs_addExclude(&model->spec);
mjs_setString(pair->bodyname1, std::string(txt).c_str());
mjs_setString(pair->bodyname2, std::string(txt2).c_str());
}
return true;
}
// add child body for cloth
mjsBody* mjCComposite::AddRopeBody(mjCModel* model, mjsBody* body, int ix, int ix1) {
char txt[100];
bool isroot = (ix==ix1);
double dx = spacing*(ix1-ix);
// add child if not root
if (!isroot) {
body = mjs_addBody(body, 0);
mju::sprintf_arr(txt, "%sB%d", prefix.c_str(), ix1);
mjs_setString(body->name, txt);
// loop
if (type==mjCOMPTYPE_LOOP) {
double alpha = 2*mjPI/count[0];
double R = 0.5*spacing*sin(mjPI-alpha)/sin(0.5*alpha);
if (ix1>ix) {
mjuu_setvec(body->pos, R*cos(0.5*alpha), R*sin(0.5*alpha), 0);
mjuu_setvec(body->quat, cos(0.5*alpha), 0, 0, sin(0.5*alpha));
} else {
mjuu_setvec(body->pos, -R*cos(0.5*alpha), R*sin(0.5*alpha), 0);
mjuu_setvec(body->quat, cos(-0.5*alpha), 0, 0, sin(-0.5*alpha));
}
}
// no loop
else {
mjuu_setvec(body->pos, dx, 0, 0);
}
}
// add geom
mjsGeom* geom = mjs_addGeom(body, &def[0].spec);
mjs_setDefault(geom->element, mjs_getDefault(body->element));
mju::sprintf_arr(txt, "%sG%d", prefix.c_str(), ix1);
mjs_setString(geom->name, txt);
mjuu_setvec(geom->pos, 0, 0, 0);
mjuu_setvec(geom->quat, sqrt(0.5), 0, sqrt(0.5), 0);
// root: no joints
if (isroot) {
return body;
}
// add main joint
for (int i=0; i<2; i++) {
// add joint
mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_JOINT][0].spec);
mjs_setDefault(jnt->element, mjs_getDefault(body->element));
mju::sprintf_arr(txt, "%sJ%d_%d", prefix.c_str(), i, ix1);
mjs_setString(jnt->name, txt);
jnt->type = mjJNT_HINGE;
mjuu_setvec(jnt->pos, -0.5*dx, 0, 0);
mjuu_setvec(jnt->axis, 0, 0, 0);
jnt->axis[i+1] = 1;
}
// add twist joint
if (add[mjCOMPKIND_TWIST]) {
// add joint
mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_TWIST][0].spec);
mjs_setDefault(jnt->element, mjs_getDefault(body->element));
mju::sprintf_arr(txt, "%sJT%d", prefix.c_str(), ix1);
mjs_setString(jnt->name, txt);
jnt->type = mjJNT_HINGE;
mjuu_setvec(jnt->pos, -0.5*dx, 0, 0);
mjuu_setvec(jnt->axis, 1, 0, 0);
// add constraint
mjsEquality* eq = mjs_addEquality(&model->spec, &def[mjCOMPKIND_TWIST].spec);
mjs_setDefault(eq->element, &model->Default()->spec);
eq->type = mjEQ_JOINT;
mjs_setString(eq->name1, mjs_getString(jnt->name));
}
// add stretch joint
if (add[mjCOMPKIND_STRETCH]) {
// add joint
mjsJoint* jnt = mjs_addJoint(body, &defjoint[mjCOMPKIND_STRETCH][0].spec);
mjs_setDefault(jnt->element, mjs_getDefault(body->element));
mju::sprintf_arr(txt, "%sJS%d", prefix.c_str(), ix1);
mjs_setString(jnt->name, txt);
jnt->type = mjJNT_SLIDE;
mjuu_setvec(jnt->pos, -0.5*dx, 0, 0);
mjuu_setvec(jnt->axis, 1, 0, 0);
// add constraint
mjsEquality* eq = mjs_addEquality(&model->spec, &def[mjCOMPKIND_STRETCH].spec);
mjs_setDefault(eq->element, &model->Default()->spec);
eq->type = mjEQ_JOINT;
mjs_setString(eq->name1, mjs_getString(jnt->name));
}
return body;
}
// add shear tendons to 2D
void mjCComposite::MakeShear(mjCModel* model) {
-2
View File
@@ -70,7 +70,6 @@ class mjCComposite {
bool MakeParticle(mjCModel* model, mjsBody* body, char* error, int error_sz);
bool MakeGrid(mjCModel* model, mjsBody* body, char* error, int error_sz);
bool MakeRope(mjCModel* model, mjsBody* body, char* error, int error_sz);
bool MakeCable(mjCModel* model, mjsBody* body, char* error, int error_sz);
void MakeShear(mjCModel* model);
@@ -124,7 +123,6 @@ class mjCComposite {
int dim; // dimensionality
private:
mjsBody* AddRopeBody(mjCModel* model, mjsBody* body, int ix, int ix1);
mjsBody* AddCableBody(mjCModel* model, mjsBody* body, int ix, double normal[3], double prev_quat[4]);
// temporary skin vectors
+24 -8
View File
@@ -157,6 +157,7 @@ bool mjCFlexcomp::Make(mjsBody* body, char* error, int error_sz) {
bool res;
switch (type) {
case mjFCOMPTYPE_GRID:
case mjFCOMPTYPE_CIRCLE:
res = MakeGrid(error, error_sz);
break;
@@ -548,15 +549,30 @@ bool mjCFlexcomp::MakeGrid(char* error, int error_sz) {
// 1D
if (dim == 1) {
for (int ix=0; ix < count[0]; ix++) {
// add point
point.push_back(spacing[0]*(ix - 0.5*(count[0]-1)));
point.push_back(0);
point.push_back(0);
if (type == mjFCOMPTYPE_CIRCLE) {
// add point
double theta = 2*mjPI/(count[0]-1);
double radius = spacing[0]/std::sin(theta/2)/2;
point.push_back(radius*std::cos(theta*ix));
point.push_back(radius*std::sin(theta*ix));
point.push_back(0);
// add element
if (ix < count[0]-1) {
element.push_back(ix);
element.push_back(ix+1);
// add element
if (ix < count[0]-1) {
element.push_back(ix);
element.push_back(ix == count[0]-2 ? 0 : ix+1);
}
} else {
// add point
point.push_back(spacing[0]*(ix - 0.5*(count[0]-1)));
point.push_back(0);
point.push_back(0);
// add element
if (ix < count[0]-1) {
element.push_back(ix);
element.push_back(ix+1);
}
}
}
}
+1
View File
@@ -31,6 +31,7 @@ typedef enum _mjtFcompType {
mjFCOMPTYPE_ELLIPSOID,
mjFCOMPTYPE_SQUARE,
mjFCOMPTYPE_DISC,
mjFCOMPTYPE_CIRCLE,
mjFCOMPTYPE_MESH,
mjFCOMPTYPE_GMSH,
mjFCOMPTYPE_DIRECT,
+1
View File
@@ -794,6 +794,7 @@ const mjMap fcomp_map[mjNFCOMPTYPES] = {
{"ellipsoid", mjFCOMPTYPE_ELLIPSOID},
{"square", mjFCOMPTYPE_SQUARE},
{"disc", mjFCOMPTYPE_DISC},
{"circle", mjFCOMPTYPE_CIRCLE},
{"mesh", mjFCOMPTYPE_MESH},
{"gmsh", mjFCOMPTYPE_GMSH},
{"direct", mjFCOMPTYPE_DIRECT}