pxmlw6n2f/Gazebo_Distributed_MPI/gazebo/physics/Joint.cc

1469 lines
41 KiB
C++

/*
* Copyright (C) 2012 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef _WIN32
// Ensure that Winsock2.h is included before Windows.h, which can get
// pulled in by anybody (e.g., Boost).
#include <Winsock2.h>
#endif
#include "gazebo/transport/TransportIface.hh"
#include "gazebo/transport/Publisher.hh"
#include "gazebo/common/Assert.hh"
#include "gazebo/common/Console.hh"
#include "gazebo/common/Events.hh"
#include "gazebo/common/Exception.hh"
#include "gazebo/physics/PhysicsEngine.hh"
#include "gazebo/physics/Link.hh"
#include "gazebo/physics/Model.hh"
#include "gazebo/physics/World.hh"
#include "gazebo/physics/Joint.hh"
using namespace gazebo;
using namespace physics;
sdf::ElementPtr Joint::sdfJoint;
//////////////////////////////////////////////////
Joint::Joint(BasePtr _parent)
: Base(_parent)
{
this->AddType(Base::JOINT);
this->effortLimit[0] = -1;
this->effortLimit[1] = -1;
this->velocityLimit[0] = -1;
this->velocityLimit[1] = -1;
this->lowerLimit[0] = -1e16;
this->lowerLimit[1] = -1e16;
this->upperLimit[0] = 1e16;
this->upperLimit[1] = 1e16;
this->dissipationCoefficient[0] = 0;
this->dissipationCoefficient[1] = 0;
this->stiffnessCoefficient[0] = 0;
this->stiffnessCoefficient[1] = 0;
this->springReferencePosition[0] = 0;
this->springReferencePosition[1] = 0;
this->provideFeedback = false;
this->stopStiffness[0] = 1e8;
this->stopDissipation[0] = 1.0;
this->stopStiffness[1] = 1e8;
this->stopDissipation[1] = 1.0;
// these flags are related to issue #494
// set default to true for backward compatibility
this->axisParentModelFrame[0] = true;
this->axisParentModelFrame[1] = true;
if (!this->sdfJoint)
{
this->sdfJoint.reset(new sdf::Element);
sdf::initFile("joint.sdf", this->sdfJoint);
}
}
//////////////////////////////////////////////////
Joint::~Joint()
{
this->Fini();
}
//////////////////////////////////////////////////
void Joint::Load(LinkPtr _parent, LinkPtr _child, const math::Pose &_pose)
{
if (_parent)
{
this->world = _parent->GetWorld();
this->model = _parent->GetModel();
}
else if (_child)
{
this->world = _child->GetWorld();
this->model = _child->GetModel();
}
else
gzthrow("both parent and child link do no exist");
this->parentLink = _parent;
this->childLink = _child;
// Joint is loaded without sdf from a model
// Initialize this->sdf so it can be used for data storage
this->sdf = this->sdfJoint->Clone();
this->LoadImpl(_pose);
}
//////////////////////////////////////////////////
void Joint::Load(sdf::ElementPtr _sdf)
{
Base::Load(_sdf);
// Joint force and torque feedback
if (_sdf->HasElement("physics"))
{
sdf::ElementPtr physicsElem = _sdf->GetElement("physics");
if (physicsElem->HasElement("provide_feedback"))
{
this->SetProvideFeedback(physicsElem->Get<bool>("provide_feedback"));
}
}
for (unsigned int index = 0; index < this->GetAngleCount(); ++index)
{
std::string axisName;
if (index == 0)
{
axisName = "axis";
}
else if (index == 1)
{
axisName = "axis2";
}
else
{
gzerr << "Invalid axis count" << std::endl;
continue;
}
if (!_sdf->HasElement(axisName))
{
continue;
}
sdf::ElementPtr axisElem = _sdf->GetElement(axisName);
{
std::string param = "use_parent_model_frame";
// Check if "use_parent_model_frame" element exists.
// It has `required=1`, so if it does not exist, then SDF is old,
// and we should assume support for backwards compatibility
if (axisElem->HasElement(param))
{
this->axisParentModelFrame[index] = axisElem->Get<bool>(param);
}
// Axis dynamics
sdf::ElementPtr dynamicsElem = axisElem->GetElement("dynamics");
if (dynamicsElem)
{
double reference = 0;
double stiffness = 0;
if (dynamicsElem->HasElement("spring_reference"))
{
reference = dynamicsElem->Get<double>("spring_reference");
}
if (dynamicsElem->HasElement("spring_stiffness"))
{
stiffness = dynamicsElem->Get<double>("spring_stiffness");
}
this->SetStiffnessDamping(index, stiffness,
dynamicsElem->Get<double>("damping"), reference);
if (dynamicsElem->HasElement("friction"))
{
this->SetParam("friction", index,
dynamicsElem->Get<double>("friction"));
}
}
}
if (axisElem->HasElement("limit"))
{
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// store upper and lower joint limits
this->upperLimit[index] = limitElem->Get<double>("upper");
this->lowerLimit[index] = limitElem->Get<double>("lower");
// store joint stop stiffness and dissipation coefficients
this->stopStiffness[index] = limitElem->Get<double>("stiffness");
this->stopDissipation[index] = limitElem->Get<double>("dissipation");
// store joint effort and velocity limits
this->effortLimit[index] = limitElem->Get<double>("effort");
this->velocityLimit[index] = limitElem->Get<double>("velocity");
}
}
sdf::ElementPtr parentElem = _sdf->GetElement("parent");
sdf::ElementPtr childElem = _sdf->GetElement("child");
GZ_ASSERT(parentElem, "Parent element is NULL");
GZ_ASSERT(childElem, "Child element is NULL");
std::string parentName = parentElem->Get<std::string>();
std::string childName = childElem->Get<std::string>();
if (this->model)
{
this->childLink = this->model->GetLink(childName);
if (!this->childLink)
{
// need to do this if child link belongs to another model
this->childLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(childName));
}
this->parentLink = this->model->GetLink(parentName);
}
else
{
this->childLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(childName));
this->parentLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(parentName));
}
// Link might not have been found because it is on another model
// or because the model name has been changed, e.g. spawning the same model
// twice will result in some suffix appended to the model name
// First try to find the link with different scopes.
if (!this->parentLink && parentName != std::string("world"))
{
BasePtr parentModel = this->model;
while (!this->parentLink && parentModel && parentModel->HasType(MODEL))
{
std::string scopedParentName =
parentModel->GetScopedName() + "::" + parentName;
this->parentLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(scopedParentName));
parentModel = parentModel->GetParent();
}
if (!this->parentLink)
{
std::string parentNameThisModel;
auto doubleColon = parentName.find("::");
if (doubleColon != std::string::npos)
{
parentNameThisModel = parentName.substr(doubleColon);
}
else
{
parentNameThisModel = "::" + parentName;
}
parentNameThisModel = parentModel->GetName() + parentNameThisModel;
this->parentLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(parentNameThisModel));
}
if (!this->parentLink)
gzthrow("Couldn't Find Parent Link[" + parentName + "]");
}
if (!this->childLink && childName != std::string("world"))
{
BasePtr parentModel = this->model;
while (!this->childLink && parentModel && parentModel->HasType(MODEL))
{
std::string scopedChildName =
parentModel->GetScopedName() + "::" + childName;
this->childLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(scopedChildName));
parentModel = parentModel->GetParent();
}
if (!this->childLink)
{
std::string childNameThisModel;
auto doubleColon = childName.find("::");
if (doubleColon != std::string::npos)
{
childNameThisModel = childName.substr(doubleColon);
}
else
{
childNameThisModel = "::" + childName;
}
childNameThisModel = parentModel->GetName() + childNameThisModel;
this->childLink = boost::dynamic_pointer_cast<Link>(
this->GetWorld()->GetByName(childNameThisModel));
}
if (!this->childLink)
gzthrow("Couldn't Find Child Link[" + childName + "]");
}
this->LoadImpl(_sdf->Get<math::Pose>("pose"));
}
/////////////////////////////////////////////////
void Joint::LoadImpl(const math::Pose &_pose)
{
this->anchorPose = _pose;
BasePtr myBase = shared_from_this();
if (this->parentLink)
this->parentLink->AddChildJoint(boost::static_pointer_cast<Joint>(myBase));
if (this->childLink)
this->childLink->AddParentJoint(boost::static_pointer_cast<Joint>(myBase));
if (!this->parentLink && !this->childLink)
gzthrow("both parent and child link do no exist");
// setting anchor relative to gazebo child link frame position
math::Pose worldPose = this->GetWorldPose();
this->anchorPos = worldPose.pos;
// Compute anchor pose relative to parent frame.
if (this->parentLink)
this->parentAnchorPose = worldPose - this->parentLink->GetWorldPose();
else
this->parentAnchorPose = worldPose;
if (this->sdf->HasElement("sensor"))
{
sdf::ElementPtr sensorElem = this->sdf->GetElement("sensor");
while (sensorElem)
{
/// \todo This if statement is a hack to prevent Joints from creating
/// other sensors. We should make this more generic.
if (sensorElem->Get<std::string>("type") == "force_torque")
{
// This must match the implementation in Sensors::GetScopedName
std::string sensorName = this->GetScopedName(true) + "::" +
sensorElem->Get<std::string>("name");
// Tell the sensor library to create a sensor.
event::Events::createSensor(sensorElem,
this->GetWorld()->GetName(), this->GetScopedName(), this->GetId());
this->sensors.push_back(sensorName);
}
else
gzerr << "A joint cannot load a [" <<
sensorElem->Get<std::string>("type") << "] sensor.\n";
sensorElem = sensorElem->GetNextElement("sensor");
}
}
}
//////////////////////////////////////////////////
void Joint::Init()
{
try
{
this->Attach(this->parentLink, this->childLink);
}
catch(...)
{
gzerr << "Attach joint failed" << std::endl;
return;
}
// Set the anchor vector
this->SetAnchor(0, this->anchorPos);
if (this->GetAngleCount() >= 1 && this->sdf->HasElement("axis"))
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis");
this->SetAxis(0, axisElem->Get<math::Vector3>("xyz"));
if (axisElem->HasElement("limit"))
{
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// Perform this three step ordering to ensure the
// parameters are set properly.
// This is taken from the ODE wiki.
this->SetHighStop(0, this->upperLimit[0].Radian());
this->SetLowStop(0, this->lowerLimit[0].Radian());
this->SetHighStop(0, this->upperLimit[0].Radian());
}
}
if (this->GetAngleCount() >= 2 && this->sdf->HasElement("axis2"))
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis2");
this->SetAxis(1, axisElem->Get<math::Vector3>("xyz"));
if (axisElem->HasElement("limit"))
{
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// Perform this three step ordering to ensure the
// parameters are set properly.
// This is taken from the ODE wiki.
this->SetHighStop(1, this->upperLimit[1].Radian());
this->SetLowStop(1, this->lowerLimit[1].Radian());
this->SetHighStop(1, this->upperLimit[1].Radian());
}
}
// Set parent name: if parentLink is NULL, it's name be the world
if (!this->parentLink)
this->sdf->GetElement("parent")->Set("world");
}
//////////////////////////////////////////////////
void Joint::Fini()
{
this->applyDamping.reset();
// Remove all the sensors attached to the joint
for (auto const &sensor : this->sensors)
{
event::Events::removeSensor(sensor);
}
this->sensors.clear();
this->anchorLink.reset();
this->childLink.reset();
this->parentLink.reset();
this->model.reset();
this->sdfJoint.reset();
Base::Fini();
}
//////////////////////////////////////////////////
math::Vector3 Joint::GetLocalAxis(unsigned int _index) const
{
math::Vector3 vec;
if (_index == 0 && this->sdf->HasElement("axis"))
vec = this->sdf->GetElement("axis")->Get<math::Vector3>("xyz");
else if (this->sdf->HasElement("axis2"))
vec = this->sdf->GetElement("axis2")->Get<math::Vector3>("xyz");
// vec = this->childLink->GetWorldPose().rot.RotateVectorReverse(vec);
// vec.Round();
return vec;
}
//////////////////////////////////////////////////
void Joint::SetEffortLimit(unsigned int _index, double _effort)
{
if (_index < this->GetAngleCount())
{
this->effortLimit[_index] = _effort;
return;
}
gzerr << "SetEffortLimit index[" << _index << "] out of range" << std::endl;
}
//////////////////////////////////////////////////
void Joint::SetVelocityLimit(unsigned int _index, double _velocity)
{
if (_index < this->GetAngleCount())
{
this->velocityLimit[_index] = _velocity;
return;
}
gzerr << "SetVelocityLimit index["
<< _index
<< "] out of range"
<< std::endl;
}
//////////////////////////////////////////////////
double Joint::GetEffortLimit(unsigned int _index)
{
if (_index < this->GetAngleCount())
return this->effortLimit[_index];
gzerr << "GetEffortLimit index[" << _index << "] out of range\n";
return 0;
}
//////////////////////////////////////////////////
double Joint::GetVelocityLimit(unsigned int _index)
{
if (_index < this->GetAngleCount())
return this->velocityLimit[_index];
gzerr << "GetVelocityLimit index[" << _index << "] out of range\n";
return 0;
}
//////////////////////////////////////////////////
void Joint::Update()
{
this->jointUpdate();
}
//////////////////////////////////////////////////
void Joint::UpdateParameters(sdf::ElementPtr _sdf)
{
Base::UpdateParameters(_sdf);
/// \todo Update joint specific parameters. Issue #1954
}
//////////////////////////////////////////////////
void Joint::Reset()
{
for (unsigned int i = 0; i < this->GetAngleCount(); ++i)
{
this->SetVelocity(i, 0.0);
}
this->staticAngle.SetFromRadian(0);
}
//////////////////////////////////////////////////
void Joint::Attach(LinkPtr _parent, LinkPtr _child)
{
this->parentLink = _parent;
this->childLink = _child;
}
//////////////////////////////////////////////////
void Joint::Detach()
{
if (this->parentLink)
this->parentLink->RemoveChildJoint(this->GetName());
if (this->childLink)
this->childLink->RemoveParentJoint(this->GetName());
}
//////////////////////////////////////////////////
void Joint::SetModel(ModelPtr _model)
{
this->model = _model;
this->SetWorld(this->model->GetWorld());
}
//////////////////////////////////////////////////
double Joint::GetParam(const std::string &_key, unsigned int _index)
{
if (_key == "hi_stop")
{
return this->GetHighStop(_index).Radian();
}
else if (_key == "lo_stop")
{
return this->GetLowStop(_index).Radian();
}
gzerr << "GetParam unrecognized parameter ["
<< _key
<< "]"
<< std::endl;
return 0;
}
//////////////////////////////////////////////////
LinkPtr Joint::GetChild() const
{
return this->childLink;
}
//////////////////////////////////////////////////
LinkPtr Joint::GetParent() const
{
return this->parentLink;
}
//////////////////////////////////////////////////
msgs::Joint::Type Joint::GetMsgType() const
{
if (this->HasType(Base::HINGE_JOINT))
{
return msgs::Joint::REVOLUTE;
}
else if (this->HasType(Base::HINGE2_JOINT))
{
return msgs::Joint::REVOLUTE2;
}
else if (this->HasType(Base::BALL_JOINT))
{
return msgs::Joint::BALL;
}
else if (this->HasType(Base::SLIDER_JOINT))
{
return msgs::Joint::PRISMATIC;
}
else if (this->HasType(Base::SCREW_JOINT))
{
return msgs::Joint::SCREW;
}
else if (this->HasType(Base::GEARBOX_JOINT))
{
return msgs::Joint::GEARBOX;
}
else if (this->HasType(Base::UNIVERSAL_JOINT))
{
return msgs::Joint::UNIVERSAL;
}
else if (this->HasType(Base::FIXED_JOINT))
{
return msgs::Joint::FIXED;
}
gzerr << "No joint recognized in type ["
<< this->GetType()
<< "], returning REVOLUTE"
<< std::endl;
return msgs::Joint::REVOLUTE;
}
//////////////////////////////////////////////////
void Joint::FillMsg(msgs::Joint &_msg)
{
_msg.set_name(this->GetScopedName());
_msg.set_id(this->GetId());
msgs::Set(_msg.mutable_pose(), this->anchorPose.Ign());
_msg.set_type(this->GetMsgType());
for (unsigned int i = 0; i < this->GetAngleCount(); ++i)
{
_msg.add_angle(this->GetAngle(i).Radian());
msgs::Axis *axis;
if (i == 0)
axis = _msg.mutable_axis1();
else if (i == 1)
axis = _msg.mutable_axis2();
else
break;
msgs::Set(axis->mutable_xyz(), this->GetLocalAxis(i).Ign());
axis->set_limit_lower(this->GetLowStop(i).Radian());
axis->set_limit_upper(this->GetHighStop(i).Radian());
axis->set_limit_effort(this->GetEffortLimit(i));
axis->set_limit_velocity(this->GetVelocityLimit(i));
axis->set_damping(this->GetDamping(i));
axis->set_friction(this->GetParam("friction", i));
axis->set_use_parent_model_frame(this->axisParentModelFrame[i]);
}
if (this->GetParent())
{
_msg.set_parent(this->GetParent()->GetScopedName());
_msg.set_parent_id(this->GetParent()->GetId());
}
else
{
_msg.set_parent("world");
_msg.set_parent_id(0);
}
if (this->GetChild())
{
_msg.set_child(this->GetChild()->GetScopedName());
_msg.set_child_id(this->GetChild()->GetId());
}
else
{
_msg.set_child("world");
_msg.set_parent_id(0);
}
// Add in the sensor data.
if (this->sdf->HasElement("sensor"))
{
sdf::ElementPtr sensorElem = this->sdf->GetElement("sensor");
while (sensorElem)
{
msgs::Sensor *msg = _msg.add_sensor();
msg->CopyFrom(msgs::SensorFromSDF(sensorElem));
msg->set_parent(this->GetScopedName());
msg->set_parent_id(this->GetId());
sensorElem = sensorElem->GetNextElement("sensor");
}
}
}
//////////////////////////////////////////////////
math::Angle Joint::GetAngle(unsigned int _index) const
{
if (this->model->IsStatic())
return this->staticAngle;
else
return this->GetAngleImpl(_index);
}
//////////////////////////////////////////////////
bool Joint::SetHighStop(unsigned int _index, const math::Angle &_angle)
{
this->SetUpperLimit(_index, _angle);
// switch below to return this->SetUpperLimit when we implement
// issue #1108
return true;
}
//////////////////////////////////////////////////
bool Joint::SetLowStop(unsigned int _index, const math::Angle &_angle)
{
this->SetLowerLimit(_index, _angle);
// switch below to return this->SetLowerLimit when we implement
// issue #1108
return true;
}
//////////////////////////////////////////////////
bool Joint::SetPosition(unsigned int /*_index*/, double _position)
{
// parent class doesn't do much, derived classes do all the work.
if (this->model)
{
if (this->model->IsStatic())
{
this->staticAngle = _position;
}
}
else
{
gzwarn << "model not setup yet, setting staticAngle.\n";
this->staticAngle = _position;
}
return true;
}
//////////////////////////////////////////////////
bool Joint::SetPositionMaximal(unsigned int _index, double _position)
{
// check if index is within bounds
if (_index >= this->GetAngleCount())
{
gzerr << "Joint axis index ["
<< _index
<< "] larger than angle count ["
<< this->GetAngleCount()
<< "]."
<< std::endl;
return false;
}
/// If the Joint is static, Gazebo stores the state of
/// this Joint as a scalar inside the Joint class in Joint::SetPosition.
if (!Joint::SetPosition(_index, _position))
{
gzerr << "Joint::SetPosition failed, "
<< "but it's not possible as implemented.\n";
return false;
}
// truncate position by joint limits
double lower = this->GetLowerLimit(_index).Radian();
double upper = this->GetUpperLimit(_index).Radian();
if (lower < upper)
_position = math::clamp(_position, lower, upper);
else
_position = math::clamp(_position, upper, lower);
// only deal with hinge, universal, slider joints in the user
// request joint_names list
if (this->HasType(Base::HINGE_JOINT) ||
this->HasType(Base::UNIVERSAL_JOINT) ||
this->HasType(Base::SLIDER_JOINT))
{
if (childLink)
{
// Get all connected links to this joint
Link_V connectedLinks;
if (this->FindAllConnectedLinks(this->parentLink, connectedLinks))
{
// debug
// gzerr << "found connected links: ";
// for (Link_V::iterator li = connectedLinks.begin();
// li != connectedLinks.end(); ++li)
// std::cout << (*li)->GetName() << " ";
// std::cout << "\n";
// successfully found a subset of links connected to this joint
// (parent link cannot be in this set). Next, compute transform
// to apply to all these links.
// Everything here must be done within one time step,
// Link pose updates need to be synchronized.
// compute transform about the current anchor, about the axis
// rotate child (childLink) about anchor point,
// Get Child Link Pose
math::Pose childLinkPose = this->childLink->GetWorldPose();
// Compute new child link pose based on position change
math::Pose newChildLinkPose =
this->ComputeChildLinkPose(_index, _position);
// debug
// gzerr << "child link pose0 [" << childLinkPose
// << "] new child link pose0 [" << newChildLinkPose
// << "]\n";
// update all connected links
{
// block any other physics pose updates
boost::recursive_mutex::scoped_lock lock(
*this->GetWorld()->GetPhysicsEngine()->GetPhysicsUpdateMutex());
for (Link_V::iterator li = connectedLinks.begin();
li != connectedLinks.end(); ++li)
{
// set pose of each link based on child link pose change
(*li)->MoveFrame(childLinkPose, newChildLinkPose);
// debug
// gzerr << "moved " << (*li)->GetName()
// << " p0 [" << childLinkPose
// << "] p1 [" << newChildLinkPose
// << "]\n";
}
}
}
else
{
// if parent Link is found in search, return false
gzwarn << "failed to find a clean set of connected links,"
<< " i.e. this joint is inside a loop, cannot SetPosition"
<< " kinematically.\n";
return false;
}
}
else
{
gzerr << "child link is null.\n";
return false;
}
}
else
{
gzerr << "joint type SetPosition not supported.\n";
return false;
}
/// \todo: Set link and joint "velocities" based on change / time
return true;
}
//////////////////////////////////////////////////
bool Joint::SetVelocityMaximal(unsigned int _index, double _velocity)
{
// check if index is within bounds
if (_index >= this->GetAngleCount())
{
gzerr << "Joint axis index ["
<< _index
<< "] larger than angle count ["
<< this->GetAngleCount()
<< "]."
<< std::endl;
return false;
}
// Set child link relative to parent for now.
// TODO: recursive velocity setting on trees.
if (!this->childLink)
{
gzerr << "SetVelocityMaximal failed for joint ["
<< this->GetScopedName()
<< "] since a child link was not found."
<< std::endl;
return false;
}
// only deal with hinge, universal, slider joints for now
if (this->HasType(Base::HINGE_JOINT) ||
this->HasType(Base::UNIVERSAL_JOINT))
{
// Desired angular and linear velocity in world frame for child link
math::Vector3 angularVel, linearVel;
if (this->parentLink)
{
// Use parent link velocity as reference (if parent exists)
angularVel = this->parentLink->GetWorldAngularVel();
// Get parent linear velocity at joint anchor
// Passing unit quaternion q ensures that parentOffset will be
// interpreted in world frame.
math::Quaternion q;
math::Vector3 parentOffset =
this->GetParentWorldPose().pos - this->parentLink->GetWorldPose().pos;
linearVel = this->parentLink->GetWorldLinearVel(parentOffset, q);
}
// Add desired velocity along specified axis
angularVel += _velocity * this->GetGlobalAxis(_index);
if (this->HasType(Base::UNIVERSAL_JOINT))
{
// For multi-axis joints, retain velocity of other axis.
unsigned int otherIndex = (_index + 1) % 2;
angularVel += this->GetVelocity(otherIndex)
* this->GetGlobalAxis(otherIndex);
}
this->childLink->SetAngularVel(angularVel);
// Compute desired linear velocity of the child link based on
// offset between the child's CG and the joint anchor
// and the desired angular velocity.
math::Vector3 childCoGOffset =
this->childLink->GetWorldCoGPose().pos - this->GetWorldPose().pos;
linearVel += angularVel.Cross(childCoGOffset);
this->childLink->SetLinearVel(linearVel);
}
else if (this->HasType(Base::SLIDER_JOINT))
{
math::Vector3 desiredVel;
if (this->parentLink)
{
desiredVel = this->parentLink->GetWorldLinearVel();
}
desiredVel += _velocity * this->GetGlobalAxis(_index);
this->childLink->SetLinearVel(desiredVel);
}
else
{
gzerr << "SetVelocityMaximal does not yet support"
<< " this joint type."
<< std::endl;
return false;
}
return true;
}
//////////////////////////////////////////////////
void Joint::SetState(const JointState &_state)
{
for (unsigned int i = 0; i < _state.GetAngleCount(); ++i)
{
this->SetVelocity(i, 0.0);
this->SetPosition(i, _state.GetAngle(i).Radian());
}
}
//////////////////////////////////////////////////
double Joint::CheckAndTruncateForce(unsigned int _index, double _effort)
{
if (_index >= this->GetAngleCount())
{
gzerr << "Calling Joint::SetForce with an index ["
<< _index << "] out of range\n";
return _effort;
}
// truncating SetForce effort if velocity limit is reached and
// effort is applied in the same direction.
if (this->velocityLimit[_index] >= 0)
{
if (this->GetVelocity(_index) > this->velocityLimit[_index])
_effort = _effort > 0 ? 0 : _effort;
else if (this->GetVelocity(_index) < -this->velocityLimit[_index])
_effort = _effort < 0 ? 0 : _effort;
}
// truncate effort if effortLimit is not negative
if (this->effortLimit[_index] >= 0.0)
_effort = math::clamp(_effort, -this->effortLimit[_index],
this->effortLimit[_index]);
return _effort;
}
//////////////////////////////////////////////////
double Joint::GetForce(unsigned int /*_index*/)
{
gzerr << "Joint::GetForce should be overloaded by physics engines.\n";
return 0;
}
//////////////////////////////////////////////////
void Joint::ApplyStiffnessDamping()
{
gzerr << "Joint::ApplyStiffnessDamping should be overloaded by "
<< "physics engines.\n";
}
//////////////////////////////////////////////////
double Joint::GetInertiaRatio(const math::Vector3 &_axis) const
{
if (this->parentLink && this->childLink)
{
math::Matrix3 pm = this->parentLink->GetWorldInertiaMatrix();
math::Matrix3 cm = this->childLink->GetWorldInertiaMatrix();
// matrix times axis
math::Vector3 pia = pm * _axis;
math::Vector3 cia = cm * _axis;
double piam = pia.GetLength();
double ciam = cia.GetLength();
// return ratio of child MOI to parent MOI.
if (!math::equal(piam, 0.0))
{
return ciam/piam;
}
else
{
gzerr << "Parent MOI is zero, ratio is not well defined.\n";
return 0;
}
}
else
{
gzerr << "Either parent or child link is missing or static, "
<< "cannot compute inertia ratio. Returning 0.\n";
return 0;
}
}
//////////////////////////////////////////////////
double Joint::GetInertiaRatio(const unsigned int _index) const
{
if (this->parentLink && this->childLink)
{
if (_index < this->GetAngleCount())
{
// joint axis in global frame
math::Vector3 axis = this->GetGlobalAxis(_index);
// compute ratio about axis
return this->GetInertiaRatio(axis);
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get inertia ratio across joint.\n";
return 0;
}
}
else
{
gzerr << "Either parent or child link is missing or static, "
<< "cannot compute inertia ratio. Returning 0.\n";
return 0;
}
}
//////////////////////////////////////////////////
double Joint::GetDamping(unsigned int _index)
{
if (_index < this->GetAngleCount())
{
return this->dissipationCoefficient[_index];
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get damping coefficient.\n";
return 0;
}
}
//////////////////////////////////////////////////
double Joint::GetStiffness(unsigned int _index)
{
if (static_cast<unsigned int>(_index) < this->GetAngleCount())
{
return this->stiffnessCoefficient[_index];
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get stiffness coefficient.\n";
return 0;
}
}
//////////////////////////////////////////////////
double Joint::GetSpringReferencePosition(unsigned int _index) const
{
if (_index < this->GetAngleCount())
{
return this->springReferencePosition[_index];
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get spring reference position.\n";
return 0;
}
}
//////////////////////////////////////////////////
math::Angle Joint::GetLowerLimit(unsigned int _index) const
{
if (_index < this->GetAngleCount())
return this->lowerLimit[_index];
gzwarn << "requesting lower limit of joint index out of bound\n";
return math::Angle();
}
//////////////////////////////////////////////////
math::Angle Joint::GetUpperLimit(unsigned int _index) const
{
if (_index < this->GetAngleCount())
return this->upperLimit[_index];
gzwarn << "requesting upper limit of joint index out of bound\n";
return math::Angle();
}
//////////////////////////////////////////////////
void Joint::SetLowerLimit(unsigned int _index, math::Angle _limit)
{
if (_index >= this->GetAngleCount())
{
gzerr << "SetLowerLimit for index [" << _index
<< "] out of bounds [" << this->GetAngleCount()
<< "]\n";
return;
}
if (_index == 0)
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis");
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// store lower joint limits
this->lowerLimit[_index] = _limit;
limitElem->GetElement("lower")->Set(_limit.Radian());
}
else if (_index == 1)
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis2");
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// store lower joint limits
this->lowerLimit[_index] = _limit;
limitElem->GetElement("lower")->Set(_limit.Radian());
}
else
{
gzwarn << "SetLowerLimit for joint [" << this->GetName()
<< "] index [" << _index
<< "] not supported\n";
}
}
//////////////////////////////////////////////////
void Joint::SetUpperLimit(unsigned int _index, math::Angle _limit)
{
if (_index >= this->GetAngleCount())
{
gzerr << "SetUpperLimit for index [" << _index
<< "] out of bounds [" << this->GetAngleCount()
<< "]\n";
return;
}
if (_index == 0)
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis");
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// store upper joint limits
this->upperLimit[_index] = _limit;
limitElem->GetElement("upper")->Set(_limit.Radian());
}
else if (_index == 1)
{
sdf::ElementPtr axisElem = this->sdf->GetElement("axis2");
sdf::ElementPtr limitElem = axisElem->GetElement("limit");
// store upper joint limits
this->upperLimit[_index] = _limit;
limitElem->GetElement("upper")->Set(_limit.Radian());
}
else
{
gzwarn << "SetUpperLimit for joint [" << this->GetName()
<< "] index [" << _index
<< "] not supported\n";
}
}
//////////////////////////////////////////////////
void Joint::SetProvideFeedback(bool _enable)
{
this->provideFeedback = _enable;
}
//////////////////////////////////////////////////
void Joint::SetStopStiffness(unsigned int _index, double _stiffness)
{
if (_index < this->GetAngleCount())
{
this->stopStiffness[_index] = _stiffness;
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to set joint stop stiffness.\n";
}
}
//////////////////////////////////////////////////
void Joint::SetStopDissipation(unsigned int _index, double _dissipation)
{
if (_index < this->GetAngleCount())
{
this->stopDissipation[_index] = _dissipation;
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to set joint stop dissipation.\n";
}
}
//////////////////////////////////////////////////
double Joint::GetStopStiffness(unsigned int _index) const
{
if (_index < this->GetAngleCount())
{
return this->stopStiffness[_index];
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get joint stop stiffness.\n";
return 0;
}
}
//////////////////////////////////////////////////
double Joint::GetStopDissipation(unsigned int _index) const
{
if (_index < this->GetAngleCount())
{
return this->stopDissipation[_index];
}
else
{
gzerr << "Invalid joint index [" << _index
<< "] when trying to get joint stop dissipation.\n";
return 0;
}
}
//////////////////////////////////////////////////
math::Pose Joint::GetInitialAnchorPose() const
{
return this->anchorPose;
}
//////////////////////////////////////////////////
math::Pose Joint::GetWorldPose() const
{
if (this->childLink)
return this->anchorPose + this->childLink->GetWorldPose();
return this->anchorPose;
}
//////////////////////////////////////////////////
math::Pose Joint::GetParentWorldPose() const
{
if (this->parentLink)
return this->parentAnchorPose + this->parentLink->GetWorldPose();
return this->parentAnchorPose;
}
//////////////////////////////////////////////////
math::Pose Joint::GetAnchorErrorPose() const
{
return this->GetWorldPose() - this->GetParentWorldPose();
}
//////////////////////////////////////////////////
math::Quaternion Joint::GetAxisFrame(unsigned int _index) const
{
if (_index >= this->GetAngleCount())
{
gzerr << "GetAxisFrame error, _index[" << _index << "] out of range"
<< std::endl;
return math::Quaternion();
}
// Legacy support for specifying axis in parent model frame (#494)
if (this->axisParentModelFrame[_index])
{
// Use parent model frame
if (this->parentLink)
return this->parentLink->GetModel()->GetWorldPose().rot;
// Parent model is world, use world frame
return math::Quaternion();
}
return this->GetWorldPose().rot;
}
//////////////////////////////////////////////////
math::Quaternion Joint::GetAxisFrameOffset(unsigned int _index) const
{
if (_index >= this->GetAngleCount())
{
gzerr << "GetAxisFrame error, _index[" << _index << "] out of range"
<< " returning identity rotation." << std::endl;
return math::Quaternion();
}
// Legacy support for specifying axis in parent model frame (#494)
if (this->axisParentModelFrame[_index])
{
// axis is defined in parent model frame, so return the rotation
// from joint frame to parent model frame, or
// world frame in absence of parent link.
math::Pose parentModelWorldPose;
math::Pose jointWorldPose = this->GetWorldPose();
if (this->parentLink)
{
parentModelWorldPose = this->parentLink->GetModel()->GetWorldPose();
}
return (parentModelWorldPose - jointWorldPose).rot;
}
// axis is defined in the joint frame, so
// return the rotation from joint frame to joint frame.
return math::Quaternion();
}
//////////////////////////////////////////////////
double Joint::GetWorldEnergyPotentialSpring(unsigned int _index) const
{
if (_index >= this->GetAngleCount())
{
gzerr << "Get spring potential error, _index[" << _index
<< "] out of range" << std::endl;
return 0;
}
// compute potential energy due to spring compression
// 1/2 k x^2
double k = this->stiffnessCoefficient[_index];
double x = this->GetAngle(_index).Radian() -
this->springReferencePosition[_index];
return 0.5 * k * x * x;
}
//////////////////////////////////////////////////
void Joint::CacheForceTorque()
{
}
//////////////////////////////////////////////////
bool Joint::FindAllConnectedLinks(const LinkPtr &_originalParentLink,
Link_V &_connectedLinks)
{
// debug
// std::string pn;
// if (_originalParentLink) pn = _originalParentLink->GetName();
// gzerr << "first call to find connected links: "
// << " parent " << pn
// << " this joint " << this->GetName() << "\n";
// unlikely, but check anyways to make sure we don't have a 0-height tree
if (this->childLink.get() == _originalParentLink.get())
{
// if parent is a child
gzerr << "we have a zero length loop.\n";
_connectedLinks.clear();
return false;
}
else
{
// add this->childLink to the list of descendent child links (should be
// the very first one added).
_connectedLinks.push_back(this->childLink);
// START RECURSIVE SEARCH, start adding child links of this->childLink
// to the collection of _connectedLinks.
return this->childLink->FindAllConnectedLinksHelper(_originalParentLink,
_connectedLinks, true);
}
}
//////////////////////////////////////////////////
math::Pose Joint::ComputeChildLinkPose(unsigned int _index,
double _position)
{
// child link pose
math::Pose childLinkPose = this->childLink->GetWorldPose();
// default return to current pose
math::Pose newRelativePose;
math::Pose newWorldPose = childLinkPose;
// get anchor and axis of the joint
math::Vector3 anchor;
math::Vector3 axis;
if (this->model->IsStatic())
{
/// \TODO: we want to get axis in global frame, but GetGlobalAxis
/// not implemented for static models yet.
axis = childLinkPose.rot.RotateVector(this->GetLocalAxis(_index));
anchor = childLinkPose.pos;
}
else
{
anchor = this->GetAnchor(_index);
axis = this->GetGlobalAxis(_index);
}
// delta-position along an axis
double dposition = _position - this->GetAngle(_index).Radian();
if (this->HasType(Base::HINGE_JOINT) ||
this->HasType(Base::UNIVERSAL_JOINT))
{
// relative to anchor point
math::Pose relativePose(childLinkPose.pos - anchor,
childLinkPose.rot);
// take axis rotation and turn it into a quaternion
math::Quaternion rotation(axis, dposition);
// rotate relative pose by rotation
newRelativePose.pos = rotation.RotateVector(relativePose.pos);
newRelativePose.rot = rotation * relativePose.rot;
newWorldPose =
math::Pose(newRelativePose.pos + anchor, newRelativePose.rot);
// \TODO: ideally we want to set this according to
// Joint Trajectory velocity and use time step since last update.
/*
double dt =
this->dataPtr->model->GetWorld()->GetPhysicsEngine()->GetMaxStepTime();
this->ComputeAndSetLinkTwist(_link, newWorldPose, newWorldPose, dt);
*/
}
else if (this->HasType(Base::SLIDER_JOINT))
{
// relative to anchor point
math::Pose relativePose(childLinkPose.pos - anchor,
childLinkPose.rot);
// slide relative pose by dposition along axis
newRelativePose.pos = relativePose.pos + axis * dposition;
newRelativePose.rot = relativePose.rot;
newWorldPose =
math::Pose(newRelativePose.pos + anchor, newRelativePose.rot);
/// \TODO: ideally we want to set this according to Joint Trajectory
/// velocity and use time step since last update.
/*
double dt =
this->dataPtr->model->GetWorld()->GetPhysicsEngine()->GetMaxStepTime();
this->ComputeAndSetLinkTwist(_link, newWorldPose, newWorldPose, dt);
*/
}
else
{
gzerr << "Setting joint position is only supported for"
<< " hinge, universal and slider joints right now.\n";
}
return newWorldPose;
}