Fixes an issue when self-attaching a model with pulley in tendons.

The decision to attach a tendon is taken by looking at whether any of the elements in its path exist when the namespace is added to them. This does not work when pulleys are present so we need to ignore them in the count of unreferenced element.

PiperOrigin-RevId: 729484365
Change-Id: I561a3a2eaa975a17421c8abe3ebfec8d4dd88951
This commit is contained in:
Alessio Quaglino
2025-02-21 04:53:24 -08:00
committed by Copybara-Service
parent 7efaf5c549
commit 6aabbc1892
2 changed files with 69 additions and 1 deletions
+5 -1
View File
@@ -5413,9 +5413,13 @@ const mjCWrap* mjCTendon::GetWrap(int i) const {
void mjCTendon::ResolveReferences(const mjCModel* m) {
int nfailure = 0;
int npulley = 0;
for (int i=0; i<path.size(); i++) {
std::string pname = path[i]->name;
std::string psidesite = path[i]->sidesite;
if (path[i]->type == mjWRAP_PULLEY) {
npulley++;
}
try {
// look for wrapped element with namespace
path[i]->name = prefix + pname + suffix;
@@ -5429,7 +5433,7 @@ void mjCTendon::ResolveReferences(const mjCModel* m) {
nfailure++;
}
}
if (nfailure==path.size()) {
if (nfailure==path.size()-npulley) {
throw mjCError(this, "tendon '%s' (id = %d): no attached reference found", name.c_str(), id);
}
prefix.clear();
+64
View File
@@ -1441,6 +1441,70 @@ TEST_F(XMLReaderTest, ParseReplicateRepeatedName) {
EXPECT_THAT(error.data(), HasSubstr("Element 'replicate'"));
}
TEST_F(XMLReaderTest, ParseReplicateTendon) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<body name="winch" pos="-.01 0 .35">
<joint name="winch" damping="1"/>
<geom type="cylinder" size=".015 .01"/>
<site name="anchor" pos=".1 0 .04"/>
</body>
<site name="pulley" pos=".1 0 .32"/>
<site name="hook_left" pos=".08 0 .3"/>
<site name="hook_right" pos=".12 0 .3"/>
<body name="sphere" pos=".1 0 .2">
<freejoint/>
<geom type="sphere" size=".03"/>
<site name="pin_left" pos="-.025 0 .025"/>
<site name="pin_right" pos=".025 0 .025"/>
</body>
<replicate count="4" offset=".025 0 0">
<replicate count="4" offset="0 .025 0">
<replicate count="4" offset="0 0 .025">
<body pos=".06 -.04 .05">
<geom type="sphere" size=".012"/>
</body>
</replicate>
</replicate>
</replicate>
</worldbody>
<tendon>
<spatial range="0 .19" limited="true">
<site site="anchor"/>
<site site="pulley"/>
<pulley divisor="3"/>
<site site="pulley"/>
<site site="hook_left"/>
<site site="pin_left"/>
<pulley divisor="3"/>
<site site="pulley"/>
<site site="hook_right"/>
<site site="pin_right"/>
</spatial>
</tendon>
<actuator>
<position name="winch" joint="winch" ctrlrange="-.7 .5" ctrllimited="true" kp="10"/>
</actuator>
</mujoco>
)";
std::array<char, 1024> error;
mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
EXPECT_THAT(spec, NotNull()) << error.data();
mjModel* m = mj_compile(spec, 0);
EXPECT_THAT(m, NotNull());
EXPECT_THAT(m->nbody, 67);
EXPECT_THAT(m->ngeom, 66);
EXPECT_THAT(m->nsite, 6);
EXPECT_THAT(m->nu, 1);
EXPECT_THAT(m->ntendon, 1);
mj_deleteModel(m);
mj_deleteSpec(spec);
}
// ---------------------- test spec assets parsing -----------------------------
TEST_F(XMLReaderTest, ParseSpecAssets) {