Skip to content

Commit 579013f

Browse files
authored
Add force offset support to ApplyLinkWrench system and to Link API (#2026)
Adds support for specifying the application point of a force in the ApplyLinkWrench system. This is done through an offset expressed in the link frame (thus, relative to the link origin). This uses the force_offset field that was already present in the Wrench message, but wasn't being used in the system. Also, an overloaded method AddWorldWrench was added to the Link interface. --------- Signed-off-by: Henrique-BO <[email protected]>
1 parent e19f26d commit 579013f

File tree

5 files changed

+83
-14
lines changed

5 files changed

+83
-14
lines changed

include/gz/sim/Link.hh

+13
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ namespace gz
306306
const math::Vector3d &_force,
307307
const math::Vector3d &_torque) const;
308308

309+
/// \brief Add a wrench expressed in world coordinates and applied to
310+
/// the link at an offset from the link's origin. This wrench
311+
/// is applied for one simulation step.
312+
/// \param[in] _ecm Mutable Entity-component manager.
313+
/// \param[in] _force Force to be applied expressed in world coordinates
314+
/// \param[in] _torque Torque to be applied expressed in world coordinates
315+
/// \param[in] _offset The point of application of the force expressed
316+
/// in the link frame
317+
public: void AddWorldWrench(EntityComponentManager &_ecm,
318+
const math::Vector3d &_force,
319+
const math::Vector3d &_torque,
320+
const math::Vector3d &_offset) const;
321+
309322
/// \brief Pointer to private data.
310323
private: std::unique_ptr<LinkPrivate> dataPtr;
311324
};

src/Link.cc

+29-6
Original file line numberDiff line numberDiff line change
@@ -412,23 +412,46 @@ void Link::AddWorldWrench(EntityComponentManager &_ecm,
412412
const math::Vector3d &_force,
413413
const math::Vector3d &_torque) const
414414
{
415-
auto linkWrenchComp =
416-
_ecm.Component<components::ExternalWorldWrenchCmd>(this->dataPtr->id);
415+
this->AddWorldWrench(_ecm, _force, _torque, math::Vector3d::Zero);
416+
}
417+
418+
//////////////////////////////////////////////////
419+
void Link::AddWorldWrench(EntityComponentManager &_ecm,
420+
const math::Vector3d &_force,
421+
const math::Vector3d &_torque,
422+
const math::Vector3d &_offset) const
423+
{
424+
math::Pose3d linkWorldPose;
425+
auto worldPoseComp = _ecm.Component<components::WorldPose>(this->dataPtr->id);
426+
if (worldPoseComp)
427+
{
428+
linkWorldPose = worldPoseComp->Data();
429+
}
430+
else
431+
{
432+
linkWorldPose = worldPose(this->dataPtr->id, _ecm);
433+
}
417434

418-
components::ExternalWorldWrenchCmd wrench;
435+
// We want the force to be applied at an offset from the link origin, so we
436+
// must compute the resulting force and torque on the link origin.
437+
auto posComWorldCoord = linkWorldPose.Rot().RotateVector(_offset);
438+
math::Vector3d torqueWithOffset = _torque + posComWorldCoord.Cross(_force);
419439

440+
auto linkWrenchComp =
441+
_ecm.Component<components::ExternalWorldWrenchCmd>(this->dataPtr->id);
420442
if (!linkWrenchComp)
421443
{
444+
components::ExternalWorldWrenchCmd wrench;
422445
msgs::Set(wrench.Data().mutable_force(), _force);
423-
msgs::Set(wrench.Data().mutable_torque(), _torque);
446+
msgs::Set(wrench.Data().mutable_torque(), torqueWithOffset);
424447
_ecm.CreateComponent(this->dataPtr->id, wrench);
425448
}
426449
else
427450
{
428451
msgs::Set(linkWrenchComp->Data().mutable_force(),
429-
msgs::Convert(linkWrenchComp->Data().force()) + _force);
452+
msgs::Convert(linkWrenchComp->Data().force()) + _force);
430453

431454
msgs::Set(linkWrenchComp->Data().mutable_torque(),
432-
msgs::Convert(linkWrenchComp->Data().torque()) + _torque);
455+
msgs::Convert(linkWrenchComp->Data().torque()) + torqueWithOffset);
433456
}
434457
}

src/systems/apply_link_wrench/ApplyLinkWrench.cc

+12-8
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,16 @@ class gz::sim::systems::ApplyLinkWrenchPrivate
8181
/// it's a model, its canonical link is returned.
8282
/// \param[out] Force to apply.
8383
/// \param[out] Torque to apply.
84+
/// \param[out] Offset of the force application point expressed in the link
85+
/// frame.
8486
/// \return Target link entity.
8587
Link decomposeMessage(const EntityComponentManager &_ecm,
8688
const msgs::EntityWrench &_msg, math::Vector3d &_force,
87-
math::Vector3d &_torque)
89+
math::Vector3d &_torque, math::Vector3d &_offset)
8890
{
8991
if (_msg.wrench().has_force_offset())
9092
{
91-
gzwarn << "Force offset currently not supported, it will be ignored."
92-
<< std::endl;
93+
_offset = msgs::Convert(_msg.wrench().force_offset());
9394
}
9495

9596
if (_msg.wrench().has_force())
@@ -270,8 +271,9 @@ void ApplyLinkWrench::PreUpdate(const UpdateInfo &_info,
270271
auto msg = this->dataPtr->newWrenches.front();
271272

272273
math::Vector3d force;
274+
math::Vector3d offset;
273275
math::Vector3d torque;
274-
auto link = decomposeMessage(_ecm, msg, force, torque);
276+
auto link = decomposeMessage(_ecm, msg, force, torque, offset);
275277
if (!link.Valid(_ecm))
276278
{
277279
gzerr << "Entity not found." << std::endl
@@ -280,11 +282,12 @@ void ApplyLinkWrench::PreUpdate(const UpdateInfo &_info,
280282
continue;
281283
}
282284

283-
link.AddWorldWrench(_ecm, force, torque);
285+
link.AddWorldWrench(_ecm, force, torque, offset);
284286

285287
if (this->dataPtr->verbose)
286288
{
287-
gzdbg << "Applying wrench [" << force << " " << torque << "] to entity ["
289+
gzdbg << "Applying wrench [" << force << " " << torque
290+
<< "] with force offset [" << offset << "] to entity ["
288291
<< link.Entity() << "] for 1 time step." << std::endl;
289292
}
290293

@@ -295,15 +298,16 @@ void ApplyLinkWrench::PreUpdate(const UpdateInfo &_info,
295298
for (auto msg : this->dataPtr->persistentWrenches)
296299
{
297300
math::Vector3d force;
301+
math::Vector3d offset;
298302
math::Vector3d torque;
299-
auto link = decomposeMessage(_ecm, msg, force, torque);
303+
auto link = decomposeMessage(_ecm, msg, force, torque, offset);
300304
if (!link.Valid(_ecm))
301305
{
302306
// Not an error, persistent wrenches can be applied preemptively before
303307
// an entity is inserted
304308
continue;
305309
}
306-
link.AddWorldWrench(_ecm, force, torque);
310+
link.AddWorldWrench(_ecm, force, torque, offset);
307311
}
308312
}
309313

src/systems/apply_link_wrench/ApplyLinkWrench.hh

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace systems
3838
/// that will receive a wrench. If a model is provided, its canonical link
3939
/// will receive it. No other entity types are supported.
4040
///
41+
/// Forces and torques are expressed in world coordinates, and the force
42+
/// offset is expressed in the link frame.
43+
///
4144
/// ## Topics
4245
///
4346
/// * /world/<world_name>/wrench

test/integration/link.cc

+26
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,32 @@ TEST_F(LinkIntegrationTest, LinkAddWorldForce)
608608
EXPECT_NE(nullptr, wrenchComp);
609609
wrenchMsg = wrenchComp->Data();
610610

611+
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
612+
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
613+
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
614+
wrenchMsg.torque().x(), wrenchMsg.torque().y(), wrenchMsg.torque().z()));
615+
616+
// apply world force at an offset and world torque
617+
math::Vector3d torque{1.0, 0.0, 0.0};
618+
link.AddWorldWrench(ecm, force, torque, offset);
619+
620+
wrenchComp = ecm.Component<components::ExternalWorldWrenchCmd>(eLink);
621+
EXPECT_NE(nullptr, wrenchComp);
622+
wrenchMsg = wrenchComp->Data();
623+
624+
math::Vector3d posComWorldCoord = linkWorldPose.Rot().RotateVector(offset);
625+
expectedTorque = torque + posComWorldCoord.Cross(force);
626+
EXPECT_EQ(force, math::Vector3d(
627+
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
628+
EXPECT_EQ(expectedTorque, math::Vector3d(
629+
wrenchMsg.torque().x(), wrenchMsg.torque().y(), wrenchMsg.torque().z()));
630+
631+
// apply opposite wrench again and verify the resulting wrench values are zero
632+
link.AddWorldWrench(ecm, -force, -torque, offset);
633+
wrenchComp = ecm.Component<components::ExternalWorldWrenchCmd>(eLink);
634+
EXPECT_NE(nullptr, wrenchComp);
635+
wrenchMsg = wrenchComp->Data();
636+
611637
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
612638
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
613639
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(

0 commit comments

Comments
 (0)