pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/sensors/SonarSensor.cc

394 lines
12 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 <ignition/math/Vector3.hh>
#include "gazebo/physics/World.hh"
#include "gazebo/physics/SurfaceParams.hh"
#include "gazebo/physics/MeshShape.hh"
#include "gazebo/physics/PhysicsEngine.hh"
#include "gazebo/physics/ContactManager.hh"
#include "gazebo/physics/Collision.hh"
#include "gazebo/common/Assert.hh"
#include "gazebo/msgs/msgs.hh"
#include "gazebo/transport/transport.hh"
#include "gazebo/sensors/SensorFactory.hh"
#include "gazebo/sensors/SonarSensorPrivate.hh"
#include "gazebo/sensors/SonarSensor.hh"
using namespace gazebo;
using namespace sensors;
GZ_REGISTER_STATIC_SENSOR("sonar", SonarSensor)
//////////////////////////////////////////////////
SonarSensor::SonarSensor()
: Sensor(sensors::OTHER),
dataPtr(new SonarSensorPrivate)
{
this->dataPtr->emptyContactCount = 0;
}
//////////////////////////////////////////////////
SonarSensor::~SonarSensor()
{
this->dataPtr->sonarCollision->Fini();
this->dataPtr->sonarCollision.reset();
this->dataPtr->sonarShape->Fini();
this->dataPtr->sonarShape.reset();
}
//////////////////////////////////////////////////
std::string SonarSensor::Topic() const
{
std::string topicName = "~/";
topicName += this->ParentName() + "/" + this->Name() + "/sonar";
boost::replace_all(topicName, "::", "/");
return topicName;
}
//////////////////////////////////////////////////
void SonarSensor::Load(const std::string &_worldName)
{
sdf::ElementPtr sonarElem = this->sdf->GetElement("sonar");
if (!sonarElem)
{
gzerr << "Sonar sensor is missing <sonar> SDF element";
return;
}
this->dataPtr->rangeMin = sonarElem->Get<double>("min");
this->dataPtr->rangeMax = sonarElem->Get<double>("max");
this->dataPtr->radius = sonarElem->Get<double>("radius");
double range = this->dataPtr->rangeMax - this->dataPtr->rangeMin;
if (this->dataPtr->radius < 0)
{
gzerr << "Sonar radius must be > 0. Current value is["
<< this->dataPtr->radius << "]\n";
return;
}
if (this->dataPtr->rangeMin < 0)
{
gzerr << "Min sonar range must be >= 0. Current value is["
<< this->dataPtr->rangeMin << "]\n";
return;
}
if (this->dataPtr->rangeMin > this->dataPtr->rangeMax)
{
gzerr << "Min sonar range of [" << this->dataPtr->rangeMin
<< "] must be less than" << "the max sonar range of["
<< this->dataPtr->rangeMax << "]\n";
return;
}
Sensor::Load(_worldName);
GZ_ASSERT(this->world != NULL,
"SonarSensor did not get a valid World pointer");
this->dataPtr->parentEntity =
this->world->GetEntity(this->ParentName());
GZ_ASSERT(this->dataPtr->parentEntity != NULL,
"Unable to get the parent entity.");
physics::PhysicsEnginePtr physicsEngine =
this->world->GetPhysicsEngine();
GZ_ASSERT(physicsEngine != NULL,
"Unable to get a pointer to the physics engine");
/// \todo: Change the collision shape to a cone. Needs a collision shape
/// within ODE. Or, switch out the collision engine.
this->dataPtr->sonarCollision = physicsEngine->CreateCollision("mesh",
this->ParentName());
GZ_ASSERT(this->dataPtr->sonarCollision != NULL,
"Unable to create a cylinder collision using the physics engine.");
this->dataPtr->sonarCollision->SetName(this->ScopedName() +
"sensor_collision");
// We need a few contacts in order to get the closest collision. This is
// not guaranteed to return the closest contact.
this->dataPtr->sonarCollision->SetMaxContacts(2);
this->dataPtr->sonarCollision->AddType(physics::Base::SENSOR_COLLISION);
this->dataPtr->parentEntity->AddChild(this->dataPtr->sonarCollision);
this->dataPtr->sonarShape = boost::dynamic_pointer_cast<physics::MeshShape>(
this->dataPtr->sonarCollision->GetShape());
GZ_ASSERT(this->dataPtr->sonarShape != NULL,
"Unable to get the sonar shape from the sonar collision.");
// Use a scaled cone mesh for the sonar collision shape.
this->dataPtr->sonarShape->SetMesh("unit_cone");
this->dataPtr->sonarShape->SetScale(ignition::math::Vector3d(
this->dataPtr->radius*2.0, this->dataPtr->radius*2.0, range));
// Position the collision shape properly. Without this, the shape will be
// centered at the start of the sonar.
ignition::math::Vector3d offset(0, 0, range * 0.5);
offset = this->pose.Rot().RotateVector(offset);
this->dataPtr->sonarMidPose.Set(this->pose.Pos() - offset,
this->pose.Rot());
this->dataPtr->sonarCollision->SetRelativePose(this->dataPtr->sonarMidPose);
this->dataPtr->sonarCollision->SetInitialRelativePose(
this->dataPtr->sonarMidPose);
// Don't create contacts when objects collide with the sonar shape
this->dataPtr->sonarCollision->GetSurface()->collideWithoutContact = true;
this->dataPtr->sonarCollision->GetSurface()->collideWithoutContactBitmask = 1;
this->dataPtr->sonarCollision->SetCollideBits(~GZ_SENSOR_COLLIDE);
this->dataPtr->sonarCollision->SetCategoryBits(GZ_SENSOR_COLLIDE);
/*std::vector<std::string> collisions;
collisions.push_back(this->dataPtr->sonarCollision->GetScopedName());
physics::ContactManager *contactMgr =
this->world->GetPhysicsEngine()->GetContactManager();
*/
// Create a contact topic for the collision shape
std::string topic =
this->world->GetPhysicsEngine()->GetContactManager()->CreateFilter(
this->dataPtr->sonarCollision->GetScopedName(),
this->dataPtr->sonarCollision->GetScopedName());
// Subscribe to the contact topic
this->dataPtr->contactSub = this->node->Subscribe(topic,
&SonarSensor::OnContacts, this);
// Advertise the sensor's topic on which we will output range data.
this->dataPtr->sonarPub = this->node->Advertise<msgs::SonarStamped>(
this->Topic());
// Initialize the message that will be published on this->dataPtr->sonarPub.
this->dataPtr->sonarMsg.mutable_sonar()->set_range_min(
this->dataPtr->rangeMin);
this->dataPtr->sonarMsg.mutable_sonar()->set_range_max(
this->dataPtr->rangeMax);
this->dataPtr->sonarMsg.mutable_sonar()->set_radius(
this->dataPtr->radius);
msgs::Set(this->dataPtr->sonarMsg.mutable_sonar()->mutable_world_pose(),
this->dataPtr->sonarMidPose);
this->dataPtr->sonarMsg.mutable_sonar()->set_range(0);
}
//////////////////////////////////////////////////
void SonarSensor::Init()
{
Sensor::Init();
this->dataPtr->sonarMsg.mutable_sonar()->set_frame(this->ParentName());
msgs::Set(this->dataPtr->sonarMsg.mutable_time(),
this->world->GetSimTime());
this->dataPtr->sonarMsg.mutable_sonar()->set_range(this->dataPtr->rangeMax);
if (this->dataPtr->sonarPub)
this->dataPtr->sonarPub->Publish(this->dataPtr->sonarMsg);
}
//////////////////////////////////////////////////
void SonarSensor::Fini()
{
if (this->world && this->world->GetRunning())
{
physics::ContactManager *mgr =
this->world->GetPhysicsEngine()->GetContactManager();
mgr->RemoveFilter(this->dataPtr->sonarCollision->GetScopedName());
}
this->dataPtr->sonarPub.reset();
this->dataPtr->contactSub.reset();
Sensor::Fini();
}
//////////////////////////////////////////////////
double SonarSensor::GetRangeMin() const
{
return this->RangeMin();
}
//////////////////////////////////////////////////
double SonarSensor::RangeMin() const
{
return this->dataPtr->rangeMin;
}
//////////////////////////////////////////////////
double SonarSensor::GetRangeMax() const
{
return this->RangeMax();
}
//////////////////////////////////////////////////
double SonarSensor::RangeMax() const
{
return this->dataPtr->rangeMax;
}
//////////////////////////////////////////////////
double SonarSensor::GetRadius() const
{
return this->Radius();
}
//////////////////////////////////////////////////
double SonarSensor::Radius() const
{
return this->dataPtr->radius;
}
//////////////////////////////////////////////////
double SonarSensor::GetRange()
{
return this->Range();
}
//////////////////////////////////////////////////
double SonarSensor::Range()
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
return this->dataPtr->sonarMsg.sonar().range();
}
//////////////////////////////////////////////////
bool SonarSensor::UpdateImpl(const bool /*_force*/)
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
this->lastMeasurementTime = this->world->GetSimTime();
msgs::Set(this->dataPtr->sonarMsg.mutable_time(),
this->lastMeasurementTime);
ignition::math::Pose3d referencePose =
this->pose + this->dataPtr->parentEntity->GetWorldPose().Ign();
ignition::math::Vector3d pos;
// A 5-step hysteresis window was chosen to reduce range value from
// bouncing.
if (!this->dataPtr->incomingContacts.empty() ||
this->dataPtr->emptyContactCount > 5)
{
this->dataPtr->sonarMsg.mutable_sonar()->set_range(
this->dataPtr->rangeMax);
this->dataPtr->emptyContactCount = 0;
}
else
{
++this->dataPtr->emptyContactCount;
}
// 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)
{
// Debug output:
// std::cout << "C1[" << (*iter)->contact(i).collision1() << "]"
// << "C2[" << (*iter)->contact(i).collision2() << "]\n";
for (int j = 0; j < (*iter)->contact(i).position_size(); ++j)
{
// Get the contact position relative to the reference position.
pos = msgs::ConvertIgn((*iter)->contact(i).position(j)) -
referencePose.Pos();
// Compute the sensed range.
double len = pos.Length() - (*iter)->contact(i).depth(j);
// Debug output:
// std::cout << " RP[" << referencePose << "] P[" << pos
// << "] L[" << len << "] D["
// << (*iter)->contact(i).depth(j) << "]\n";
// Copy the contact message.
if (len < this->dataPtr->sonarMsg.sonar().range())
{
this->dataPtr->sonarMsg.mutable_sonar()->set_range(len);
}
}
}
}
// Clear the incoming contact list.
this->dataPtr->incomingContacts.clear();
this->dataPtr->update(this->dataPtr->sonarMsg);
if (this->dataPtr->sonarPub)
this->dataPtr->sonarPub->Publish(this->dataPtr->sonarMsg);
return true;
}
//////////////////////////////////////////////////
bool SonarSensor::IsActive() const
{
return Sensor::IsActive() || this->dataPtr->sonarPub->HasConnections();
}
//////////////////////////////////////////////////
void SonarSensor::OnContacts(ConstContactsPtr &_msg)
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
// Only store information if the sensor is active
if (this->IsActive() && _msg->contact_size() > 0)
{
// 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();
}
}
//////////////////////////////////////////////////
event::ConnectionPtr SonarSensor::ConnectUpdate(
std::function<void (msgs::SonarStamped)> _subscriber)
{
return this->dataPtr->update.Connect(_subscriber);
}
//////////////////////////////////////////////////
void SonarSensor::DisconnectUpdate(event::ConnectionPtr &_conn)
{
this->dataPtr->update.Disconnect(_conn);
}