pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/physics/bullet/BulletSliderJoint.cc

458 lines
15 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.
*
*/
#include "gazebo/common/Assert.hh"
#include "gazebo/common/Console.hh"
#include "gazebo/common/Exception.hh"
#include "gazebo/physics/Model.hh"
#include "gazebo/physics/World.hh"
#include "gazebo/physics/bullet/bullet_inc.h"
#include "gazebo/physics/bullet/BulletLink.hh"
#include "gazebo/physics/bullet/BulletPhysics.hh"
#include "gazebo/physics/bullet/BulletSliderJoint.hh"
using namespace gazebo;
using namespace physics;
//////////////////////////////////////////////////
BulletSliderJoint::BulletSliderJoint(btDynamicsWorld *_world, BasePtr _parent)
: SliderJoint<BulletJoint>(_parent)
{
GZ_ASSERT(_world, "bullet world pointer is NULL");
this->bulletWorld = _world;
this->bulletSlider = NULL;
}
//////////////////////////////////////////////////
BulletSliderJoint::~BulletSliderJoint()
{
}
//////////////////////////////////////////////////
void BulletSliderJoint::Load(sdf::ElementPtr _sdf)
{
SliderJoint<BulletJoint>::Load(_sdf);
}
//////////////////////////////////////////////////
void BulletSliderJoint::Init()
{
SliderJoint<BulletJoint>::Init();
BulletLinkPtr bulletChildLink =
boost::static_pointer_cast<BulletLink>(this->childLink);
BulletLinkPtr bulletParentLink =
boost::static_pointer_cast<BulletLink>(this->parentLink);
// Get axis unit vector (expressed in world frame).
math::Vector3 axis = this->initialWorldAxis;
if (axis == math::Vector3::Zero)
{
gzerr << "axis must have non-zero length, resetting to 0 0 1\n";
axis.Set(0, 0, 1);
}
// Local variables used to compute pivots and axes in body-fixed frames
// for the parent and child links.
math::Vector3 pivotParent, pivotChild, axisParent, axisChild;
math::Pose pose;
btTransform frameParent, frameChild;
btVector3 axis2, axis3;
// Initialize pivots to anchorPos, which is expressed in the
// world coordinate frame.
pivotParent = this->anchorPos;
pivotChild = this->anchorPos;
// Check if parentLink exists. If not, the parent will be the world.
if (this->parentLink)
{
// Compute relative pose between joint anchor and inertial frame of parent.
pose = this->parentLink->GetWorldInertialPose();
// Subtract CoG position from anchor position, both in world frame.
pivotParent -= pose.pos;
// Rotate pivot offset and axis into body-fixed inertial frame of parent.
pivotParent = pose.rot.RotateVectorReverse(pivotParent);
frameParent.setOrigin(BulletTypes::ConvertVector3(pivotParent));
axisParent = pose.rot.RotateVectorReverse(axis);
axisParent = axisParent.Normalize();
// The following math is based on btHingeConstraint.cpp:95-115
btPlaneSpace1(BulletTypes::ConvertVector3(axisParent), axis2, axis3);
frameParent.getBasis().setValue(
axisParent.x, axis2.x(), axis3.x(),
axisParent.y, axis2.y(), axis3.y(),
axisParent.z, axis2.z(), axis3.z());
}
// Check if childLink exists. If not, the child will be the world.
if (this->childLink)
{
// Compute relative pose between joint anchor and inertial frame of child.
pose = this->childLink->GetWorldInertialPose();
// Subtract CoG position from anchor position, both in world frame.
pivotChild -= pose.pos;
// Rotate pivot offset and axis into body-fixed inertial frame of child.
pivotChild = pose.rot.RotateVectorReverse(pivotChild);
frameChild.setOrigin(BulletTypes::ConvertVector3(pivotChild));
axisChild = pose.rot.RotateVectorReverse(axis);
axisChild = axisChild.Normalize();
// The following math is based on btHingeConstraint.cpp:95-115
btPlaneSpace1(BulletTypes::ConvertVector3(axisChild), axis2, axis3);
frameChild.getBasis().setValue(
axisChild.x, axis2.x(), axis3.x(),
axisChild.y, axis2.y(), axis3.y(),
axisChild.z, axis2.z(), axis3.z());
}
// If both links exist, then create a joint between the two links.
if (bulletChildLink && bulletParentLink)
{
// Parent and child constraint frames generated with btPlaneSpace1 may
// differ by 90 degrees because they are underdetermined (only one axis
// given). Here we set the child constraint frame by taking the parent
// constraint frame (in the frame of the parent Bullet link) and rotating
// it into the frame of the child Bullet link. This ensures that the
// constraint frames are initially aligned.
pose = math::Pose(pivotChild,
this->childLink->GetWorldInertialPose().rot.GetInverse() *
this->parentLink->GetWorldInertialPose().rot *
BulletTypes::ConvertPose(frameParent).rot);
frameChild = BulletTypes::ConvertPose(pose);
this->bulletSlider = new btSliderConstraint(
*bulletParentLink->GetBulletLink(),
*bulletChildLink->GetBulletLink(),
frameParent, frameChild, true);
}
// If only the child exists, then create a joint between the child
// and the world.
else if (bulletChildLink)
{
this->bulletSlider = new btSliderConstraint(
*bulletChildLink->GetBulletLink(), frameChild, true);
}
// If only the parent exists, then create a joint between the parent
// and the world.
else if (bulletParentLink)
{
this->bulletSlider = new btSliderConstraint(
*bulletParentLink->GetBulletLink(), frameParent, true);
}
// Throw an error if no links are given.
else
{
gzerr << "joint without links\n";
return;
}
if (!this->bulletSlider)
{
gzerr << "unable to create bullet slider joint\n";
return;
}
// btSliderConstraint has 2 degrees-of-freedom (like a piston)
// so disable the rotation.
this->bulletSlider->setLowerAngLimit(0.0);
this->bulletSlider->setUpperAngLimit(0.0);
// Apply joint translation limits here.
// TODO: velocity and effort limits.
GZ_ASSERT(this->sdf != NULL, "Joint sdf member is NULL");
sdf::ElementPtr axisElem = this->sdf->GetElement("axis");
GZ_ASSERT(axisElem != NULL, "Joint axis sdf member is NULL");
{
sdf::ElementPtr limitElem;
limitElem = this->sdf->GetElement("axis")->GetElement("limit");
this->bulletSlider->setLowerLinLimit(limitElem->Get<double>("lower"));
this->bulletSlider->setUpperLinLimit(limitElem->Get<double>("upper"));
}
// Set Joint friction here in Init, since the bullet data structure didn't
// exist when the friction was set during Joint::Load
this->SetParam("friction", 0,
axisElem->GetElement("dynamics")->Get<double>("friction"));
this->constraint = this->bulletSlider;
// Add the joint to the world
GZ_ASSERT(this->bulletWorld, "bullet world pointer is NULL");
this->bulletWorld->addConstraint(this->bulletSlider, true);
// Allows access to impulse
this->constraint->enableFeedback(true);
// Setup Joint force and torque feedback
this->SetupJointFeedback();
}
//////////////////////////////////////////////////
double BulletSliderJoint::GetVelocity(unsigned int /*_index*/) const
{
double result = 0;
math::Vector3 globalAxis = this->GetGlobalAxis(0);
if (this->childLink)
result += globalAxis.Dot(this->childLink->GetWorldLinearVel());
if (this->parentLink)
result -= globalAxis.Dot(this->parentLink->GetWorldLinearVel());
return result;
}
//////////////////////////////////////////////////
void BulletSliderJoint::SetVelocity(unsigned int _index, double _angle)
{
this->SetVelocityMaximal(_index, _angle);
}
//////////////////////////////////////////////////
void BulletSliderJoint::SetAxis(unsigned int /*_index*/,
const math::Vector3 &_axis)
{
// Note that _axis is given in a world frame,
// but bullet uses a body-fixed frame
if (!this->bulletSlider)
{
// this hasn't been initialized yet, store axis in initialWorldAxis
math::Quaternion axisFrame = this->GetAxisFrame(0);
this->initialWorldAxis = axisFrame.RotateVector(_axis);
}
else
{
gzerr << "SetAxis for existing joint is not implemented\n";
}
}
//////////////////////////////////////////////////
void BulletSliderJoint::SetDamping(unsigned int /*index*/,
const double _damping)
{
/// \TODO: special case bullet specific linear damping, this needs testing.
if (this->bulletSlider)
this->bulletSlider->setDampingDirLin(_damping);
}
//////////////////////////////////////////////////
void BulletSliderJoint::SetForceImpl(unsigned int /*_index*/, double _effort)
{
if (this->bulletSlider && this->constraint)
{
// x-axis of constraint frame
btVector3 hingeAxisLocalA =
this->bulletSlider->getFrameOffsetA().getBasis().getColumn(0);
btVector3 hingeAxisLocalB =
this->bulletSlider->getFrameOffsetB().getBasis().getColumn(0);
btVector3 hingeAxisWorldA =
this->bulletSlider->getRigidBodyA().getWorldTransform().getBasis() *
hingeAxisLocalA;
btVector3 hingeAxisWorldB =
this->bulletSlider->getRigidBodyB().getWorldTransform().getBasis() *
hingeAxisLocalB;
btVector3 hingeForceA = _effort * hingeAxisWorldA;
btVector3 hingeForceB = _effort * hingeAxisWorldB;
// TODO: switch to applyForce and specify body-fixed offset
this->constraint->getRigidBodyA().applyCentralForce(-hingeForceA);
this->constraint->getRigidBodyB().applyCentralForce(hingeForceB);
}
}
//////////////////////////////////////////////////
bool BulletSliderJoint::SetHighStop(unsigned int /*_index*/,
const math::Angle &_angle)
{
Joint::SetHighStop(0, _angle);
if (this->bulletSlider)
{
this->bulletSlider->setUpperLinLimit(_angle.Radian());
return true;
}
else
{
gzlog << "bulletSlider not yet created.\n";
return false;
}
}
//////////////////////////////////////////////////
bool BulletSliderJoint::SetLowStop(unsigned int /*_index*/,
const math::Angle &_angle)
{
Joint::SetLowStop(0, _angle);
if (this->bulletSlider)
{
this->bulletSlider->setLowerLinLimit(_angle.Radian());
return true;
}
else
{
gzlog << "bulletSlider not yet created.\n";
return false;
}
}
//////////////////////////////////////////////////
math::Angle BulletSliderJoint::GetHighStop(unsigned int /*_index*/)
{
math::Angle result;
if (this->bulletSlider)
result = this->bulletSlider->getUpperLinLimit();
else
gzlog << "Joint must be created before getting high stop\n";
return result;
}
//////////////////////////////////////////////////
math::Angle BulletSliderJoint::GetLowStop(unsigned int /*_index*/)
{
math::Angle result;
if (this->bulletSlider)
result = this->bulletSlider->getLowerLinLimit();
else
gzlog << "Joint must be created before getting low stop\n";
return result;
}
//////////////////////////////////////////////////
math::Vector3 BulletSliderJoint::GetGlobalAxis(unsigned int /*_index*/) const
{
math::Vector3 result = this->initialWorldAxis;
if (this->bulletSlider)
{
// bullet uses x-axis for slider
btVector3 vec =
this->bulletSlider->getRigidBodyA().getCenterOfMassTransform().getBasis()
* this->bulletSlider->getFrameOffsetA().getBasis().getColumn(0);
result = BulletTypes::ConvertVector3(vec);
}
return result;
}
//////////////////////////////////////////////////
math::Angle BulletSliderJoint::GetAngleImpl(unsigned int _index) const
{
if (_index >= this->GetAngleCount())
{
gzerr << "Invalid axis index [" << _index << "]" << std::endl;
return math::Angle();
}
// The getLinearPos function seems to be off by one time-step
// https://github.com/bulletphysics/bullet3/issues/239
// if (this->bulletSlider)
// result = this->bulletSlider->getLinearPos();
// else
// gzlog << "bulletSlider does not exist, returning default position\n";
// Compute slider angle from gazebo's cached poses instead
math::Vector3 offset = this->GetWorldPose().pos
- this->GetParentWorldPose().pos;
math::Vector3 axis = this->GetGlobalAxis(_index);
return math::Angle(axis.Dot(offset));
}
//////////////////////////////////////////////////
bool BulletSliderJoint::SetParam(const std::string &_key,
unsigned int _index,
const boost::any &_value)
{
if (_index >= this->GetAngleCount())
{
gzerr << "Invalid index [" << _index << "]" << std::endl;
return false;
}
try
{
if (_key == "friction")
{
if (this->bulletSlider)
{
this->bulletSlider->setPoweredLinMotor(true);
this->bulletSlider->setTargetLinMotorVelocity(0.0);
double value = boost::any_cast<double>(_value);
#ifndef LIBBULLET_VERSION_GT_282
// there is an upstream bug in bullet 2.82 and earlier
// the maxLinMotorForce parameter is inadvertently divided
// by timestep when attempting to compute an impulse in
// the bullet solver.
// https://github.com/bulletphysics/bullet3/pull/328
// As a workaround, multiply the desired friction
// parameter by dt^2 when setting
double dt = this->world->GetPhysicsEngine()->GetMaxStepSize();
value *= dt*dt;
#endif
this->bulletSlider->setMaxLinMotorForce(value);
}
else
{
gzerr << "Joint must be created before setting " << _key << std::endl;
return false;
}
}
else
{
return BulletJoint::SetParam(_key, _index, _value);
}
}
catch(const boost::bad_any_cast &e)
{
gzerr << "SetParam(" << _key << ")"
<< " boost any_cast error:" << e.what()
<< std::endl;
return false;
}
return true;
}
//////////////////////////////////////////////////
double BulletSliderJoint::GetParam(const std::string &_key, unsigned int _index)
{
if (_index >= this->GetAngleCount())
{
gzerr << "Invalid index [" << _index << "]" << std::endl;
return 0;
}
if (_key == "friction")
{
if (this->bulletSlider)
{
double value = this->bulletSlider->getMaxLinMotorForce();
#ifndef LIBBULLET_VERSION_GT_282
// there is an upstream bug in bullet 2.82 and earlier
// the maxLinMotorForce parameter is inadvertently divided
// by timestep when attempting to compute an impulse in
// the bullet solver.
// https://github.com/bulletphysics/bullet3/pull/328
// As a workaround, divide the desired friction
// parameter by dt^2 when getting
double dt = this->world->GetPhysicsEngine()->GetMaxStepSize();
value /= dt*dt;
#endif
return value;
}
else
{
gzerr << "Joint must be created before getting " << _key << std::endl;
return 0.0;
}
}
return BulletJoint::GetParam(_key, _index);
}