1684 lines
48 KiB
C++
1684 lines
48 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 <boost/algorithm/string.hpp>
|
|
#include <boost/bind.hpp>
|
|
#include <sstream>
|
|
#include <functional>
|
|
|
|
#include "gazebo/msgs/msgs.hh"
|
|
|
|
#include "gazebo/transport/TransportIface.hh"
|
|
#include "gazebo/transport/Node.hh"
|
|
#include "gazebo/transport/Publisher.hh"
|
|
|
|
#include "gazebo/util/OpenAL.hh"
|
|
#include "gazebo/common/Events.hh"
|
|
#include "gazebo/math/Quaternion.hh"
|
|
#include "gazebo/common/Console.hh"
|
|
#include "gazebo/common/Exception.hh"
|
|
#include "gazebo/common/Assert.hh"
|
|
#include "gazebo/common/Battery.hh"
|
|
|
|
#include "gazebo/physics/PhysicsIface.hh"
|
|
#include "gazebo/physics/Light.hh"
|
|
#include "gazebo/physics/Model.hh"
|
|
#include "gazebo/physics/World.hh"
|
|
#include "gazebo/physics/ContactManager.hh"
|
|
#include "gazebo/physics/PhysicsEngine.hh"
|
|
#include "gazebo/physics/Collision.hh"
|
|
#include "gazebo/physics/Link.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace physics;
|
|
|
|
//////////////////////////////////////////////////
|
|
Link::Link(EntityPtr _parent)
|
|
: Entity(_parent), initialized(false)
|
|
{
|
|
this->AddType(Base::LINK);
|
|
this->inertial.reset(new Inertial);
|
|
this->parentJoints.clear();
|
|
this->childJoints.clear();
|
|
this->publishData = false;
|
|
this->publishDataMutex = new boost::recursive_mutex();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Link::~Link()
|
|
{
|
|
this->Fini();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::Load(sdf::ElementPtr _sdf)
|
|
{
|
|
Entity::Load(_sdf);
|
|
|
|
// before loading child collision, we have to figure out if selfCollide is
|
|
// true and modify parent class Entity so this body has its own spaceId
|
|
if (this->sdf->HasElement("self_collide"))
|
|
{
|
|
this->SetSelfCollide(this->sdf->Get<bool>("self_collide"));
|
|
}
|
|
else
|
|
{
|
|
this->SetSelfCollide(this->GetModel()->GetSelfCollide());
|
|
}
|
|
this->sdf->GetElement("self_collide")->GetValue()->SetUpdateFunc(std::bind(&Link::GetSelfCollide, this));
|
|
|
|
// Parse visuals from SDF
|
|
this->ParseVisuals();
|
|
|
|
// Load the geometries
|
|
if (this->sdf->HasElement("collision"))
|
|
{
|
|
sdf::ElementPtr collisionElem = this->sdf->GetElement("collision");
|
|
while (collisionElem)
|
|
{
|
|
// Create and Load a collision, which will belong to this body.
|
|
this->LoadCollision(collisionElem);
|
|
collisionElem = collisionElem->GetNextElement("collision");
|
|
}
|
|
}
|
|
|
|
// Modified by zhangshuai 2019.04.02 ----Begin
|
|
if (this->GetWorld()->GetFlag() == 1)
|
|
{
|
|
// Judge if the model to load is simulated in this gazebo
|
|
for (unsigned int i = 0; i < this->GetWorld()->GetDistribution()->GetGazeboIDPtr(this->GetWorld()->GetGazeboLocalID())->GetModelCount(); i++)
|
|
{
|
|
if (this->GetModel()->GetName() == this->GetWorld()->GetDistribution()->GetGazeboIDPtr(this->GetWorld()->GetGazeboLocalID())->GetModelName(i))
|
|
{
|
|
// Original part
|
|
if (this->sdf->HasElement("sensor"))
|
|
{
|
|
sdf::ElementPtr sensorElem = this->sdf->GetElement("sensor");
|
|
while (sensorElem)
|
|
{
|
|
/// \todo This if statement is a hack to prevent Links from creating
|
|
/// a force torque sensor. We should make this more generic.
|
|
if (sensorElem->Get<std::string>("type") == "force_torque")
|
|
{
|
|
gzerr << "A link cannot load a [" << sensorElem->Get<std::string>("type") << "] sensor.\n";
|
|
}
|
|
else if (sensorElem->Get<std::string>("type") != "__default__")
|
|
{
|
|
// 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);
|
|
}
|
|
sensorElem = sensorElem->GetNextElement("sensor");
|
|
}
|
|
}
|
|
// Original part
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this->sdf->HasElement("sensor"))
|
|
{
|
|
sdf::ElementPtr sensorElem = this->sdf->GetElement("sensor");
|
|
while (sensorElem)
|
|
{
|
|
/// \todo This if statement is a hack to prevent Links from creating
|
|
/// a force torque sensor. We should make this more generic.
|
|
if (sensorElem->Get<std::string>("type") == "force_torque")
|
|
{
|
|
gzerr << "A link cannot load a [" << sensorElem->Get<std::string>("type") << "] sensor.\n";
|
|
}
|
|
else if (sensorElem->Get<std::string>("type") != "__default__")
|
|
{
|
|
// 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);
|
|
}
|
|
sensorElem = sensorElem->GetNextElement("sensor");
|
|
}
|
|
}
|
|
}
|
|
// Modified by zhangshuai 2019.04.02 ----End
|
|
|
|
// Load the lights
|
|
if (this->sdf->HasElement("light"))
|
|
{
|
|
sdf::ElementPtr lightElem = this->sdf->GetElement("light");
|
|
while (lightElem)
|
|
{
|
|
// Create and Load a light
|
|
this->world->LoadLight(lightElem, shared_from_this());
|
|
lightElem = lightElem->GetNextElement("light");
|
|
}
|
|
}
|
|
|
|
if (!this->IsStatic())
|
|
{
|
|
this->inertial->Load(this->sdf->GetElement("inertial"));
|
|
}
|
|
else
|
|
{
|
|
this->inertial->SetMass(0.0);
|
|
this->inertial->SetInertiaMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
|
|
#ifdef HAVE_OPENAL
|
|
if (_sdf->HasElement("audio_source"))
|
|
{
|
|
// bool onContact = false;
|
|
sdf::ElementPtr audioElem = this->sdf->GetElement("audio_source");
|
|
std::vector<std::string> collisionNames;
|
|
|
|
while (audioElem)
|
|
{
|
|
util::OpenALSourcePtr source = util::OpenAL::Instance()->CreateSource(
|
|
audioElem);
|
|
|
|
std::vector<std::string> names = source->CollisionNames();
|
|
std::copy(names.begin(), names.end(), std::back_inserter(collisionNames));
|
|
|
|
audioElem = audioElem->GetNextElement("audio_source");
|
|
this->audioSources.push_back(source);
|
|
}
|
|
|
|
if (!collisionNames.empty())
|
|
{
|
|
for (std::vector<std::string>::iterator iter = collisionNames.begin();
|
|
iter != collisionNames.end(); ++iter)
|
|
{
|
|
(*iter) = this->GetScopedName() + "::" + (*iter);
|
|
}
|
|
|
|
std::string topic =
|
|
this->world->GetPhysicsEngine()->GetContactManager()->CreateFilter(
|
|
this->GetScopedName() + "/audio_collision", collisionNames);
|
|
this->audioContactsSub = this->node->Subscribe(topic,
|
|
&Link::OnCollision, this);
|
|
}
|
|
}
|
|
|
|
if (_sdf->HasElement("audio_sink"))
|
|
{
|
|
this->audioSink = util::OpenAL::Instance()->CreateSink(
|
|
_sdf->GetElement("audio_sink"));
|
|
}
|
|
#endif
|
|
|
|
if (this->sdf->HasElement("battery"))
|
|
{
|
|
sdf::ElementPtr batteryElem = this->sdf->GetElement("battery");
|
|
while (batteryElem)
|
|
{
|
|
this->LoadBattery(batteryElem);
|
|
batteryElem = batteryElem->GetNextElement("battery");
|
|
}
|
|
}
|
|
|
|
this->connections.push_back(event::Events::ConnectWorldUpdateBegin(
|
|
boost::bind(&Link::Update, this, _1)));
|
|
|
|
std::string topicName = "~/" + this->GetScopedName() + "/wrench";
|
|
boost::replace_all(topicName, "::", "/");
|
|
this->wrenchSub = this->node->Subscribe(topicName, &Link::OnWrenchMsg, this);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::Init()
|
|
{
|
|
this->linearAccel.Set(0, 0, 0);
|
|
this->angularAccel.Set(0, 0, 0);
|
|
|
|
this->enabled = true;
|
|
|
|
// Set Link pose before setting pose of child collisions
|
|
this->SetRelativePose(this->sdf->Get<math::Pose>("pose"));
|
|
this->SetInitialRelativePose(this->sdf->Get<math::Pose>("pose"));
|
|
|
|
// Call Init for child collisions, which whill set their pose
|
|
Base_V::iterator iter;
|
|
for (iter = this->children.begin(); iter != this->children.end(); ++iter)
|
|
{
|
|
if ((*iter)->HasType(Base::COLLISION))
|
|
{
|
|
CollisionPtr collision = boost::static_pointer_cast<Collision>(*iter);
|
|
this->collisions.push_back(collision);
|
|
collision->Init();
|
|
}
|
|
if ((*iter)->HasType(Base::LIGHT))
|
|
{
|
|
LightPtr light = boost::static_pointer_cast<Light>(*iter);
|
|
// TODO add to lights var?
|
|
// this->lights.push_back(light);
|
|
light->Init();
|
|
}
|
|
}
|
|
|
|
// Initialize all the batteries
|
|
for (auto &battery : this->batteries)
|
|
{
|
|
battery->Init();
|
|
}
|
|
|
|
this->initialized = true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::Fini()
|
|
{
|
|
this->attachedModels.clear();
|
|
this->parentJoints.clear();
|
|
this->childJoints.clear();
|
|
this->collisions.clear();
|
|
this->inertial.reset();
|
|
this->batteries.clear();
|
|
|
|
// Remove all the sensors attached to the link
|
|
for (auto const &sensor : this->sensors)
|
|
{
|
|
event::Events::removeSensor(sensor);
|
|
}
|
|
|
|
this->sensors.clear();
|
|
|
|
// Clean up visuals
|
|
// FIXME: Do we really need to send 2 msgs to delete a visual?!
|
|
if (this->visPub && this->requestPub)
|
|
{
|
|
for (auto iter : this->visuals)
|
|
{
|
|
auto deleteMsg = msgs::CreateRequest("entity_delete",
|
|
std::to_string(iter.second.id()));
|
|
this->requestPub->Publish(*deleteMsg, true);
|
|
|
|
msgs::Visual msg;
|
|
msg.set_name(iter.second.name());
|
|
msg.set_id(iter.second.id());
|
|
if (this->parent)
|
|
{
|
|
msg.set_parent_name(this->parent->GetScopedName());
|
|
msg.set_parent_id(this->parent->GetId());
|
|
}
|
|
else
|
|
{
|
|
msg.set_parent_name("");
|
|
msg.set_parent_id(0);
|
|
}
|
|
msg.set_delete_me(true);
|
|
this->visPub->Publish(msg);
|
|
}
|
|
}
|
|
this->visuals.clear();
|
|
|
|
#ifdef HAVE_OPENAL
|
|
if (this->world && this->world->GetPhysicsEngine() &&
|
|
this->world->GetPhysicsEngine()->GetContactManager())
|
|
{
|
|
this->world->GetPhysicsEngine()->GetContactManager()->RemoveFilter(
|
|
this->GetScopedName() + "/audio_collision");
|
|
}
|
|
this->audioContactsSub.reset();
|
|
this->audioSink.reset();
|
|
this->audioSources.clear();
|
|
#endif
|
|
|
|
// Clean transport
|
|
{
|
|
this->dataPub.reset();
|
|
this->visPub.reset();
|
|
|
|
this->wrenchSub.reset();
|
|
}
|
|
this->connections.clear();
|
|
|
|
delete this->publishDataMutex;
|
|
this->publishDataMutex = NULL;
|
|
|
|
Entity::Fini();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::Reset()
|
|
{
|
|
// resets pose
|
|
Entity::Reset();
|
|
|
|
// resets velocity, acceleration, wrench
|
|
this->ResetPhysicsStates();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::ResetPhysicsStates()
|
|
{
|
|
this->SetAngularVel(math::Vector3(0, 0, 0));
|
|
this->SetLinearVel(math::Vector3(0, 0, 0));
|
|
this->SetAngularAccel(math::Vector3(0, 0, 0));
|
|
this->SetLinearAccel(math::Vector3(0, 0, 0));
|
|
this->SetForce(math::Vector3(0, 0, 0));
|
|
this->SetTorque(math::Vector3(0, 0, 0));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::UpdateParameters(sdf::ElementPtr _sdf)
|
|
{
|
|
Entity::UpdateParameters(_sdf);
|
|
|
|
if (this->sdf->HasElement("inertial"))
|
|
{
|
|
sdf::ElementPtr inertialElem = this->sdf->GetElement("inertial");
|
|
this->inertial->UpdateParameters(inertialElem);
|
|
}
|
|
|
|
this->sdf->GetElement("gravity")->GetValue()->SetUpdateFunc(
|
|
boost::bind(&Link::GetGravityMode, this));
|
|
this->sdf->GetElement("kinematic")->GetValue()->SetUpdateFunc(boost::bind(&Link::GetKinematic, this));
|
|
|
|
if (this->sdf->Get<bool>("gravity") != this->GetGravityMode())
|
|
this->SetGravityMode(this->sdf->Get<bool>("gravity"));
|
|
|
|
// before loading child collision, we have to figure out if
|
|
// selfCollide is true and modify parent class Entity so this
|
|
// body has its own spaceId
|
|
this->SetSelfCollide(this->sdf->Get<bool>("self_collide"));
|
|
|
|
// TODO: this shouldn't be in the physics sim
|
|
if (this->sdf->HasElement("visual"))
|
|
{
|
|
sdf::ElementPtr visualElem = this->sdf->GetElement("visual");
|
|
while (visualElem)
|
|
{
|
|
// TODO: Update visuals properly
|
|
msgs::Visual msg = msgs::VisualFromSDF(visualElem);
|
|
|
|
msg.set_name(this->GetScopedName() + "::" + msg.name());
|
|
msg.set_parent_name(this->GetScopedName());
|
|
msg.set_is_static(this->IsStatic());
|
|
msg.set_type(msgs::Visual::VISUAL);
|
|
|
|
this->visPub->Publish(msg);
|
|
|
|
visualElem = visualElem->GetNextElement("visual");
|
|
}
|
|
}
|
|
|
|
if (this->sdf->HasElement("collision"))
|
|
{
|
|
sdf::ElementPtr collisionElem = this->sdf->GetElement("collision");
|
|
while (collisionElem)
|
|
{
|
|
CollisionPtr collision = boost::dynamic_pointer_cast<Collision>(
|
|
this->GetChild(collisionElem->Get<std::string>("name")));
|
|
|
|
if (collision)
|
|
collision->UpdateParameters(collisionElem);
|
|
collisionElem = collisionElem->GetNextElement("collision");
|
|
}
|
|
}
|
|
|
|
// Update the battery information
|
|
if (this->sdf->HasElement("battery"))
|
|
{
|
|
sdf::ElementPtr batteryElem = this->sdf->GetElement("battery");
|
|
while (batteryElem)
|
|
{
|
|
common::BatteryPtr battery = this->Battery(
|
|
batteryElem->Get<std::string>("name"));
|
|
|
|
if (battery)
|
|
battery->UpdateParameters(batteryElem);
|
|
batteryElem = batteryElem->GetNextElement("battery");
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetCollideMode(const std::string &_mode)
|
|
{
|
|
unsigned int categoryBits;
|
|
unsigned int collideBits;
|
|
|
|
if (_mode == "all")
|
|
{
|
|
categoryBits = GZ_ALL_COLLIDE;
|
|
collideBits = GZ_ALL_COLLIDE;
|
|
}
|
|
else if (_mode == "none")
|
|
{
|
|
categoryBits = GZ_NONE_COLLIDE;
|
|
collideBits = GZ_NONE_COLLIDE;
|
|
}
|
|
else if (_mode == "sensors")
|
|
{
|
|
categoryBits = GZ_SENSOR_COLLIDE;
|
|
collideBits = ~GZ_SENSOR_COLLIDE;
|
|
}
|
|
else if (_mode == "fixed")
|
|
{
|
|
categoryBits = GZ_FIXED_COLLIDE;
|
|
collideBits = ~GZ_FIXED_COLLIDE;
|
|
}
|
|
else if (_mode == "ghost")
|
|
{
|
|
categoryBits = GZ_GHOST_COLLIDE;
|
|
collideBits = ~GZ_GHOST_COLLIDE;
|
|
}
|
|
else
|
|
{
|
|
gzerr << "Unknown collide mode[" << _mode << "]\n";
|
|
return;
|
|
}
|
|
|
|
for (Collision_V::iterator iter = this->collisions.begin();
|
|
iter != this->collisions.end(); ++iter)
|
|
{
|
|
if ((*iter))
|
|
{
|
|
(*iter)->SetCategoryBits(categoryBits);
|
|
(*iter)->SetCollideBits(collideBits);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Link::GetSelfCollide() const
|
|
{
|
|
GZ_ASSERT(this->sdf != NULL, "Link sdf member is NULL");
|
|
if (this->sdf->HasElement("self_collide"))
|
|
return this->sdf->Get<bool>("self_collide");
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetLaserRetro(float _retro)
|
|
{
|
|
for (Collision_V::iterator iter = this->collisions.begin();
|
|
iter != this->collisions.end(); ++iter)
|
|
{
|
|
(*iter)->SetLaserRetro(_retro);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::Update(const common::UpdateInfo & /*_info*/)
|
|
{
|
|
#ifdef HAVE_OPENAL
|
|
if (this->audioSink)
|
|
{
|
|
this->audioSink->SetPose(this->GetWorldPose().Ign());
|
|
this->audioSink->SetVelocity(this->GetWorldLinearVel().Ign());
|
|
}
|
|
|
|
// Update all the audio sources
|
|
for (std::vector<util::OpenALSourcePtr>::iterator iter =
|
|
this->audioSources.begin();
|
|
iter != this->audioSources.end(); ++iter)
|
|
{
|
|
(*iter)->SetPose(this->GetWorldPose().Ign());
|
|
(*iter)->SetVelocity(this->GetWorldLinearVel().Ign());
|
|
}
|
|
#endif
|
|
|
|
// FIXME: race condition on factory-based model loading!!!!!
|
|
/*if (this->GetEnabled() != this->enabled)
|
|
{
|
|
this->enabled = this->GetEnabled();
|
|
this->enabledSignal(this->enabled);
|
|
}*/
|
|
|
|
if (!this->wrenchMsgs.empty())
|
|
{
|
|
std::vector<msgs::Wrench> messages;
|
|
{
|
|
boost::mutex::scoped_lock lock(this->wrenchMsgMutex);
|
|
messages = this->wrenchMsgs;
|
|
this->wrenchMsgs.clear();
|
|
}
|
|
|
|
for (auto it : messages)
|
|
{
|
|
this->ProcessWrenchMsg(it);
|
|
}
|
|
}
|
|
|
|
// Update the batteries.
|
|
for (auto &battery : this->batteries)
|
|
{
|
|
battery->Update();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Joint_V Link::GetParentJoints() const
|
|
{
|
|
return this->parentJoints;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Joint_V Link::GetChildJoints() const
|
|
{
|
|
return this->childJoints;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Link_V Link::GetChildJointsLinks() const
|
|
{
|
|
Link_V links;
|
|
for (std::vector<JointPtr>::const_iterator iter = this->childJoints.begin();
|
|
iter != this->childJoints.end();
|
|
++iter)
|
|
{
|
|
if ((*iter)->GetChild())
|
|
links.push_back((*iter)->GetChild());
|
|
}
|
|
return links;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
Link_V Link::GetParentJointsLinks() const
|
|
{
|
|
Link_V links;
|
|
for (std::vector<JointPtr>::const_iterator iter = this->parentJoints.begin();
|
|
iter != this->parentJoints.end();
|
|
++iter)
|
|
{
|
|
if ((*iter)->GetParent())
|
|
links.push_back((*iter)->GetParent());
|
|
}
|
|
return links;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::LoadCollision(sdf::ElementPtr _sdf)
|
|
{
|
|
CollisionPtr collision;
|
|
std::string geomType =
|
|
_sdf->GetElement("geometry")->GetFirstElement()->GetName();
|
|
|
|
if (geomType == "heightmap" || geomType == "map")
|
|
this->SetStatic(true);
|
|
|
|
collision = this->GetWorld()->GetPhysicsEngine()->CreateCollision(geomType,
|
|
boost::static_pointer_cast<Link>(shared_from_this()));
|
|
|
|
if (!collision)
|
|
gzthrow("Unknown Collisionetry Type[" + geomType + "]");
|
|
|
|
collision->Load(_sdf);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
CollisionPtr Link::GetCollisionById(unsigned int _id) const
|
|
{
|
|
return boost::dynamic_pointer_cast<Collision>(this->GetById(_id));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
CollisionPtr Link::GetCollision(const std::string &_name)
|
|
{
|
|
CollisionPtr result;
|
|
Base_V::const_iterator biter;
|
|
for (biter = this->children.begin(); biter != this->children.end(); ++biter)
|
|
{
|
|
if ((*biter)->GetName() == _name)
|
|
{
|
|
result = boost::dynamic_pointer_cast<Collision>(*biter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Collision_V Link::GetCollisions() const
|
|
{
|
|
return this->collisions;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
CollisionPtr Link::GetCollision(unsigned int _index) const
|
|
{
|
|
CollisionPtr collision;
|
|
if (_index <= this->GetChildCount())
|
|
collision = boost::static_pointer_cast<Collision>(this->GetChild(_index));
|
|
else
|
|
gzerr << "Index is out of range\n";
|
|
|
|
return collision;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetLinearAccel(const math::Vector3 &_accel)
|
|
{
|
|
this->SetEnabled(true);
|
|
this->linearAccel = _accel;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetAngularAccel(const math::Vector3 &_accel)
|
|
{
|
|
this->SetEnabled(true);
|
|
this->angularAccel = _accel;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Pose Link::GetWorldCoGPose() const
|
|
{
|
|
math::Pose pose = this->GetWorldPose();
|
|
pose.pos += pose.rot.RotateVector(this->inertial->GetCoG());
|
|
return pose;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeLinearVel() const
|
|
{
|
|
return this->GetWorldPose().rot.RotateVectorReverse(
|
|
this->GetWorldLinearVel());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeAngularVel() const
|
|
{
|
|
return this->GetWorldPose().rot.RotateVectorReverse(
|
|
this->GetWorldAngularVel());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeLinearAccel() const
|
|
{
|
|
return this->GetRelativeForce() / this->inertial->GetMass();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetWorldLinearAccel() const
|
|
{
|
|
return this->GetWorldForce() / this->inertial->GetMass();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeAngularAccel() const
|
|
{
|
|
return this->GetWorldPose().rot.RotateVectorReverse(
|
|
this->GetWorldAngularAccel());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetWorldAngularAccel() const
|
|
{
|
|
// I: inertia matrix in world frame
|
|
// T: sum of external torques in world frame
|
|
// L: angular momentum of CoG in world frame
|
|
// w: angular velocity in world frame
|
|
// return I^-1 * (T - w x L)
|
|
return this->GetWorldInertiaMatrix().Inverse() * (this->GetWorldTorque() - this->GetWorldAngularVel().Cross(this->GetWorldAngularMomentum()));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetWorldAngularMomentum() const
|
|
{
|
|
return this->GetWorldInertiaMatrix() * this->GetWorldAngularVel();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeForce() const
|
|
{
|
|
return this->GetWorldPose().rot.RotateVectorReverse(this->GetWorldForce());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Vector3 Link::GetRelativeTorque() const
|
|
{
|
|
return this->GetWorldPose().rot.RotateVectorReverse(this->GetWorldTorque());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
ModelPtr Link::GetModel() const
|
|
{
|
|
return boost::dynamic_pointer_cast<Model>(this->GetParent());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Box Link::GetBoundingBox() const
|
|
{
|
|
math::Box box;
|
|
|
|
box.min.Set(GZ_DBL_MAX, GZ_DBL_MAX, GZ_DBL_MAX);
|
|
box.max.Set(0, 0, 0);
|
|
|
|
for (Collision_V::const_iterator iter = this->collisions.begin();
|
|
iter != this->collisions.end(); ++iter)
|
|
{
|
|
box += (*iter)->GetBoundingBox();
|
|
}
|
|
|
|
return box;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Link::SetSelected(bool _s)
|
|
{
|
|
Entity::SetSelected(_s);
|
|
|
|
if (_s == false)
|
|
this->SetEnabled(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetInertial(const InertialPtr & /*_inertial*/)
|
|
{
|
|
gzwarn << "Link::SetMass is empty\n";
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Pose Link::GetWorldInertialPose() const
|
|
{
|
|
math::Pose inertialPose;
|
|
if (this->inertial)
|
|
inertialPose = this->inertial->GetPose();
|
|
return inertialPose + this->GetWorldPose();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
math::Matrix3 Link::GetWorldInertiaMatrix() const
|
|
{
|
|
math::Matrix3 moi;
|
|
if (this->inertial)
|
|
{
|
|
math::Vector3 pos = this->inertial->GetPose().pos;
|
|
math::Quaternion rot = this->GetWorldPose().rot.GetInverse();
|
|
moi = this->inertial->GetMOI(math::Pose(pos, rot));
|
|
}
|
|
return moi;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::AddParentJoint(JointPtr _joint)
|
|
{
|
|
this->parentJoints.push_back(_joint);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::AddChildJoint(JointPtr _joint)
|
|
{
|
|
this->childJoints.push_back(_joint);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::RemoveParentJoint(const std::string &_jointName)
|
|
{
|
|
for (std::vector<JointPtr>::iterator iter = this->parentJoints.begin();
|
|
iter != this->parentJoints.end();
|
|
++iter)
|
|
{
|
|
/// @todo: can we assume there are no repeats?
|
|
if ((*iter)->GetName() == _jointName)
|
|
{
|
|
this->parentJoints.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::RemoveChildJoint(const std::string &_jointName)
|
|
{
|
|
for (std::vector<JointPtr>::iterator iter = this->childJoints.begin();
|
|
iter != this->childJoints.end();
|
|
++iter)
|
|
{
|
|
/// @todo: can we assume there are no repeats?
|
|
if ((*iter)->GetName() == _jointName)
|
|
{
|
|
this->childJoints.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::FillMsg(msgs::Link &_msg)
|
|
{
|
|
math::Pose relPose = this->GetRelativePose();
|
|
|
|
_msg.set_id(this->GetId());
|
|
_msg.set_name(this->GetScopedName());
|
|
_msg.set_self_collide(this->GetSelfCollide());
|
|
_msg.set_gravity(this->GetGravityMode());
|
|
_msg.set_kinematic(this->GetKinematic());
|
|
_msg.set_enabled(this->GetEnabled());
|
|
msgs::Set(_msg.mutable_pose(), relPose.Ign());
|
|
|
|
// The visual msgs name might not have been set if the link was created
|
|
// dynamically without using SDF.
|
|
if (!this->visualMsg->has_name())
|
|
this->visualMsg->set_name(this->GetScopedName());
|
|
|
|
msgs::Set(this->visualMsg->mutable_pose(), relPose.Ign());
|
|
_msg.add_visual()->CopyFrom(*this->visualMsg);
|
|
|
|
_msg.mutable_inertial()->set_mass(this->inertial->GetMass());
|
|
_msg.mutable_inertial()->set_ixx(this->inertial->GetIXX());
|
|
_msg.mutable_inertial()->set_ixy(this->inertial->GetIXY());
|
|
_msg.mutable_inertial()->set_ixz(this->inertial->GetIXZ());
|
|
_msg.mutable_inertial()->set_iyy(this->inertial->GetIYY());
|
|
_msg.mutable_inertial()->set_iyz(this->inertial->GetIYZ());
|
|
_msg.mutable_inertial()->set_izz(this->inertial->GetIZZ());
|
|
msgs::Set(_msg.mutable_inertial()->mutable_pose(),
|
|
this->inertial->GetPose().Ign());
|
|
|
|
for (auto &child : this->children)
|
|
{
|
|
if (child->HasType(Base::COLLISION))
|
|
{
|
|
CollisionPtr collision = boost::static_pointer_cast<Collision>(child);
|
|
collision->FillMsg(*_msg.add_collision());
|
|
}
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
if (this->visuals.empty())
|
|
this->ParseVisuals();
|
|
else
|
|
this->UpdateVisualMsg();
|
|
|
|
for (Visuals_M::iterator iter = this->visuals.begin();
|
|
iter != this->visuals.end(); ++iter)
|
|
{
|
|
msgs::Visual *vis = _msg.add_visual();
|
|
vis->CopyFrom(iter->second);
|
|
}
|
|
|
|
if (this->sdf->HasElement("projector"))
|
|
{
|
|
sdf::ElementPtr elem = this->sdf->GetElement("projector");
|
|
|
|
msgs::Projector *proj = _msg.add_projector();
|
|
proj->set_name(
|
|
this->GetScopedName() + "::" + elem->Get<std::string>("name"));
|
|
proj->set_texture(elem->Get<std::string>("texture"));
|
|
proj->set_fov(elem->Get<double>("fov"));
|
|
proj->set_near_clip(elem->Get<double>("near_clip"));
|
|
proj->set_far_clip(elem->Get<double>("far_clip"));
|
|
msgs::Set(proj->mutable_pose(), elem->Get<ignition::math::Pose3d>("pose"));
|
|
}
|
|
|
|
if (this->IsCanonicalLink())
|
|
_msg.set_canonical(true);
|
|
|
|
// Fill message with battery information
|
|
for (auto &battery : this->batteries)
|
|
{
|
|
msgs::Battery *bat = _msg.add_battery();
|
|
bat->set_name(battery->Name());
|
|
bat->set_voltage(battery->Voltage());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::ProcessMsg(const msgs::Link &_msg)
|
|
{
|
|
if (_msg.id() != this->GetId())
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->SetName(_msg.name());
|
|
|
|
if (_msg.has_self_collide())
|
|
this->SetSelfCollide(_msg.self_collide());
|
|
if (_msg.has_gravity())
|
|
{
|
|
this->SetGravityMode(_msg.gravity());
|
|
this->SetEnabled(true);
|
|
}
|
|
if (_msg.has_kinematic())
|
|
{
|
|
this->SetKinematic(_msg.kinematic());
|
|
this->SetEnabled(true);
|
|
}
|
|
if (_msg.has_inertial())
|
|
{
|
|
this->inertial->ProcessMsg(_msg.inertial());
|
|
this->SetEnabled(true);
|
|
// Only update the Center of Mass if object is dynamic
|
|
if (!this->GetKinematic())
|
|
this->UpdateMass();
|
|
}
|
|
|
|
if (_msg.has_pose())
|
|
{
|
|
this->SetEnabled(true);
|
|
this->SetRelativePose(msgs::ConvertIgn(_msg.pose()));
|
|
}
|
|
|
|
for (int i = 0; i < _msg.collision_size(); i++)
|
|
{
|
|
CollisionPtr coll = this->GetCollisionById(_msg.collision(i).id());
|
|
if (coll)
|
|
coll->ProcessMsg(_msg.collision(i));
|
|
}
|
|
if (_msg.collision_size() > 0)
|
|
this->UpdateSurface();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Link::GetSensorCount() const
|
|
{
|
|
return this->sensors.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
std::string Link::GetSensorName(unsigned int _i) const
|
|
{
|
|
if (_i < this->sensors.size())
|
|
return this->sensors[_i];
|
|
|
|
return std::string();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::AttachStaticModel(ModelPtr &_model, const math::Pose &_offset)
|
|
{
|
|
if (!_model->IsStatic())
|
|
{
|
|
gzerr << "AttachStaticModel requires a static model\n";
|
|
return;
|
|
}
|
|
|
|
this->attachedModels.push_back(_model);
|
|
this->attachedModelsOffset.push_back(_offset);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::DetachStaticModel(const std::string &_modelName)
|
|
{
|
|
for (unsigned int i = 0; i < this->attachedModels.size(); i++)
|
|
{
|
|
if (this->attachedModels[i]->GetName() == _modelName)
|
|
{
|
|
this->attachedModels.erase(this->attachedModels.begin() + i);
|
|
this->attachedModelsOffset.erase(this->attachedModelsOffset.begin() + i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::DetachAllStaticModels()
|
|
{
|
|
this->attachedModels.clear();
|
|
this->attachedModelsOffset.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::OnPoseChange()
|
|
{
|
|
math::Pose p;
|
|
for (unsigned int i = 0; i < this->attachedModels.size(); i++)
|
|
{
|
|
p = this->GetWorldPose();
|
|
p.pos += this->attachedModelsOffset[i].pos;
|
|
p.rot = p.rot * this->attachedModelsOffset[i].rot;
|
|
|
|
this->attachedModels[i]->SetWorldPose(p, true);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::SetState(const LinkState &_state)
|
|
{
|
|
this->SetWorldPose(_state.GetPose());
|
|
this->SetLinearVel(_state.GetVelocity().pos);
|
|
this->SetAngularVel(_state.GetVelocity().rot.GetAsEuler());
|
|
this->SetLinearAccel(_state.GetAcceleration().pos);
|
|
this->SetAngularAccel(_state.GetAcceleration().rot.GetAsEuler());
|
|
this->SetForce(_state.GetWrench().pos);
|
|
this->SetTorque(_state.GetWrench().rot.GetAsEuler());
|
|
|
|
/*
|
|
for (unsigned int i = 0; i < _state.GetCollisionStateCount(); ++i)
|
|
{
|
|
CollisionState collisionState = _state.GetCollisionState(i);
|
|
CollisionPtr collision = this->GetCollision(collisionState.GetName());
|
|
if (collision)
|
|
collision->SetState(collisionState);
|
|
else
|
|
gzerr << "Unable to find collision[" << collisionState.GetName() << "]\n";
|
|
}*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
double Link::GetLinearDamping() const
|
|
{
|
|
if (this->sdf->HasElement("velocity_decay"))
|
|
return this->sdf->GetElement("velocity_decay")->Get<double>("linear");
|
|
else
|
|
return 0.0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
double Link::GetAngularDamping() const
|
|
{
|
|
if (this->sdf->HasElement("velocity_decay"))
|
|
return this->sdf->GetElement("velocity_decay")->Get<double>("angular");
|
|
else
|
|
return 0.0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::SetKinematic(const bool & /*_kinematic*/)
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::SetPublishData(bool _enable)
|
|
{
|
|
// Skip if we're trying to disable after the publisher has already been
|
|
// cleared
|
|
if (!_enable && !this->dataPub)
|
|
return;
|
|
|
|
{
|
|
boost::recursive_mutex::scoped_lock lock(*this->publishDataMutex);
|
|
if (this->publishData == _enable)
|
|
return;
|
|
|
|
this->publishData = _enable;
|
|
}
|
|
if (_enable)
|
|
{
|
|
std::string topic = "~/" + this->GetScopedName();
|
|
this->dataPub = this->node->Advertise<msgs::LinkData>(topic);
|
|
this->connections.push_back(
|
|
event::Events::ConnectWorldUpdateEnd(
|
|
boost::bind(&Link::PublishData, this)));
|
|
}
|
|
else
|
|
{
|
|
this->dataPub.reset();
|
|
// Do we want to clear all of them though?
|
|
this->connections.clear();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::PublishData()
|
|
{
|
|
if (this->publishData && this->dataPub->HasConnections())
|
|
{
|
|
msgs::Set(this->linkDataMsg.mutable_time(), this->world->GetSimTime());
|
|
linkDataMsg.set_name(this->GetScopedName());
|
|
msgs::Set(this->linkDataMsg.mutable_linear_velocity(),
|
|
this->GetWorldLinearVel().Ign());
|
|
msgs::Set(this->linkDataMsg.mutable_angular_velocity(),
|
|
this->GetWorldAngularVel().Ign());
|
|
this->dataPub->Publish(this->linkDataMsg);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
common::BatteryPtr Link::Battery(const std::string &_name) const
|
|
{
|
|
common::BatteryPtr result;
|
|
|
|
for (auto &battery : this->batteries)
|
|
{
|
|
if (battery->Name() == _name)
|
|
{
|
|
result = battery;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
common::BatteryPtr Link::Battery(const size_t _index) const
|
|
{
|
|
if (_index < this->batteries.size())
|
|
return this->batteries[_index];
|
|
else
|
|
return common::BatteryPtr();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
size_t Link::BatteryCount() const
|
|
{
|
|
return this->batteries.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Link::VisualId(const std::string &_visName, uint32_t &_visualId) const
|
|
{
|
|
for (auto &iter : this->visuals)
|
|
{
|
|
if (iter.second.name() == _visName ||
|
|
iter.second.name() == this->GetScopedName() + "::" + _visName)
|
|
{
|
|
_visualId = iter.first;
|
|
return true;
|
|
}
|
|
}
|
|
gzerr << "Trying to get unique ID of visual from invalid visual name["
|
|
<< _visName << "] for link [" << this->GetScopedName() << "]\n";
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Link::VisualPose(const uint32_t _id, ignition::math::Pose3d &_pose) const
|
|
{
|
|
auto iter = this->visuals.find(_id);
|
|
if (iter == this->visuals.end())
|
|
{
|
|
gzerr << "Trying to get pose of visual from invalid visual id[" << _id
|
|
<< "] for link [" << this->GetScopedName() << "]\n";
|
|
return false;
|
|
}
|
|
const msgs::Visual &msg = iter->second;
|
|
if (msg.has_pose())
|
|
{
|
|
_pose = msgs::ConvertIgn(msg.pose());
|
|
}
|
|
else
|
|
{
|
|
// Pose wasn't specified on SDF, use default value
|
|
_pose = ignition::math::Pose3d::Zero;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Link::SetVisualPose(const uint32_t _id,
|
|
const ignition::math::Pose3d &_pose)
|
|
{
|
|
auto iter = this->visuals.find(_id);
|
|
if (iter == this->visuals.end())
|
|
{
|
|
gzerr << "Trying to set pose of visual from invalid visual id[" << _id
|
|
<< "] for link [" << this->GetScopedName() << "]\n";
|
|
return false;
|
|
}
|
|
msgs::Visual &msg = iter->second;
|
|
msgs::Set(msg.mutable_pose(), _pose);
|
|
std::string linkName = this->GetScopedName();
|
|
if (this->sdf->HasElement("visual"))
|
|
{
|
|
sdf::ElementPtr visualElem = this->sdf->GetElement("visual");
|
|
while (visualElem)
|
|
{
|
|
std::string visName = linkName + "::" +
|
|
visualElem->Get<std::string>("name");
|
|
|
|
// update visual msg if it exists
|
|
if (msg.name() == visName)
|
|
{
|
|
visualElem->GetElement("pose")->Set(_pose);
|
|
break;
|
|
}
|
|
|
|
visualElem = visualElem->GetNextElement("visual");
|
|
}
|
|
}
|
|
msgs::Visual visual;
|
|
visual.set_name(msg.name());
|
|
visual.set_id(_id);
|
|
visual.set_parent_name(linkName);
|
|
visual.set_parent_id(this->GetId());
|
|
msgs::Set(visual.mutable_pose(), _pose);
|
|
this->visPub->Publish(visual);
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::OnCollision(ConstContactsPtr &_msg)
|
|
{
|
|
std::string collisionName1;
|
|
std::string collisionName2;
|
|
std::string::size_type pos1, pos2;
|
|
|
|
for (int i = 0; i < _msg->contact_size(); ++i)
|
|
{
|
|
collisionName1 = _msg->contact(i).collision1();
|
|
collisionName2 = _msg->contact(i).collision2();
|
|
pos1 = collisionName1.rfind("::");
|
|
pos2 = collisionName2.rfind("::");
|
|
|
|
GZ_ASSERT(pos1 != std::string::npos, "Invalid collision name");
|
|
GZ_ASSERT(pos2 != std::string::npos, "Invalid collision name");
|
|
|
|
collisionName1 = collisionName1.substr(pos1 + 2);
|
|
collisionName2 = collisionName2.substr(pos2 + 2);
|
|
|
|
#ifdef HAVE_OPENAL
|
|
for (std::vector<util::OpenALSourcePtr>::iterator iter =
|
|
this->audioSources.begin();
|
|
iter != this->audioSources.end(); ++iter)
|
|
{
|
|
if ((*iter)->HasCollisionName(collisionName1) ||
|
|
(*iter)->HasCollisionName(collisionName2))
|
|
(*iter)->Play();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::ParseVisuals()
|
|
{
|
|
this->UpdateVisualMsg();
|
|
|
|
for (auto const it : this->visuals)
|
|
this->visPub->Publish(it.second);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::RemoveChild(EntityPtr _child)
|
|
{
|
|
if (_child->HasType(COLLISION))
|
|
{
|
|
this->RemoveCollision(_child->GetScopedName());
|
|
}
|
|
|
|
Entity::RemoveChild(_child->GetId());
|
|
|
|
this->SetEnabled(true);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::RemoveCollision(const std::string &_name)
|
|
{
|
|
for (Collision_V::iterator iter = this->collisions.begin();
|
|
iter != this->collisions.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetName() == _name || (*iter)->GetScopedName() == _name)
|
|
{
|
|
this->collisions.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::SetScale(const math::Vector3 &_scale)
|
|
{
|
|
Base_V::const_iterator biter;
|
|
for (biter = this->children.begin(); biter != this->children.end(); ++biter)
|
|
{
|
|
if ((*biter)->HasType(Base::COLLISION))
|
|
{
|
|
boost::static_pointer_cast<Collision>(*biter)->SetScale(_scale);
|
|
}
|
|
}
|
|
|
|
// update the visual sdf to ensure cloning and saving has the correct values.
|
|
this->UpdateVisualGeomSDF(_scale);
|
|
|
|
this->scale = _scale.Ign();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::UpdateVisualGeomSDF(const math::Vector3 &_scale)
|
|
{
|
|
// TODO: this shouldn't be in the physics sim
|
|
if (this->sdf->HasElement("visual"))
|
|
{
|
|
sdf::ElementPtr visualElem = this->sdf->GetElement("visual");
|
|
while (visualElem)
|
|
{
|
|
sdf::ElementPtr geomElem = visualElem->GetElement("geometry");
|
|
|
|
if (geomElem->HasElement("box"))
|
|
{
|
|
math::Vector3 size =
|
|
geomElem->GetElement("box")->Get<math::Vector3>("size");
|
|
geomElem->GetElement("box")->GetElement("size")->Set(
|
|
_scale / this->scale * size);
|
|
}
|
|
else if (geomElem->HasElement("sphere"))
|
|
{
|
|
// update radius the same way as collision shapes
|
|
double radius = geomElem->GetElement("sphere")->Get<double>("radius");
|
|
double newRadius = std::max(_scale.z, std::max(_scale.x, _scale.y));
|
|
double oldRadius = std::max(this->scale.Z(),
|
|
std::max(this->scale.X(), this->scale.Y()));
|
|
geomElem->GetElement("sphere")->GetElement("radius")->Set(
|
|
newRadius / oldRadius * radius);
|
|
}
|
|
else if (geomElem->HasElement("cylinder"))
|
|
{
|
|
// update radius the same way as collision shapes
|
|
double radius = geomElem->GetElement("cylinder")->Get<double>("radius");
|
|
double newRadius = std::max(_scale.x, _scale.y);
|
|
double oldRadius = std::max(this->scale.X(), this->scale.Y());
|
|
|
|
double length = geomElem->GetElement("cylinder")->Get<double>("length");
|
|
geomElem->GetElement("cylinder")->GetElement("radius")->Set(newRadius / oldRadius * radius);
|
|
geomElem->GetElement("cylinder")->GetElement("length")->Set(_scale.z / this->scale.Z() * length);
|
|
}
|
|
else if (geomElem->HasElement("mesh"))
|
|
geomElem->GetElement("mesh")->GetElement("scale")->Set(_scale);
|
|
|
|
visualElem = visualElem->GetNextElement("visual");
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::UpdateVisualMsg()
|
|
{
|
|
// TODO: this shouldn't be in the physics sim
|
|
if (this->sdf->HasElement("visual"))
|
|
{
|
|
sdf::ElementPtr visualElem = this->sdf->GetElement("visual");
|
|
while (visualElem)
|
|
{
|
|
msgs::Visual msg = msgs::VisualFromSDF(visualElem);
|
|
|
|
bool newVis = true;
|
|
std::string linkName = this->GetScopedName();
|
|
|
|
// update visual msg if it exists
|
|
for (auto &iter : this->visuals)
|
|
{
|
|
std::string visName = linkName + "::" +
|
|
visualElem->Get<std::string>("name");
|
|
if (iter.second.name() == visName)
|
|
{
|
|
iter.second.mutable_geometry()->CopyFrom(msg.geometry());
|
|
newVis = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add to visual msgs if not found.
|
|
if (newVis)
|
|
{
|
|
std::string visName = this->GetScopedName() + "::" + msg.name();
|
|
msg.set_name(visName);
|
|
msg.set_id(physics::getUniqueId());
|
|
msg.set_parent_name(this->GetScopedName());
|
|
msg.set_parent_id(this->GetId());
|
|
msg.set_is_static(this->IsStatic());
|
|
msg.set_type(msgs::Visual::VISUAL);
|
|
|
|
auto iter = this->visuals.find(msg.id());
|
|
if (iter != this->visuals.end())
|
|
gzthrow(std::string("Duplicate visual name[") + msg.name() + "]\n");
|
|
this->visuals[msg.id()] = msg;
|
|
}
|
|
|
|
visualElem = visualElem->GetNextElement("visual");
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
double Link::GetWorldEnergyPotential() const
|
|
{
|
|
// compute gravitational potential energy for link CG location
|
|
// use origin as reference position
|
|
// E = -m g^T z
|
|
double m = this->GetInertial()->GetMass();
|
|
math::Vector3 g = this->GetWorld()->GetPhysicsEngine()->GetGravity();
|
|
math::Vector3 z = this->GetWorldCoGPose().pos;
|
|
return -m * g.Dot(z);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
double Link::GetWorldEnergyKinetic() const
|
|
{
|
|
double energy = 0.0;
|
|
|
|
// compute linear kinetic energy
|
|
// E = 1/2 m v^T v
|
|
{
|
|
double m = this->GetInertial()->GetMass();
|
|
math::Vector3 v = this->GetWorldCoGLinearVel();
|
|
energy += 0.5 * m * v.Dot(v);
|
|
}
|
|
|
|
// compute angular kinetic energy
|
|
// E = 1/2 w^T I w
|
|
{
|
|
math::Vector3 w = this->GetWorldAngularVel();
|
|
math::Matrix3 I = this->GetWorldInertiaMatrix();
|
|
energy += 0.5 * w.Dot(I * w);
|
|
}
|
|
|
|
return energy;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
double Link::GetWorldEnergy() const
|
|
{
|
|
return this->GetWorldEnergyPotential() + this->GetWorldEnergyKinetic();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void Link::MoveFrame(const math::Pose &_worldReferenceFrameSrc,
|
|
const math::Pose &_worldReferenceFrameDst)
|
|
{
|
|
math::Pose targetWorldPose = (this->GetWorldPose() - _worldReferenceFrameSrc) + _worldReferenceFrameDst;
|
|
this->SetWorldPose(targetWorldPose);
|
|
this->SetWorldTwist(math::Vector3(0, 0, 0), math::Vector3(0, 0, 0));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool Link::FindAllConnectedLinksHelper(const LinkPtr &_originalParentLink,
|
|
Link_V &_connectedLinks, bool _fistLink)
|
|
{
|
|
// debug
|
|
// std::string pn;
|
|
// if (_originalParentLink) pn = _originalParentLink->GetName();
|
|
// gzerr << "subsequent call to find connected links: "
|
|
// << " parent " << pn
|
|
// << " this link " << this->GetName() << "\n";
|
|
|
|
// get all child joints from this link
|
|
Link_V childLinks = this->GetChildJointsLinks();
|
|
|
|
// gzerr << "debug: child links are: ";
|
|
// for (Link_V::iterator li = childLinks.begin();
|
|
// li != childLinks.end(); ++li)
|
|
// std::cout << (*li)->GetName() << " ";
|
|
// std::cout << "\n";
|
|
|
|
// loop through all joints where this link is a parent link of the joint
|
|
for (Link_V::iterator li = childLinks.begin();
|
|
li != childLinks.end(); ++li)
|
|
{
|
|
// gzerr << "debug: checking " << (*li)->GetName() << "\n";
|
|
|
|
// check child link of each child joint recursively
|
|
if ((*li).get() == _originalParentLink.get())
|
|
{
|
|
// if parent is a child, failed search to find a nice subset of links
|
|
gzdbg << "we have a loop! cannot find nice subset of connected links,"
|
|
<< " this link " << this->GetName() << " connects back to"
|
|
<< " parent " << _originalParentLink->GetName() << ".\n";
|
|
_connectedLinks.clear();
|
|
return false;
|
|
}
|
|
else if (this->ContainsLink(_connectedLinks, (*li)))
|
|
{
|
|
// do nothing
|
|
// gzerr << "debug: do nothing with " << (*li)->GetName() << "\n";
|
|
}
|
|
else
|
|
{
|
|
// gzerr << "debug: add and recurse " << (*li)->GetName() << "\n";
|
|
// add child link to list
|
|
_connectedLinks.push_back((*li));
|
|
|
|
// recursively check if child link has already been checked
|
|
// if it returns false, it looped back to parent, mark flag and break
|
|
// from current for-loop.
|
|
if (!(*li)->FindAllConnectedLinksHelper(_originalParentLink,
|
|
_connectedLinks))
|
|
{
|
|
// one of the recursed link is the parent link
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \todo: later we can optimize loop below by merging and using a flag.
|
|
|
|
// search parents, but if this is the first search, keep going, otherwise
|
|
// flag failure
|
|
// get all parent joints from this link
|
|
Link_V parentLinks = this->GetParentJointsLinks();
|
|
|
|
// loop through all joints where this link is a parent link of the joint
|
|
for (Link_V::iterator li = parentLinks.begin();
|
|
li != parentLinks.end(); ++li)
|
|
{
|
|
// check child link of each child joint recursively
|
|
if ((*li).get() == _originalParentLink.get())
|
|
{
|
|
if (_fistLink)
|
|
{
|
|
// this is the first child link, simply skip if the parent is
|
|
// the _originalParentLink
|
|
}
|
|
else
|
|
{
|
|
// if parent is a child, failed search to find a nice subset of links
|
|
gzdbg << "we have a loop! cannot find nice subset of connected links,"
|
|
<< " this link " << this->GetName() << " connects back to"
|
|
<< " parent " << _originalParentLink->GetName() << ".\n";
|
|
_connectedLinks.clear();
|
|
return false;
|
|
}
|
|
}
|
|
else if (this->ContainsLink(_connectedLinks, (*li)))
|
|
{
|
|
// do nothing
|
|
}
|
|
else
|
|
{
|
|
// add parent link to list
|
|
_connectedLinks.push_back((*li));
|
|
|
|
// recursively check if parent link has already been checked
|
|
// if it returns false, it looped back to parent, mark flag and break
|
|
// from current for-loop.
|
|
if (!(*li)->FindAllConnectedLinksHelper(_originalParentLink,
|
|
_connectedLinks))
|
|
{
|
|
// one of the recursed link is the parent link
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool Link::ContainsLink(const Link_V &_vector, const LinkPtr &_value)
|
|
{
|
|
for (Link_V::const_iterator iter = _vector.begin();
|
|
iter != _vector.end(); ++iter)
|
|
{
|
|
if ((*iter).get() == _value.get())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
msgs::Visual Link::GetVisualMessage(const std::string &_name) const
|
|
{
|
|
msgs::Visual result;
|
|
|
|
Visuals_M::const_iterator iter;
|
|
for (iter = this->visuals.begin(); iter != this->visuals.end(); ++iter)
|
|
if (iter->second.name() == _name)
|
|
break;
|
|
|
|
if (iter != this->visuals.end())
|
|
result = iter->second;
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::OnWrenchMsg(ConstWrenchPtr &_msg)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->wrenchMsgMutex);
|
|
this->wrenchMsgs.push_back(*_msg);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::ProcessWrenchMsg(const msgs::Wrench &_msg)
|
|
{
|
|
math::Vector3 pos = math::Vector3::Zero;
|
|
if (_msg.has_force_offset())
|
|
{
|
|
pos = msgs::ConvertIgn(_msg.force_offset());
|
|
}
|
|
|
|
const ignition::math::Vector3d force = msgs::ConvertIgn(_msg.force());
|
|
this->AddLinkForce(force, pos);
|
|
|
|
const ignition::math::Vector3d torque = msgs::ConvertIgn(_msg.torque());
|
|
this->AddRelativeTorque(torque);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Link::LoadBattery(sdf::ElementPtr _sdf)
|
|
{
|
|
common::BatteryPtr battery(new common::Battery());
|
|
battery->Load(_sdf);
|
|
this->batteries.push_back(battery);
|
|
}
|