Skip to content

Commit acdb445

Browse files
authored
Merge pull request #2807 from jorisv/topic/repro-append-model-issue
Fix appendModel issue when a frame have non null transformation
2 parents 5c50e66 + 0d16150 commit acdb445

File tree

3 files changed

+85
-21
lines changed

3 files changed

+85
-21
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2828
- Remove pixi 0.57 warnings ([#2802](https://github.com/stack-of-tasks/pinocchio/pull/2802))
2929
- CMake: use `sdformat.cmake` from `jrl-cmakemodules` ([#2800](https://github.com/stack-of-tasks/pinocchio/pull/2800))
3030
- Fix doxygen document hierarchy ([#2811](https://github.com/stack-of-tasks/pinocchio/pull/2811))
31+
- Fix `appendModel` issues ([#2807](https://github.com/stack-of-tasks/pinocchio/pull/2807)):
32+
- Bad inertia transformation when multiple frame with inertia have non null placement
33+
- Undefined behavior when more than two frame was attached to universe
3134

3235
### Removed
3336
- Remove CMake < 3.22 details ([#2790](https://github.com/stack-of-tasks/pinocchio/pull/2790))

include/pinocchio/algorithm/model.hxx

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ namespace pinocchio
7070
parentFrame < model.frames.size(), std::invalid_argument,
7171
"parentFrame is greater than the size of the frames vector.");
7272

73-
const Frame & pframe = model.frames[parentFrame];
74-
JointIndex jid = pframe.parentJoint;
73+
const auto pframe_placement = model.frames[parentFrame].placement;
74+
JointIndex jid = model.frames[parentFrame].parentJoint;
7575
assert(jid < model.joints.size());
7676

7777
// If inertia is not NaN, add it.
7878
if (modelAB.inertias[0] == modelAB.inertias[0])
79-
model.appendBodyToJoint(jid, modelAB.inertias[0], pframe.placement * pfMAB);
79+
model.appendBodyToJoint(jid, modelAB.inertias[0], pframe_placement * pfMAB);
8080

8181
// Add all frames whose parent is this joint.
8282
for (FrameIndex fid = 1; fid < modelAB.frames.size(); ++fid)
@@ -101,15 +101,10 @@ namespace pinocchio
101101
}
102102

103103
// Modify frame placement
104-
frame.placement = pframe.placement * pfMAB * frame.placement;
105-
// Some frames may have some inertia attached to them. In this case, we need to remove it
106-
// from the parent joint. To prevent introducing NaNs, we check if the frame inertia is
107-
// not NaN and is not zero.
108-
if (frame.inertia == frame.inertia && frame.inertia != Inertia::Zero())
109-
{
110-
model.inertias[frame.parentJoint] -= frame.inertia;
111-
}
112-
model.addFrame(frame);
104+
frame.placement = pframe_placement * pfMAB * frame.placement;
105+
// Inertias are already computed in model.appendBodyToJoint call.
106+
// No need to append them again.
107+
model.addFrame(frame, false);
113108
}
114109
}
115110

@@ -130,7 +125,7 @@ namespace pinocchio
130125
{
131126
go.parentFrame = parentFrame;
132127
}
133-
go.placement = (pframe.placement * pfMAB) * go.placement;
128+
go.placement = (pframe_placement * pfMAB) * go.placement;
134129
geomModel.addGeometryObject(go);
135130
}
136131
}
@@ -248,14 +243,9 @@ namespace pinocchio
248243
modelAB, model, modelAB.frames[frame.parentFrame].name,
249244
modelAB.frames[frame.parentFrame].type);
250245
}
251-
// Some frames may have some inertia attached to them. In this case, we need to remove
252-
// it from the parent joint. To prevent introducing NaNs, we check if the frame inertia
253-
// is not NaN and is not zero.
254-
if (frame.inertia == frame.inertia && frame.inertia != Inertia::Zero())
255-
{
256-
model.inertias[frame.parentJoint] -= frame.inertia;
257-
}
258-
model.addFrame(frame);
246+
// Inertias are already computed in modelAB.inertias[joint_id_in].
247+
// No need to append them again.
248+
model.addFrame(frame, false);
259249
}
260250
}
261251
// Add all geometries whose parent is this joint.

unittest/model.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,4 +1098,75 @@ BOOST_AUTO_TEST_CASE(test_has_configuration_limit_mimic)
10981098
BOOST_CHECK_EQUAL(model.hasConfigurationLimitInTangent().size(), 2);
10991099
}
11001100

1101+
// Non regression test for: https://github.com/stack-of-tasks/pinocchio/issues/2805
1102+
// When a frame with non null placement have inertia, the inertia was not well transformed
1103+
// in the support joint.
1104+
BOOST_AUTO_TEST_CASE(test_append_model_issue_2805)
1105+
{
1106+
// Create robot with a fixed joint
1107+
Model model_fixed_joint;
1108+
Inertia I(1000, Eigen::Vector3d::Zero(), Eigen::Matrix3d::Identity() * 166);
1109+
SE3 translation(Eigen::Matrix3d::Identity(), Eigen::Vector3d::UnitX());
1110+
auto root_joint_index = model_fixed_joint.addJoint(
1111+
0, pinocchio::JointModel(pinocchio::JointModelFreeFlyer()), SE3::Identity(), "root_joint");
1112+
auto root_joint_frame_index =
1113+
model_fixed_joint.addFrame(Frame("root_joint", root_joint_index, 0, SE3::Identity(), JOINT));
1114+
auto link_1_index = model_fixed_joint.addFrame(
1115+
Frame("link_1", root_joint_index, root_joint_frame_index, SE3::Identity(), BODY, I));
1116+
auto joint1_fixed_index = model_fixed_joint.addFrame(
1117+
Frame("urdf_joint_1fixed", root_joint_index, link_1_index, translation, FIXED_JOINT));
1118+
model_fixed_joint.addFrame(
1119+
Frame("link_2", root_joint_index, joint1_fixed_index, translation, BODY, I));
1120+
1121+
Model model_root;
1122+
auto new_model = appendModel(model_root, model_fixed_joint, 0, SE3::Identity());
1123+
1124+
BOOST_CHECK(new_model.inertias[1].isApprox(model_fixed_joint.inertias[1]));
1125+
}
1126+
1127+
// Non regression test for: https://github.com/stack-of-tasks/pinocchio/issues/2805
1128+
// Same test as above but verify the special case of frame attached to universe frame.
1129+
BOOST_AUTO_TEST_CASE(test_append_model_universe_with_inertia_issue_2805)
1130+
{
1131+
Inertia I(1000, Eigen::Vector3d::Zero(), Eigen::Matrix3d::Identity() * 166);
1132+
SE3 translation_root_joint(Eigen::Matrix3d::Identity(), Eigen::Vector3d::UnitX());
1133+
SE3 translation_fixed_link_1(Eigen::Matrix3d::Identity(), Eigen::Vector3d::UnitY());
1134+
SE3 translation_fixed_link_2(Eigen::Matrix3d::Identity(), Eigen::Vector3d(1., 0., 1.));
1135+
SE3 translation_AB(Eigen::Matrix3d::Identity(), Eigen::Vector3d::UnitZ());
1136+
1137+
// Base model
1138+
Model model_a;
1139+
auto model_a_root_joint_index = model_a.addJoint(
1140+
0, pinocchio::JointModel(pinocchio::JointModelFreeFlyer()), SE3::Identity(), "root_joint");
1141+
auto model_a_root_joint_frame = model_a.addFrame(
1142+
Frame("root_joint", model_a_root_joint_index, 0, translation_root_joint, JOINT));
1143+
1144+
//  Model with frame and inertia attached to universe.
1145+
// To reproduce the issue, we must have two frame with inertia attached to universe.
1146+
// With only one frame, some computation will give a 0 mass inertia that will hide the bug.
1147+
Model model_b;
1148+
model_b.addFrame(Frame("fixed_link_1", 0, 0, translation_fixed_link_1, BODY, I));
1149+
model_b.addFrame(Frame("fixed_link_2", 0, 0, translation_fixed_link_2, BODY, I));
1150+
1151+
Model expected_model;
1152+
auto expected_model_root_joint_index = expected_model.addJoint(
1153+
0, pinocchio::JointModel(pinocchio::JointModelFreeFlyer()), SE3::Identity(), "root_joint");
1154+
auto expected_model_root_joint_frame_index = expected_model.addFrame(
1155+
Frame("root_joint", expected_model_root_joint_index, 0, translation_root_joint, JOINT));
1156+
auto fixed_link_1_frame_index = expected_model.addFrame(Frame(
1157+
"fixed_link_1", expected_model_root_joint_index, expected_model_root_joint_frame_index,
1158+
translation_root_joint.act(translation_AB.act(translation_fixed_link_1)), BODY, I));
1159+
auto fixed_link_2_frame_index = expected_model.addFrame(Frame(
1160+
"fixed_link_2", expected_model_root_joint_index, expected_model_root_joint_frame_index,
1161+
translation_root_joint.act(translation_AB.act(translation_fixed_link_2)), BODY, I));
1162+
1163+
// Test frame position and inertias attached to root_joint
1164+
auto new_model = appendModel(model_a, model_b, model_a_root_joint_frame, translation_AB);
1165+
BOOST_CHECK(new_model.frames[fixed_link_1_frame_index].placement.isApprox(
1166+
expected_model.frames[fixed_link_1_frame_index].placement));
1167+
BOOST_CHECK(new_model.frames[fixed_link_2_frame_index].placement.isApprox(
1168+
expected_model.frames[fixed_link_2_frame_index].placement));
1169+
BOOST_CHECK(new_model.inertias[1].isApprox(expected_model.inertias[1]));
1170+
}
1171+
11011172
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)