341 lines
9.9 KiB
C++
341 lines
9.9 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 <sstream>
|
|
|
|
#include "gazebo/common/Exception.hh"
|
|
|
|
#include "gazebo/transport/Node.hh"
|
|
|
|
#include "gazebo/physics/PhysicsIface.hh"
|
|
#include "gazebo/physics/Contact.hh"
|
|
#include "gazebo/physics/World.hh"
|
|
#include "gazebo/physics/Collision.hh"
|
|
#include "gazebo/physics/ContactManager.hh"
|
|
#include "gazebo/physics/PhysicsEngine.hh"
|
|
|
|
#include "gazebo/sensors/SensorFactory.hh"
|
|
#include "gazebo/sensors/ContactSensorPrivate.hh"
|
|
#include "gazebo/sensors/ContactSensor.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace sensors;
|
|
|
|
GZ_REGISTER_STATIC_SENSOR("contact", ContactSensor)
|
|
|
|
//////////////////////////////////////////////////
|
|
ContactSensor::ContactSensor()
|
|
: Sensor(sensors::OTHER),
|
|
dataPtr(new ContactSensorPrivate)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
ContactSensor::~ContactSensor()
|
|
{
|
|
this->dataPtr->collisions.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ContactSensor::Load(const std::string &_worldName, sdf::ElementPtr _sdf)
|
|
{
|
|
Sensor::Load(_worldName, _sdf);
|
|
|
|
// Create a publisher for the contact information.
|
|
if (this->sdf->HasElement("contact") &&
|
|
this->sdf->GetElement("contact")->HasElement("topic") &&
|
|
this->sdf->GetElement("contact")->Get<std::string>("topic")
|
|
!= "__default_topic__")
|
|
{
|
|
// This will create a topic based on the name specified in SDF.
|
|
this->dataPtr->contactsPub = this->node->Advertise<msgs::Contacts>(
|
|
this->sdf->GetElement("contact")->Get<std::string>("topic"),
|
|
100);
|
|
}
|
|
else
|
|
{
|
|
// This will create a topic based on the name of the parent and the
|
|
// name of the sensor.
|
|
std::string topicName = "~/";
|
|
topicName += this->ParentName() + "/" + this->Name();
|
|
boost::replace_all(topicName, "::", "/");
|
|
|
|
this->dataPtr->contactsPub =
|
|
this->node->Advertise<msgs::Contacts>(topicName, 100);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ContactSensor::Load(const std::string &_worldName)
|
|
{
|
|
Sensor::Load(_worldName);
|
|
|
|
std::string collisionName;
|
|
std::string collisionScopedName;
|
|
|
|
sdf::ElementPtr collisionElem =
|
|
this->sdf->GetElement("contact")->GetElement("collision");
|
|
|
|
std::string entityName =
|
|
this->world->GetEntity(this->ParentName())->GetScopedName();
|
|
this->dataPtr->filterName = entityName + "::" + this->Name();
|
|
|
|
// Get all the collision elements
|
|
while (collisionElem)
|
|
{
|
|
// get collision name
|
|
collisionName = collisionElem->Get<std::string>();
|
|
collisionScopedName = entityName;
|
|
collisionScopedName += "::" + collisionName;
|
|
|
|
this->dataPtr->collisions.push_back(collisionScopedName);
|
|
|
|
collisionElem = collisionElem->GetNextElement("collision");
|
|
}
|
|
|
|
if (!this->dataPtr->collisions.empty())
|
|
{
|
|
// request the contact manager to publish messages to a custom topic for
|
|
// this sensor
|
|
physics::ContactManager *mgr =
|
|
this->world->GetPhysicsEngine()->GetContactManager();
|
|
std::string topic = mgr->CreateFilter(this->dataPtr->filterName,
|
|
this->dataPtr->collisions);
|
|
if (!this->dataPtr->contactSub)
|
|
{
|
|
this->dataPtr->contactSub = this->node->Subscribe(topic,
|
|
&ContactSensor::OnContacts, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ContactSensor::Init()
|
|
{
|
|
Sensor::Init();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool ContactSensor::UpdateImpl(const bool /*_force*/)
|
|
{
|
|
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
|
|
|
|
// Don't do anything if there is no new data to process.
|
|
if (this->dataPtr->incomingContacts.empty())
|
|
return false;
|
|
|
|
std::vector<std::string>::iterator collIter;
|
|
std::string collision1;
|
|
|
|
// Clear the outgoing contact message.
|
|
this->dataPtr->contactsMsg.clear_contact();
|
|
|
|
// Iterate over all the contact messages
|
|
for (auto iter = this->dataPtr->incomingContacts.begin();
|
|
iter != this->dataPtr->incomingContacts.end(); ++iter)
|
|
{
|
|
// Iterate over all the contacts in the message
|
|
for (int i = 0; i < (*iter)->contact_size(); ++i)
|
|
{
|
|
collision1 = (*iter)->contact(i).collision1();
|
|
|
|
// Try to find the first collision's name
|
|
collIter = std::find(this->dataPtr->collisions.begin(),
|
|
this->dataPtr->collisions.end(), collision1);
|
|
|
|
// If unable to find the first collision's name, try the second
|
|
if (collIter == this->dataPtr->collisions.end())
|
|
{
|
|
collision1 = (*iter)->contact(i).collision2();
|
|
collIter = std::find(this->dataPtr->collisions.begin(),
|
|
this->dataPtr->collisions.end(), collision1);
|
|
}
|
|
|
|
// If this sensor is monitoring one of the collision's in the
|
|
// contact, then add the contact to our outgoing message.
|
|
if (collIter != this->dataPtr->collisions.end())
|
|
{
|
|
int count = (*iter)->contact(i).position_size();
|
|
|
|
// Check to see if the contact arrays all have the same size.
|
|
if (count != (*iter)->contact(i).normal_size() ||
|
|
count != (*iter)->contact(i).wrench_size() ||
|
|
count != (*iter)->contact(i).depth_size())
|
|
{
|
|
gzerr << "Contact message has invalid array sizes\n";
|
|
continue;
|
|
}
|
|
|
|
// Copy the contact message.
|
|
msgs::Contact *contactMsg = this->dataPtr->contactsMsg.add_contact();
|
|
contactMsg->CopyFrom((*iter)->contact(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the incoming contact list.
|
|
this->dataPtr->incomingContacts.clear();
|
|
|
|
this->lastMeasurementTime = this->world->GetSimTime();
|
|
msgs::Set(this->dataPtr->contactsMsg.mutable_time(),
|
|
this->lastMeasurementTime);
|
|
|
|
// Generate a outgoing message only if someone is listening.
|
|
if (this->dataPtr->contactsPub &&
|
|
this->dataPtr->contactsPub->HasConnections())
|
|
{
|
|
this->dataPtr->contactsPub->Publish(this->dataPtr->contactsMsg);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ContactSensor::Fini()
|
|
{
|
|
if (this->world && this->world->GetRunning())
|
|
{
|
|
physics::ContactManager *mgr =
|
|
this->world->GetPhysicsEngine()->GetContactManager();
|
|
mgr->RemoveFilter(this->dataPtr->filterName);
|
|
}
|
|
|
|
this->dataPtr->contactSub.reset();
|
|
this->dataPtr->contactsPub.reset();
|
|
Sensor::Fini();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int ContactSensor::GetCollisionCount() const
|
|
{
|
|
return this->dataPtr->collisions.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
std::string ContactSensor::GetCollisionName(unsigned int _index) const
|
|
{
|
|
std::string result;
|
|
|
|
if (_index < this->dataPtr->collisions.size())
|
|
result = this->dataPtr->collisions[_index];
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int ContactSensor::GetCollisionContactCount(
|
|
const std::string &_collisionName) const
|
|
{
|
|
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
|
|
unsigned int result = 0;
|
|
|
|
for (int i = 0; i < this->dataPtr->contactsMsg.contact_size(); ++i)
|
|
{
|
|
if (this->dataPtr->contactsMsg.contact(i).collision1() == _collisionName ||
|
|
this->dataPtr->contactsMsg.contact(i).collision2() == _collisionName)
|
|
{
|
|
result += this->dataPtr->contactsMsg.contact(i).position_size();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
msgs::Contacts ContactSensor::GetContacts() const
|
|
{
|
|
return this->Contacts();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
msgs::Contacts ContactSensor::Contacts() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
|
|
return this->dataPtr->contactsMsg;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
std::map<std::string, gazebo::physics::Contact> ContactSensor::GetContacts(
|
|
const std::string &_collisionName)
|
|
{
|
|
return this->Contacts(_collisionName);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
std::map<std::string, gazebo::physics::Contact> ContactSensor::Contacts(
|
|
const std::string &_collisionName) const
|
|
{
|
|
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
|
|
|
|
std::map<std::string, gazebo::physics::Contact> result;
|
|
|
|
std::string collision2;
|
|
|
|
for (int i = 0; i < this->dataPtr->contactsMsg.contact_size(); ++i)
|
|
{
|
|
collision2.clear();
|
|
|
|
if (this->dataPtr->contactsMsg.contact(i).collision1() == _collisionName)
|
|
{
|
|
collision2 = this->dataPtr->contactsMsg.contact(i).collision2();
|
|
}
|
|
else if (this->dataPtr->contactsMsg.contact(i).collision2() ==
|
|
_collisionName)
|
|
{
|
|
collision2 = this->dataPtr->contactsMsg.contact(i).collision1();
|
|
}
|
|
|
|
if (collision2.empty())
|
|
continue;
|
|
|
|
result[collision2] = this->dataPtr->contactsMsg.contact(i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void ContactSensor::OnContacts(ConstContactsPtr &_msg)
|
|
{
|
|
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
|
|
|
|
// Only store information if the sensor is active
|
|
if (this->IsActive())
|
|
{
|
|
// Store the contacts message for processing in UpdateImpl
|
|
this->dataPtr->incomingContacts.push_back(_msg);
|
|
|
|
// Prevent the incomingContacts list to grow indefinitely.
|
|
if (this->dataPtr->incomingContacts.size() > 100)
|
|
this->dataPtr->incomingContacts.pop_front();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool ContactSensor::IsActive() const
|
|
{
|
|
return this->active ||
|
|
(this->dataPtr->contactsPub &&
|
|
this->dataPtr->contactsPub->HasConnections());
|
|
}
|