Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add force offset support to ApplyLinkWrench system and to Link API #2026

Merged
merged 15 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions include/gz/sim/Link.hh
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,19 @@ namespace gz
const math::Vector3d &_force,
const math::Vector3d &_torque) const;

/// \brief Add a wrench expressed in world coordinates and applied to
/// the link at the given offset from its center of mass. This wrench
/// is applied for one simulation step.
/// \param[in] _ecm Mutable Entity-component manager.
/// \param[in] _force Force to be applied expressed in world coordinates
/// \param[in] _offset The point of application of the force expressed
azeey marked this conversation as resolved.
Show resolved Hide resolved
/// in the link-fixed frame and relative to the center of mass.
/// \param[in] _torque Torque to be applied expressed in world coordinates
public: void AddWorldWrench(EntityComponentManager &_ecm,
azeey marked this conversation as resolved.
Show resolved Hide resolved
const math::Vector3d &_force,
const math::Vector3d &_offset,
const math::Vector3d &_torque) const;

/// \brief Pointer to private data.
private: std::unique_ptr<LinkPrivate> dataPtr;
};
Expand Down
54 changes: 54 additions & 0 deletions src/Link.cc
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,57 @@ void Link::AddWorldWrench(EntityComponentManager &_ecm,
msgs::Convert(linkWrenchComp->Data().torque()) + _torque);
}
}

//////////////////////////////////////////////////
void Link::AddWorldWrench(EntityComponentManager &_ecm,
azeey marked this conversation as resolved.
Show resolved Hide resolved
const math::Vector3d &_force,
const math::Vector3d &_offset,
const math::Vector3d &_torque) const
{
auto inertial = _ecm.Component<components::Inertial>(this->dataPtr->id);
auto worldPoseComp = _ecm.Component<components::WorldPose>(this->dataPtr->id);

// Can't apply force if the inertial's pose is not found
if (!inertial)
{
gzdbg << "Inertial Component not found" << std::endl;
return;
}

math::Pose3d linkWorldPose;
if (worldPoseComp)
{
linkWorldPose = worldPoseComp->Data();
}
else
{
linkWorldPose = worldPose(this->dataPtr->id, _ecm);
}

// We want the force to be applied at an offset from the center of mass, but
// ExternalWorldWrenchCmd applies the force at the link origin so we need to
// compute the resulting force and torque on the link origin.
auto posComWorldCoord = linkWorldPose.Rot().RotateVector(
_offset + inertial->Data().Pose().Pos());

math::Vector3d torqueWithOffset = _torque + posComWorldCoord.Cross(_force);

auto linkWrenchComp =
_ecm.Component<components::ExternalWorldWrenchCmd>(this->dataPtr->id);

if (!linkWrenchComp)
{
components::ExternalWorldWrenchCmd wrench;
msgs::Set(wrench.Data().mutable_force(), _force);
msgs::Set(wrench.Data().mutable_torque(), torqueWithOffset);
_ecm.CreateComponent(this->dataPtr->id, wrench);
}
else
{
msgs::Set(linkWrenchComp->Data().mutable_force(),
msgs::Convert(linkWrenchComp->Data().force()) + _force);

msgs::Set(linkWrenchComp->Data().mutable_torque(),
msgs::Convert(linkWrenchComp->Data().torque()) + torqueWithOffset);
}
}
20 changes: 12 additions & 8 deletions src/systems/apply_link_wrench/ApplyLinkWrench.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,17 @@ class gz::sim::systems::ApplyLinkWrenchPrivate
/// \param[in] _msg Entity message. If it's a link, that link is returned. If
/// it's a model, its canonical link is returned.
/// \param[out] Force to apply.
/// \param[out] Offset of the force application point relative to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies that the offset in msgs::EntityWrench and by association, in the component::ExternalWorldWrench. is relative to the COM, but the point of application is the link origin. Am I right? If so, I think we should avoid having two different reference points. If the point of application is the link origin, the offset should also be from the link origin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in ee693bc so that the Link API and the ApplyLinkWrench system interpret the offset in the link frame. This way, the semantics of the offset are consistent between them, the msgs::EntityWrench message and the component::ExternalWorldWrench component.

/// link's center of mass.
/// \param[out] Torque to apply.
/// \return Target link entity.
Link decomposeMessage(const EntityComponentManager &_ecm,
const msgs::EntityWrench &_msg, math::Vector3d &_force,
math::Vector3d &_torque)
math::Vector3d &_offset, math::Vector3d &_torque)
{
if (_msg.wrench().has_force_offset())
{
gzwarn << "Force offset currently not supported, it will be ignored."
<< std::endl;
_offset = msgs::Convert(_msg.wrench().force_offset());
}

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

math::Vector3d force;
math::Vector3d offset;
math::Vector3d torque;
auto link = decomposeMessage(_ecm, msg, force, torque);
auto link = decomposeMessage(_ecm, msg, force, offset, torque);
if (!link.Valid(_ecm))
{
gzerr << "Entity not found." << std::endl
Expand All @@ -280,11 +282,12 @@ void ApplyLinkWrench::PreUpdate(const UpdateInfo &_info,
continue;
}

link.AddWorldWrench(_ecm, force, torque);
link.AddWorldWrench(_ecm, force, offset, torque);

if (this->dataPtr->verbose)
{
gzdbg << "Applying wrench [" << force << " " << torque << "] to entity ["
gzdbg << "Applying wrench [" << force << " " << torque
<< "] with force offset [" << offset << "] to entity ["
<< link.Entity() << "] for 1 time step." << std::endl;
}

Expand All @@ -295,15 +298,16 @@ void ApplyLinkWrench::PreUpdate(const UpdateInfo &_info,
for (auto msg : this->dataPtr->persistentWrenches)
{
math::Vector3d force;
math::Vector3d offset;
math::Vector3d torque;
auto link = decomposeMessage(_ecm, msg, force, torque);
auto link = decomposeMessage(_ecm, msg, force, offset, torque);
if (!link.Valid(_ecm))
{
// Not an error, persistent wrenches can be applied preemptively before
// an entity is inserted
continue;
}
link.AddWorldWrench(_ecm, force, torque);
link.AddWorldWrench(_ecm, force, offset, torque);
}
}

Expand Down
27 changes: 27 additions & 0 deletions test/integration/link.cc
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,33 @@ TEST_F(LinkIntegrationTest, LinkAddWorldForce)
EXPECT_NE(nullptr, wrenchComp);
wrenchMsg = wrenchComp->Data();

EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
wrenchMsg.torque().x(), wrenchMsg.torque().y(), wrenchMsg.torque().z()));

// apply world force at an offset and world torque
math::Vector3d torque{1.0, 0.0, 0.0};
link.AddWorldWrench(ecm, force, offset, torque);

wrenchComp = ecm.Component<components::ExternalWorldWrenchCmd>(eLink);
EXPECT_NE(nullptr, wrenchComp);
wrenchMsg = wrenchComp->Data();

expectedTorque =
torque +
linkWorldPose.Rot().RotateVector(offset + inertiaPose.Pos()).Cross(force);
EXPECT_EQ(force, math::Vector3d(
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
EXPECT_EQ(expectedTorque, math::Vector3d(
wrenchMsg.torque().x(), wrenchMsg.torque().y(), wrenchMsg.torque().z()));

// apply opposite wrench again and verify the resulting wrench values are zero
link.AddWorldWrench(ecm, -force, offset, -torque);
wrenchComp = ecm.Component<components::ExternalWorldWrenchCmd>(eLink);
EXPECT_NE(nullptr, wrenchComp);
wrenchMsg = wrenchComp->Data();

EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
wrenchMsg.force().x(), wrenchMsg.force().y(), wrenchMsg.force().z()));
EXPECT_EQ(math::Vector3d::Zero, math::Vector3d(
Expand Down