Remove the Shell plugin and integrate it into the engine.

PiperOrigin-RevId: 760688502
Change-Id: Ia70988d42b7edf571d7cb4a4f48f8fc50b51667d
This commit is contained in:
Alessio Quaglino
2025-05-19 10:50:27 -07:00
committed by Copybara-Service
parent 45fc15b844
commit 7edbdd0ad6
28 changed files with 90 additions and 291 deletions
+3 -3
View File
@@ -4073,9 +4073,9 @@ stress-strain relationship.. See also :ref:`deformable <CDeformable>` objects.
.. _flex-elasticity-elastic2d: .. _flex-elasticity-elastic2d:
:at:`elastic2d`: :at-val:`int, "1"` :at:`elastic2d`: :at-val:`[none, bend, stretch, both], "none"`
Elastic contribution to passive forces of 2D flexes. 0: none, 1: bending only, 2: stretching only, 3: bending and Elastic contribution to passive forces of 2D flexes. "none": none, "bend": bending only, "stretch": stretching only,
stretching "both": bending and stretching.
.. _flex-contact: .. _flex-contact:
+2
View File
@@ -9,6 +9,8 @@ General
^^^^^^^ ^^^^^^^
- Refactored island implementation so that island data is memory-contiguous. This speeds up island processing in the - Refactored island implementation so that island data is memory-contiguous. This speeds up island processing in the
solver and clears the way for the addition of the Newton and PGS solvers (currently only CG is supported). solver and clears the way for the addition of the Newton and PGS solvers (currently only CG is supported).
- Removed the :at:`shell` plugin. This is now supported by :ref:`flexcomp<body-flexcomp>` and is active depending on
the :ref:`elastic2d<body-flexcomp-elasticity-elastic2d>` attribute (on by default).
simulate simulate
^^^^^^^^ ^^^^^^^^
+1 -1
View File
@@ -31,7 +31,7 @@
</body> </body>
<flexcomp type="mesh" file="bunny.obj" pos="0 0 .1" dim="2" euler="90 0 0" <flexcomp type="mesh" file="bunny.obj" pos="0 0 .1" dim="2" euler="90 0 0"
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear"> radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
<elasticity young="1e3" poisson="0.1" damping="0.001"/> <elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="stretch"/>
<contact selfcollide="none" internal="false"/> <contact selfcollide="none" internal="false"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
+1 -1
View File
@@ -37,7 +37,7 @@
</body> </body>
<flexcomp type="mesh" file="bunny_with_uv.obj" pos="0 0 .1" dim="2" euler="90 0 0" <flexcomp type="mesh" file="bunny_with_uv.obj" pos="0 0 .1" dim="2" euler="90 0 0"
radius=".001" material="matsponge" mass=".05" name="softbody" dof="trilinear"> radius=".001" material="matsponge" mass=".05" name="softbody" dof="trilinear">
<elasticity young="1e3" poisson="0.1" damping="0.001"/> <elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="stretch"/>
<contact selfcollide="none" internal="false"/> <contact selfcollide="none" internal="false"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
@@ -26,10 +26,6 @@
<option wind="5 5 0" density="10" solver="CG" tolerance="1e-6"> <option wind="5 5 0" density="10" solver="CG" tolerance="1e-6">
</option> </option>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<worldbody> <worldbody>
<geom name="floor" type="plane" size="0 0 .1"/> <geom name="floor" type="plane" size="0 0 .1"/>
<light diffuse=".6 .6 .6" specular="0.2 0.2 0.2" pos="0 0 4" dir="0 0 -1"/> <light diffuse=".6 .6 .6" specular="0.2 0.2 0.2" pos="0 0 4" dir="0 0 -1"/>
@@ -37,8 +33,7 @@
<flexcomp type="grid" count="9 19 1" spacing=".05 .05 .05" mass="10" <flexcomp type="grid" count="9 19 1" spacing=".05 .05 .05" mass="10"
name="flag" radius="0.001"> name="flag" radius="0.001">
<edge equality="true" damping="0.001"/> <edge equality="true" damping="0.001"/>
<elasticity poisson="0" thickness="1e-2" young="3e6"/> <elasticity poisson="0" thickness="1e-2" young="3e6" elastic2d="none"/>
<plugin plugin="mujoco.elasticity.shell"/>
</flexcomp> </flexcomp>
</body> </body>
</worldbody> </worldbody>
@@ -16,10 +16,6 @@
<mujoco model="Pancake"> <mujoco model="Pancake">
<include file="scene.xml"/> <include file="scene.xml"/>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<option timestep="0.001" solver="CG" tolerance="1e-6" integrator="implicitfast"/> <option timestep="0.001" solver="CG" tolerance="1e-6" integrator="implicitfast"/>
<size memory="10M"/> <size memory="10M"/>
@@ -39,7 +35,7 @@
radius=".01" mass=".5" name="plate" dim="2"> radius=".01" mass=".5" name="plate" dim="2">
<contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/> <contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/>
<edge equality="true" damping="10"/> <edge equality="true" damping="10"/>
<elasticity poisson="0" thickness="8e-3" young="3e5"/> <elasticity poisson="0" thickness="8e-3" young="3e5" elastic2d="bend"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
</mujoco> </mujoco>
@@ -16,10 +16,6 @@
<mujoco model="Plate"> <mujoco model="Plate">
<include file="scene.xml"/> <include file="scene.xml"/>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<option solver="CG" tolerance="1e-6" integrator="implicitfast"/> <option solver="CG" tolerance="1e-6" integrator="implicitfast"/>
<size memory="10M"/> <size memory="10M"/>
@@ -42,8 +38,7 @@
radius=".001" mass="100" name="plate"> radius=".001" mass="100" name="plate">
<contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/> <contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/>
<edge equality="true" damping="0.001"/> <edge equality="true" damping="0.001"/>
<elasticity poisson="0" thickness="1e-2" young="3e7"/> <elasticity poisson="0" thickness="1e-2" young="3e7" elastic2d="bend"/>
<plugin plugin="mujoco.elasticity.shell"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
</mujoco> </mujoco>
@@ -19,10 +19,6 @@
<flag energy="enable"/> <flag energy="enable"/>
</option> </option>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<visual> <visual>
<map force="0.1" zfar="30"/> <map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/> <rgba haze="0.15 0.25 0.35 1"/>
@@ -1418,9 +1414,8 @@
398 399 418 398 399 418
398 376 378"> 398 376 378">
<edge equality="true" damping="0.1"/> <edge equality="true" damping="0.1"/>
<elasticity young="3e5" poisson="0" thickness="8e-3"/> <elasticity young="3e5" poisson="0" thickness="8e-3" elastic2d="bend"/>
<contact solref="0.003"/> <contact solref="0.003"/>
<plugin plugin="mujoco.elasticity.shell"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
@@ -19,10 +19,6 @@
<flag energy="enable"/> <flag energy="enable"/>
</option> </option>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<visual> <visual>
<map force="0.1" zfar="30"/> <map force="0.1" zfar="30"/>
<rgba haze="0.15 0.25 0.35 1"/> <rgba haze="0.15 0.25 0.35 1"/>
@@ -1418,9 +1414,8 @@
398 399 418 398 399 418
398 376 378"> 398 376 378">
<edge equality="true" damping="0.1"/> <edge equality="true" damping="0.1"/>
<elasticity poisson="0.3" young="3e5" thickness="8e-3"/> <elasticity poisson="0.3" young="3e5" thickness="8e-3" elastic2d="bend"/>
<contact solref="0.003" contype="0" conaffinity="0" vertcollide="true"/> <contact solref="0.003" contype="0" conaffinity="0" vertcollide="true"/>
<plugin plugin="mujoco.elasticity.shell"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
+1 -1
View File
@@ -39,7 +39,7 @@
radius=".001" mass="10" name="plate" dim="2"> radius=".001" mass="10" name="plate" dim="2">
<contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/> <contact condim="3" solref="0.01 1" solimp=".95 .99 .0001"/>
<edge equality="false" damping="10"/> <edge equality="false" damping="10"/>
<elasticity young="3e5" poisson="0" thickness="1e-2" elastic2d="2"/> <elasticity young="3e5" poisson="0" thickness="1e-2" elastic2d="stretch"/>
<pin id="0 15 240 255"/> <pin id="0 15 240 255"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
-2
View File
@@ -22,8 +22,6 @@ set(MUJOCO_ELASTICITY_SRCS
elasticity.cc elasticity.cc
elasticity.h elasticity.h
register.cc register.cc
shell.cc
shell.h
) )
add_library(elasticity SHARED) add_library(elasticity SHARED)
-12
View File
@@ -18,15 +18,3 @@ Parameters:
- `bend` [Pa]: bending stiffness. - `bend` [Pa]: bending stiffness.
- `flat` [bool]: if true, the stress-equilibrium configuration is that of a straight cable; if false or unspecified, it is the configuration defined in the XML. - `flat` [bool]: if true, the stress-equilibrium configuration is that of a straight cable; if false or unspecified, it is the configuration defined in the XML.
- `vmax` [N/m^2]: If greater than zero, the cable is colored using mechanical stresses; the value represent the maximum stress in the color scale. - `vmax` [N/m^2]: If greater than zero, the cable is colored using mechanical stresses; the value represent the maximum stress in the color scale.
### Shell
Implemented in [shell.cc](shell.cc).
The shell plugin discretizes an inextensible 2D continuum. It is intended to simulate the bending of plates where the stretching is negligible compared to other deformation modes.
Parameters:
- `young` [Pa]: Young's modulus.
- `poisson` [Pa]: Poisson's ratio; if 0, then the material only opposed shear deformations; if near 0.5, then the material is nearly incompressible (rubber-like).
- `thickness` [m]: shell thickness, used to scale the bending stiffness.
-2
View File
@@ -14,13 +14,11 @@
#include <mujoco/mjplugin.h> #include <mujoco/mjplugin.h>
#include "cable.h" #include "cable.h"
#include "shell.h"
namespace mujoco::plugin::elasticity { namespace mujoco::plugin::elasticity {
mjPLUGIN_LIB_INIT { mjPLUGIN_LIB_INIT {
Cable::RegisterPlugin(); Cable::RegisterPlugin();
Shell::RegisterPlugin();
} }
} // namespace mujoco::plugin::elasticity } // namespace mujoco::plugin::elasticity
-154
View File
@@ -1,154 +0,0 @@
// Copyright 2023 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.
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <optional>
#include <utility>
#include <vector>
#include <mujoco/mjplugin.h>
#include <mujoco/mjtnum.h>
#include <mujoco/mujoco.h>
#include "elasticity.h"
#include "shell.h"
namespace mujoco::plugin::elasticity {
namespace {
// local tetrahedron numbering
constexpr int kNumVerts = Stencil2D::kNumVerts;
} // namespace
// factory function
std::optional<Shell> Shell::Create(const mjModel* m, mjData* d, int instance) {
return Shell(m, d, instance);
}
// plugin constructor
Shell::Shell(const mjModel* m, mjData* d, int instance)
: f0(-1) {
// count plugin bodies
nv = 0;
for (int i = 1; i < m->nbody; i++) {
if (m->body_plugin[i] == instance) {
if (!nv++) {
i0 = i;
}
}
}
// count flexes
for (int i = 0; i < m->nflex; i++) {
for (int j = 0; j < m->flex_vertnum[i]; j++) {
if (m->flex_vertbodyid[m->flex_vertadr[i]+j] == i0) {
f0 = i;
nv = m->flex_vertnum[f0];
if (m->flex_dim[i] != 2) { // SHOULD NOT OCCUR
mju_error("mujoco.elasticity.shell requires a 2D mesh");
}
}
}
}
// loop over all triangles
for (int t = 0; t < m->flex_elemnum[f0]; t++) {
int* v = m->flex_elem + 3*(t+m->flex_elemadr[f0]);
for (int i = 0; i < kNumVerts; i++) {
if (m->body_plugin[i0+v[i]] != instance) {
mju_error("This body does not have the requested plugin instance");
}
}
}
// allocate array
position.assign(nv*3, 0);
// store previous positions
mju_copy(position.data(), m->body_pos+3*i0, 3*nv);
}
void Shell::Compute(const mjModel* m, mjData* d, int instance) {
for (int e = 0; e < m->flex_edgenum[f0]; e++) {
int* edge = m->flex_edge + 2*(e+m->flex_edgeadr[f0]);
int* flap = m->flex_edgeflap + 2*(e+m->flex_edgeadr[f0]);
int v[4] = {edge[0], edge[1], flap[0], flap[1]};
mjtNum force[12] = {0};
if (v[3] == -1) {
// skip boundary edges
continue;
}
mjtNum* k = m->flex_bending + 16*m->flex_edgeadr[f0];
for (int i = 0; i < StencilFlap::kNumVerts; i++) {
for (int j = 0; j < StencilFlap::kNumVerts; j++) {
for (int x = 0; x < 3; x++) {
force[3*i+x] += k[16*e+4*i+j] * d->xpos[3*(i0+v[j])+x];
}
}
}
// update stored positions
mju_copy(position.data(), d->xpos+3*i0, 3*nv);
// insert into global force
for (int i = 0; i < StencilFlap::kNumVerts; i++) {
for (int x = 0; x < 3; x++) {
d->qfrc_passive[m->body_dofadr[i0]+3*v[i]+x] -= force[3*i+x];
}
}
}
}
void Shell::RegisterPlugin() {
mjpPlugin plugin;
mjp_defaultPlugin(&plugin);
plugin.name = "mujoco.elasticity.shell";
plugin.capabilityflags |= mjPLUGIN_PASSIVE;
const char* attributes[] = {"damping"};
plugin.nattribute = sizeof(attributes) / sizeof(attributes[0]);
plugin.attributes = attributes;
plugin.nstate = +[](const mjModel* m, int instance) { return 0; };
plugin.init = +[](const mjModel* m, mjData* d, int instance) {
auto elasticity_or_null = Shell::Create(m, d, instance);
if (!elasticity_or_null.has_value()) {
return -1;
}
d->plugin_data[instance] = reinterpret_cast<uintptr_t>(
new Shell(std::move(*elasticity_or_null)));
return 0;
};
plugin.destroy = +[](mjData* d, int instance) {
delete reinterpret_cast<Shell*>(d->plugin_data[instance]);
d->plugin_data[instance] = 0;
};
plugin.compute = +[](const mjModel* m, mjData* d, int instance, int type) {
auto* elasticity = reinterpret_cast<Shell*>(d->plugin_data[instance]);
elasticity->Compute(m, d, instance);
};
mjp_registerPlugin(&plugin);
}
} // namespace mujoco::plugin::elasticity
-61
View File
@@ -1,61 +0,0 @@
// Copyright 2023 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.
#ifndef MUJOCO_PLUGIN_ELASTICITY_SHELL_H_
#define MUJOCO_PLUGIN_ELASTICITY_SHELL_H_
#include <optional>
#include <vector>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjtnum.h>
#include "elasticity.h"
namespace mujoco::plugin::elasticity {
struct StencilFlap {
static constexpr int kNumVerts = 4;
int vertices[kNumVerts];
};
class Shell {
public:
// Returns a new Shell instance or nullopt on failure.
static std::optional<Shell> Create(const mjModel* m, mjData* d,
int instance);
Shell(Shell&&) = default;
Shell& operator=(Shell&& other) = default;
void Compute(const mjModel* m, mjData* d, int instance);
static void RegisterPlugin();
int i0; // index of first body
int f0; // index of corresponding flex
int nc; // number of quads in the grid
int nv; // number of vertices (bodies) in the Shell
// precomputed quantities
std::vector<mjtNum> position; // previous-step positions (nv x 3)
private:
Shell(const mjModel* m, mjData* d, int instance);
};
} // namespace mujoco::plugin::elasticity
#endif // MUJOCO_PLUGIN_ELASTICITY_SHELL_H_
+42 -1
View File
@@ -116,9 +116,50 @@ static void mj_springdamper(const mjModel* m, mjData* d) {
// flex elasticity // flex elasticity
for (int f=0; f < m->nflex; f++) { for (int f=0; f < m->nflex; f++) {
mjtNum* k = m->flex_stiffness + 21*m->flex_elemadr[f]; mjtNum* k = m->flex_stiffness + 21*m->flex_elemadr[f];
mjtNum* b = m->flex_bending + 16*m->flex_edgeadr[f];
int dim = m->flex_dim[f]; int dim = m->flex_dim[f];
if (dim == 1 || m->flex_rigid[f] || k[0] == 0) { if (dim == 1 || m->flex_rigid[f]) {
continue;
}
// add bending forces to qfrc_spring
if (dim == 2) {
mjtNum* xpos = d->flexvert_xpos + 3*m->flex_vertadr[f];
int* bodyid = m->flex_vertbodyid + m->flex_vertadr[f];
for (int e = 0; e < m->flex_edgenum[f]; e++) {
const int* edge = m->flex_edge + 2*(e+m->flex_edgeadr[f]);
const int* flap = m->flex_edgeflap + 2*(e+m->flex_edgeadr[f]);
int v[4] = {edge[0], edge[1], flap[0], flap[1]};
if (v[3] == -1) {
// skip boundary edges
continue;
}
mjtNum force[12] = {0};
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int x = 0; x < 3; x++) {
force[3*i+x] += b[16*e+4*i+j] * xpos[3*v[j]+x];
}
}
}
// TODO: add damping
// insert into global force
for (int i = 0; i < 4; i++) {
int bid = bodyid[v[i]];
int body_dofnum = m->body_dofnum[bid];
int body_dofadr = m->body_dofadr[bid];
for (int x = 0; x < body_dofnum; x++) {
d->qfrc_spring[body_dofadr+x] -= force[3*i+x];
}
}
}
}
if (k[0] == 0) {
continue; continue;
} }
-1
View File
@@ -238,7 +238,6 @@ void mjs_defaultFlex(mjsFlex* flex) {
flex->rgba[0] = flex->rgba[1] = flex->rgba[2] = 0.5f; flex->rgba[0] = flex->rgba[1] = flex->rgba[2] = 0.5f;
flex->rgba[3] = 1.0f; flex->rgba[3] = 1.0f;
flex->thickness = -1; flex->thickness = -1;
flex->elastic2d = 1;
} }
+12 -5
View File
@@ -3382,9 +3382,13 @@ void mjCFlex::ResolveReferences(const mjCModel* m) {
vertbodyid.clear(); vertbodyid.clear();
nodebodyid.clear(); nodebodyid.clear();
for (const auto& vertbody : vertbody_) { for (const auto& vertbody : vertbody_) {
mjCBase* pbody = m->FindObject(mjOBJ_BODY, vertbody); mjCBody* pbody = static_cast<mjCBody*>(m->FindObject(mjOBJ_BODY, vertbody));
if (pbody) { if (pbody) {
vertbodyid.push_back(pbody->id); vertbodyid.push_back(pbody->id);
if (pbody->joints.size() != 3 && dim == 2 && (elastic2d == 1 || elastic2d == 3)) {
// TODO(quaglino): add support for pins
throw mjCError(this, "pins are not supported for bending");
}
} else { } else {
throw mjCError(this, "unknown body '%s' in flex", vertbody.c_str()); throw mjCError(this, "unknown body '%s' in flex", vertbody.c_str());
} }
@@ -3658,7 +3662,10 @@ void mjCFlex::Compile(const mjVFS* vfs) {
} }
// bending stiffness (2D only) // bending stiffness (2D only)
if (dim == 2 && (elastic2d == 1 || elastic2d == 3) && thickness > 0) { if (dim == 2 && (elastic2d == 1 || elastic2d == 3)) {
if (thickness < 0) {
throw mjCError(this, "thickness must be positive for bending stiffness");
}
bending.assign(nedge*16, 0); bending.assign(nedge*16, 0);
for (unsigned int e = 0; e < nedge; e++) { for (unsigned int e = 0; e < nedge; e++) {
@@ -3668,7 +3675,7 @@ void mjCFlex::Compile(const mjVFS* vfs) {
} }
} }
// add plugins // placeholder for setting plugins parameters, currently not used
for (const auto& vbodyid : vertbodyid) { for (const auto& vbodyid : vertbodyid) {
if (vbodyid < 0) { if (vbodyid < 0) {
continue; continue;
@@ -3676,8 +3683,8 @@ void mjCFlex::Compile(const mjVFS* vfs) {
if (model->Bodies()[vbodyid]->plugin.element) { if (model->Bodies()[vbodyid]->plugin.element) {
mjCPlugin* plugin_instance = mjCPlugin* plugin_instance =
static_cast<mjCPlugin*>(model->Bodies()[vbodyid]->plugin.element); static_cast<mjCPlugin*>(model->Bodies()[vbodyid]->plugin.element);
if (damping > 0) { if (!plugin_instance) {
plugin_instance->config_attribs["damping"] = std::to_string(damping); throw mjCError(this, "plugin instance not found");
} }
} }
} }
+1 -1
View File
@@ -3165,7 +3165,7 @@ void mjCModel::CopyObjects(mjModel* m) {
for (int k=0; k < pfl->nedge; k++) { for (int k=0; k < pfl->nedge; k++) {
m->flex_edge[2*(edge_adr+k)] = pfl->edge[k].first; m->flex_edge[2*(edge_adr+k)] = pfl->edge[k].first;
m->flex_edge[2*(edge_adr+k)+1] = pfl->edge[k].second; m->flex_edge[2*(edge_adr+k)+1] = pfl->edge[k].second;
if (pfl->dim == 2) { if (pfl->dim == 2 && (pfl->elastic2d == 1 || pfl->elastic2d == 3)) {
m->flex_edgeflap[2*(edge_adr+k)+0] = pfl->flaps[k].vertices[2]; m->flex_edgeflap[2*(edge_adr+k)+0] = pfl->flaps[k].vertices[2];
m->flex_edgeflap[2*(edge_adr+k)+1] = pfl->flaps[k].vertices[3]; m->flex_edgeflap[2*(edge_adr+k)+1] = pfl->flaps[k].vertices[3];
} else { } else {
+1
View File
@@ -71,6 +71,7 @@ extern const mjMap datatype_map[];
extern const mjMap meshtype_map[]; extern const mjMap meshtype_map[];
extern const mjMap meshinertia_map[]; extern const mjMap meshinertia_map[];
extern const mjMap flexself_map[]; extern const mjMap flexself_map[];
extern const mjMap elastic2d_map[];
//---------------------------------- Base XML class ------------------------------------------------ //---------------------------------- Base XML class ------------------------------------------------
+11 -2
View File
@@ -807,6 +807,15 @@ const mjMap flexself_map[5] = {
}; };
// flex elastic 2d type
const mjMap elastic2d_map[5] = {
{"none", 0},
{"bend", 1},
{"stretch", 2},
{"both", 3},
};
//---------------------------------- class mjXReader implementation -------------------------------- //---------------------------------- class mjXReader implementation --------------------------------
@@ -1401,7 +1410,7 @@ void mjXReader::OneFlex(XMLElement* elem, mjsFlex* flex) {
ReadAttr(elasticity, "poisson", 1, &flex->poisson, text); ReadAttr(elasticity, "poisson", 1, &flex->poisson, text);
ReadAttr(elasticity, "thickness", 1, &flex->thickness, text); ReadAttr(elasticity, "thickness", 1, &flex->thickness, text);
ReadAttr(elasticity, "damping", 1, &flex->damping, text); ReadAttr(elasticity, "damping", 1, &flex->damping, text);
ReadAttr(elasticity, "elastic2d", 1, &flex->elastic2d, text); MapValue(elasticity, "elastic2d", &flex->elastic2d, elastic2d_map, 4);
} }
// write error info // write error info
@@ -2665,7 +2674,7 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* body, const mjVFS* vfs) {
ReadAttr(elasticity, "poisson", 1, &dflex.poisson, text); ReadAttr(elasticity, "poisson", 1, &dflex.poisson, text);
ReadAttr(elasticity, "damping", 1, &dflex.damping, text); ReadAttr(elasticity, "damping", 1, &dflex.damping, text);
ReadAttr(elasticity, "thickness", 1, &dflex.thickness, text); ReadAttr(elasticity, "thickness", 1, &dflex.thickness, text);
ReadAttr(elasticity, "elastic2d", 1, &dflex.elastic2d, text); MapValue(elasticity, "elastic2d", &dflex.elastic2d, elastic2d_map, 4);
} }
// check errors // check errors
+1 -1
View File
@@ -194,7 +194,7 @@ void mjXWriter::OneFlex(XMLElement* elem, const mjCFlex* flex) {
WriteAttr(elastic, "poisson", 1, &flex->poisson, &defflex.poisson); WriteAttr(elastic, "poisson", 1, &flex->poisson, &defflex.poisson);
WriteAttr(elastic, "thickness", 1, &flex->thickness, &defflex.thickness); WriteAttr(elastic, "thickness", 1, &flex->thickness, &defflex.thickness);
WriteAttr(elastic, "damping", 1, &flex->damping, &defflex.damping); WriteAttr(elastic, "damping", 1, &flex->damping, &defflex.damping);
WriteAttr(elastic, "elastic2d", 1, &flex->elastic2d, &defflex.elastic2d); WriteAttrKey(elastic, "elastic2d", elastic2d_map, 2, flex->elastic2d, defflex.elastic2d);
// edge subelement // edge subelement
XMLElement* edge = InsertEnd(elem, "edge"); XMLElement* edge = InsertEnd(elem, "edge");
@@ -343,7 +343,7 @@ void ABSL_ATTRIBUTE_NOINLINE mulMatVecSparse_8(mjtNum* res,
// ----------------------------- benchmark ------------------------------------ // ----------------------------- benchmark ------------------------------------
static void BM_MatVecSparse(benchmark::State& state, int unroll) { static void BM_MatVecSparse(benchmark::State& state, int unroll) {
static mjModel* m = LoadModelFromPath("plugin/elasticity/flag_flex.xml"); static mjModel* m = LoadModelFromPath("flex/flag.xml");
mjData* d = mj_makeData(m); mjData* d = mj_makeData(m);
// warm-up rollout to get a typical state // warm-up rollout to get a typical state
+1 -1
View File
@@ -73,7 +73,7 @@ static void run_parse_benchmark(const std::string xml_path,
// run_parse_benchmark). // run_parse_benchmark).
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_ParseFlagPlugin(benchmark::State& state) { void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_ParseFlagPlugin(benchmark::State& state) {
run_parse_benchmark(GetModelPath("plugin/elasticity/flag_flex.xml"), state); run_parse_benchmark(GetModelPath("flex/flag.xml"), state);
} }
BENCHMARK(BM_ParseFlagPlugin); BENCHMARK(BM_ParseFlagPlugin);
+1 -1
View File
@@ -73,7 +73,7 @@ static void run_step_benchmark(const mjModel* model, benchmark::State& state) {
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepFlagPlugin(benchmark::State& state) { void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepFlagPlugin(benchmark::State& state) {
MujocoErrorTestGuard guard; MujocoErrorTestGuard guard;
static mjModel* model = LoadModelFromPath("plugin/elasticity/flag_flex.xml"); static mjModel* model = LoadModelFromPath("flex/flag.xml");
run_step_benchmark(model, state); run_step_benchmark(model, state);
} }
BENCHMARK(BM_StepFlagPlugin); BENCHMARK(BM_StepFlagPlugin);
+1 -1
View File
@@ -37,7 +37,7 @@ using ::testing::DoubleNear;
using ::testing::HasSubstr; using ::testing::HasSubstr;
using ::testing::NotNull; using ::testing::NotNull;
constexpr int kNumTruePlugins = 11; constexpr int kNumTruePlugins = 10;
constexpr int kNumFakePlugins = 30; constexpr int kNumFakePlugins = 30;
constexpr int kNumTestPlugins = 4; constexpr int kNumTestPlugins = 4;
+5 -10
View File
@@ -22,7 +22,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "test/fixture.h" #include "test/fixture.h"
#include "plugin/elasticity/shell.h"
namespace mujoco { namespace mujoco {
namespace { namespace {
@@ -58,15 +57,10 @@ TEST_F(ElasticityTest, FlexCompatibility) {
TEST_F(ElasticityTest, ElasticEnergyShell) { TEST_F(ElasticityTest, ElasticEnergyShell) {
static constexpr char cantilever_xml[] = R"( static constexpr char cantilever_xml[] = R"(
<mujoco> <mujoco>
<extension>
<plugin plugin="mujoco.elasticity.shell"/>
</extension>
<worldbody> <worldbody>
<flexcomp type="grid" count="8 8 1" spacing="1 1 1" <flexcomp type="grid" count="8 8 1" spacing="1 1 1"
radius=".025" name="test" dim="2"> radius=".025" name="test" dim="2">
<elasticity young="2" poisson="0" thickness="1"/> <elasticity young="2" poisson="0" thickness="1"/>
<plugin plugin="mujoco.elasticity.shell"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>
</mujoco> </mujoco>
@@ -76,7 +70,8 @@ TEST_F(ElasticityTest, ElasticEnergyShell) {
mjModel* m = LoadModelFromString(cantilever_xml, error, sizeof(error)); mjModel* m = LoadModelFromString(cantilever_xml, error, sizeof(error));
ASSERT_THAT(m, testing::NotNull()) << error; ASSERT_THAT(m, testing::NotNull()) << error;
mjData* d = mj_makeData(m); mjData* d = mj_makeData(m);
auto* shell = reinterpret_cast<plugin::elasticity::Shell*>(d->plugin_data[0]); mj_kinematics(m, d);
mj_flex(m, d);
// check that a plane is in the kernel of the energy // check that a plane is in the kernel of the energy
for (mjtNum scale = 1; scale < 4; scale++) { for (mjtNum scale = 1; scale < 4; scale++) {
@@ -92,8 +87,8 @@ TEST_F(ElasticityTest, ElasticEnergyShell) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
for (int x = 0; x < 3; x++) { for (int x = 0; x < 3; x++) {
mjtNum elongation1 = scale * shell->position[3*v[i]+x]; mjtNum elongation1 = scale * d->flexvert_xpos[3*v[i]+x];
mjtNum elongation2 = scale * shell->position[3*v[j]+x]; mjtNum elongation2 = scale * d->flexvert_xpos[3*v[j]+x];
energy += m->flex_bending[16*e+4*i+j] * elongation1 * elongation2; energy += m->flex_bending[16*e+4*i+j] * elongation1 * elongation2;
} }
} }
@@ -114,7 +109,7 @@ TEST_F(PluginTest, ElasticEnergyMembrane) {
<worldbody> <worldbody>
<flexcomp type="grid" count="8 8 1" spacing="1 1 1" <flexcomp type="grid" count="8 8 1" spacing="1 1 1"
radius=".025" name="test" dim="2"> radius=".025" name="test" dim="2">
<elasticity young="2" poisson="0" thickness="1" elastic2d="2"/> <elasticity young="2" poisson="0" thickness="1" elastic2d="stretch"/>
<edge equality="false"/> <edge equality="false"/>
</flexcomp> </flexcomp>
</worldbody> </worldbody>