diff --git a/examples/worlds/pendulum_links.sdf b/examples/worlds/pendulum_links.sdf
new file mode 100644
index 0000000000..2f62bab25c
--- /dev/null
+++ b/examples/worlds/pendulum_links.sdf
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+ 0.001
+ 1.0
+
+
+
+
+
+
+
+
+
+ true
+ 0 0 10 0 0 0
+ 0.8 0.8 0.8 1
+ 0.2 0.2 0.2 1
+
+ 1000
+ 0.9
+ 0.01
+ 0.001
+
+ -0.5 0.1 -0.9
+
+
+
+ true
+
+
+
+
+ 0 0 1
+
+
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+
+
+
+
+
+ 100
+
+
+ 0 0 0.01 0 0 0
+
+
+ 0.8
+ 0.02
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ -0.275 0 1.1 0 0 0
+
+
+ 0.2 0.2 2.2
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ 0 0 0.01 0 0 0
+
+
+ 0.8
+ 0.02
+
+
+
+
+ -0.275 0 1.1 0 0 0
+
+
+ 0.2 0.2 2.2
+
+
+
+
+
+
+ 0 0 2.1 -1.5708 0 0
+ 0
+
+ 0 0 0.5 0 0 0
+
+
+ -0.05 0 0 0 1.5708 0
+
+
+ 0.1
+ 0.3
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ 0 0 1.0 0 1.5708 0
+
+
+ 0.1
+ 0.2
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ 0 0 0.5 0 0 0
+
+
+ 0.1
+ 0.9
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ -0.05 0 0 0 1.5708 0
+
+
+ 0.1
+ 0.3
+
+
+
+
+ 0 0 1.0 0 1.5708 0
+
+
+ 0.1
+ 0.2
+
+
+
+
+ 0 0 0.5 0 0 0
+
+
+ 0.1
+ 0.9
+
+
+
+
+
+
+ 0.25 1.0 2.1 -2 0 0
+ 0
+
+ 0 0 0.5 0 0 0
+
+
+ 0 0 0 0 1.5708 0
+
+
+ 0.08
+ 0.3
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ 0 0 0.5 0 0 0
+
+
+ 0.1
+ 0.9
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+ 0 0 0 0 1.5708 0
+
+
+ 0.08
+ 0.3
+
+
+
+
+ 0 0 0.5 0 0 0
+
+
+ 0.1
+ 0.9
+
+
+
+
+
+
+ base
+ upper_link
+
+ 1.0 0 0
+
+
+
+
+ upper_link
+ lower_link
+
+ 1.0 0 0
+
+
+
+
+ base
+ upper_link
+ lower_link
+
+
+
+
diff --git a/src/systems/physics/Physics.cc b/src/systems/physics/Physics.cc
index 672d9bb828..72ccc67339 100644
--- a/src/systems/physics/Physics.cc
+++ b/src/systems/physics/Physics.cc
@@ -1563,6 +1563,111 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm)
return true;
});
+ // Update link angular velocity
+ _ecm.Each(
+ [&](const Entity &_entity, const components::Link *,
+ const components::AngularVelocityCmd *_angularVelocityCmd)
+ {
+ if (!this->entityLinkMap.HasEntity(_entity))
+ {
+ ignwarn << "Failed to find link [" << _entity
+ << "]." << std::endl;
+ return true;
+ }
+
+ auto linkPtrPhys = this->entityLinkMap.Get(_entity);
+ if (nullptr == linkPtrPhys)
+ return true;
+
+ auto freeGroup = linkPtrPhys->FindFreeGroup();
+ if (!freeGroup)
+ return true;
+ this->entityFreeGroupMap.AddEntity(_entity, freeGroup);
+
+ auto worldAngularVelFeature =
+ this->entityFreeGroupMap
+ .EntityCast(_entity);
+
+ if (!worldAngularVelFeature)
+ {
+ static bool informed{false};
+ if (!informed)
+ {
+ igndbg << "Attempting to set link angular velocity, but the "
+ << "physics engine doesn't support velocity commands. "
+ << "Velocity won't be set."
+ << std::endl;
+ informed = true;
+ }
+ return true;
+ }
+ // velocity in world frame = world_to_model_tf * model_to_link_tf * vel
+ Entity modelEntity = topLevelModel(_entity, _ecm);
+ const components::Pose *modelEntityPoseComp =
+ _ecm.Component(modelEntity);
+ math::Pose3d modelToLinkTransform = this->RelativePose(
+ modelEntity, _entity, _ecm);
+ math::Vector3d worldAngularVel = modelEntityPoseComp->Data().Rot()
+ * modelToLinkTransform.Rot() * _angularVelocityCmd->Data();
+ worldAngularVelFeature->SetWorldAngularVelocity(
+ math::eigen3::convert(worldAngularVel));
+
+ return true;
+ });
+
+ // Update link linear velocity
+ _ecm.Each(
+ [&](const Entity &_entity, const components::Link *,
+ const components::LinearVelocityCmd *_linearVelocityCmd)
+ {
+ if (!this->entityLinkMap.HasEntity(_entity))
+ {
+ ignwarn << "Failed to find link [" << _entity
+ << "]." << std::endl;
+ return true;
+ }
+
+ auto linkPtrPhys = this->entityLinkMap.Get(_entity);
+ if (nullptr == linkPtrPhys)
+ return true;
+
+ auto freeGroup = linkPtrPhys->FindFreeGroup();
+ if (!freeGroup)
+ return true;
+ this->entityFreeGroupMap.AddEntity(_entity, freeGroup);
+
+ auto worldLinearVelFeature =
+ this->entityFreeGroupMap
+ .EntityCast(_entity);
+ if (!worldLinearVelFeature)
+ {
+ static bool informed{false};
+ if (!informed)
+ {
+ igndbg << "Attempting to set link linear velocity, but the "
+ << "physics engine doesn't support velocity commands. "
+ << "Velocity won't be set."
+ << std::endl;
+ informed = true;
+ }
+ return true;
+ }
+
+ // velocity in world frame = world_to_model_tf * model_to_link_tf * vel
+ Entity modelEntity = topLevelModel(_entity, _ecm);
+ const components::Pose *modelEntityPoseComp =
+ _ecm.Component(modelEntity);
+ math::Pose3d modelToLinkTransform = this->RelativePose(
+ modelEntity, _entity, _ecm);
+ math::Vector3d worldLinearVel = modelEntityPoseComp->Data().Rot()
+ * modelToLinkTransform.Rot() * _linearVelocityCmd->Data();
+ worldLinearVelFeature->SetWorldLinearVelocity(
+ math::eigen3::convert(worldLinearVel));
+
+ return true;
+ });
+
+
// Populate bounding box info
// Only compute bounding box if component exists to avoid unnecessary
// computations
@@ -2027,6 +2132,20 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm)
return true;
});
+ _ecm.Each(
+ [&](const Entity &, components::AngularVelocityCmd *_vel) -> bool
+ {
+ _vel->Data() = math::Vector3d::Zero;
+ return true;
+ });
+
+ _ecm.Each(
+ [&](const Entity &, components::LinearVelocityCmd *_vel) -> bool
+ {
+ _vel->Data() = math::Vector3d::Zero;
+ return true;
+ });
+
// Update joint positions
_ecm.Each(
[&](const Entity &_entity, components::Joint *,
diff --git a/src/systems/velocity_control/VelocityControl.cc b/src/systems/velocity_control/VelocityControl.cc
index d6e07f48e7..7ed361c627 100644
--- a/src/systems/velocity_control/VelocityControl.cc
+++ b/src/systems/velocity_control/VelocityControl.cc
@@ -15,8 +15,11 @@
*
*/
+#include