pxmlw6n2f/Gazebo_Distributed/plugins/FollowerPlugin.cc

342 lines
9.5 KiB
C++
Raw Normal View History

2019-03-28 10:57:49 +08:00
/*
* Copyright (C) 2016 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 <functional>
#include <mutex>
#include <string>
#include <ignition/math/Box.hh>
#include <sdf/sdf.hh>
#include <gazebo/common/Assert.hh>
#include <gazebo/common/Plugin.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/rendering/DepthCamera.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/sensors/sensors.hh>
#include "plugins/FollowerPlugin.hh"
using namespace gazebo;
GZ_REGISTER_MODEL_PLUGIN(FollowerPlugin)
namespace gazebo
{
/// \internal
/// \brief Private data for the FollowerPlugin class
struct FollowerPluginPrivate
{
/// \brief Pointer to the update event connection.
public: event::ConnectionPtr updateConnection;
/// \brief Pointer to the model.
public: physics::ModelPtr model;
/// \brief Update mutex.
public: std::mutex mutex;
/// \brief Local copy of input image.
public: msgs::Image imageMsg;
/// \brief Revolute joint for moving the left wheel of the vehicle.
public: physics::JointPtr leftJoint;
/// \brief Revolute joint for moving the right wheel of the vehicle.
public: physics::JointPtr rightJoint;
/// \brief Left/Right wheel speed.
public: double wheelSpeed[2];
/// \brief Wheel separation.
public: double wheelSeparation;
/// \brief Wheel radius.
public: double wheelRadius;
/// \brief Connection to the depth camera frame event.
public: event::ConnectionPtr newDepthFrameConnection;
/// \brief Pointer to the depth camera
public: rendering::DepthCameraPtr depthCamera;
/// \brief Depth image data buffer
public: float *depthBuffer = NULL;
};
}
// Used for left/right wheel.
enum {RIGHT, LEFT};
/////////////////////////////////////////////////
FollowerPlugin::FollowerPlugin()
: dataPtr(new FollowerPluginPrivate)
{
this->dataPtr->wheelSpeed[LEFT] = this->dataPtr->wheelSpeed[RIGHT] = 0;
this->dataPtr->wheelSeparation = 1.0;
this->dataPtr->wheelRadius = 1.0;
}
/////////////////////////////////////////////////
FollowerPlugin::~FollowerPlugin()
{
if (this->dataPtr->depthCamera)
{
this->dataPtr->depthCamera->DisconnectNewDepthFrame(
this->dataPtr->newDepthFrameConnection);
}
event::Events::DisconnectWorldUpdateBegin(this->dataPtr->updateConnection);
if (this->dataPtr->depthBuffer != NULL)
delete [] this->dataPtr->depthBuffer;
}
/////////////////////////////////////////////////
void FollowerPlugin::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
{
GZ_ASSERT(_model, "FollowerPlugin _model pointer is NULL");
GZ_ASSERT(_sdf, "FollowerPlugin _sdf pointer is NULL");
if (_model == NULL || _sdf == NULL)
{
gzerr << "Failed to load FollowerPlugin. NULL model or sdf" << std::endl;
return;
}
this->dataPtr->model = _model;
// find depth camera sensor
if (!this->FindSensor(this->dataPtr->model))
{
gzerr << "depth sensor not found!" << std::endl;
return;
}
// diff drive params
if (_sdf->HasElement("left_joint"))
{
this->dataPtr->leftJoint = _model->GetJoint(
_sdf->GetElement("left_joint")->Get<std::string>());
}
if (_sdf->HasElement("right_joint"))
{
this->dataPtr->rightJoint = _model->GetJoint(
_sdf->GetElement("right_joint")->Get<std::string>());
}
if (!this->dataPtr->leftJoint || !this->dataPtr->rightJoint)
this->FindJoints();
if (!this->dataPtr->leftJoint || !this->dataPtr->rightJoint)
{
gzerr << "left or right joint not found!" << std::endl;
return;
}
// Listen to the update event. This event is broadcast every simulation
// iteration.
this->dataPtr->updateConnection = event::Events::ConnectWorldUpdateBegin(
std::bind(&FollowerPlugin::OnUpdate, this));
}
/////////////////////////////////////////////////
void FollowerPlugin::Init()
{
if (!this->dataPtr->leftJoint || !this->dataPtr->rightJoint)
return;
this->dataPtr->wheelSeparation =
this->dataPtr->leftJoint->GetAnchor(0).Distance(
this->dataPtr->rightJoint->GetAnchor(0));
physics::EntityPtr parent = boost::dynamic_pointer_cast<physics::Entity>(
this->dataPtr->leftJoint->GetChild());
ignition::math::Box bb = parent->GetBoundingBox().Ign();
// This assumes that the largest dimension of the wheel is the diameter
this->dataPtr->wheelRadius = bb.Size().Max() * 0.5;
}
/////////////////////////////////////////////////
void FollowerPlugin::Reset()
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
this->dataPtr->imageMsg.Clear();
}
/////////////////////////////////////////////////
void FollowerPlugin::FindJoints()
{
// assumes the first two revolute joints are the ones connecting the
// wheels to the chassis
auto joints = this->dataPtr->model->GetJoints();
if (joints.size() < 2u)
return;
physics::Joint_V revJoints;
for (const auto &j : joints)
{
if (j->GetMsgType() == msgs::Joint::REVOLUTE)
revJoints.push_back(j);
}
if (revJoints.size() < 2u)
return;
this->dataPtr->leftJoint = revJoints[0];
this->dataPtr->rightJoint = revJoints[1];
}
/////////////////////////////////////////////////
bool FollowerPlugin::FindSensor(const physics::ModelPtr &_model)
{
// loop through links to find depth sensor
for (const auto l : _model->GetLinks())
{
for (unsigned int i = 0; i < l->GetSensorCount(); ++i)
{
std::string sensorName = l->GetSensorName(i);
sensors::SensorPtr sensor = sensors::get_sensor(sensorName);
if (!sensor)
continue;
if (sensor->Type() == "depth")
{
sensors::DepthCameraSensorPtr depthSensor =
std::dynamic_pointer_cast<sensors::DepthCameraSensor>(sensor);
if (depthSensor)
{
rendering::DepthCameraPtr camera =
depthSensor->DepthCamera();
if (camera)
{
this->dataPtr->depthCamera = camera;
this->dataPtr->newDepthFrameConnection =
this->dataPtr->depthCamera->ConnectNewDepthFrame(
std::bind(&FollowerPlugin::OnNewDepthFrame, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5));
return true;
}
}
}
}
}
// recursively look for sensor in nested models
for (const auto &m : _model->NestedModels())
{
if (this->FindSensor(m))
return true;
}
return false;
}
/////////////////////////////////////////////////
void FollowerPlugin::OnUpdate()
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
// Update follower.
this->UpdateFollower();
}
/////////////////////////////////////////////////
void FollowerPlugin::OnNewDepthFrame(const float *_image,
const unsigned int _width, const unsigned int _height,
const unsigned int /*_depth*/, const std::string &/*_format*/)
{
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
float f;
unsigned int depthBufferSize = _width*_height*sizeof(f);
if (_width != this->dataPtr->imageMsg.width() ||
_height != this->dataPtr->imageMsg.height())
{
if (this->dataPtr->depthBuffer != NULL)
delete [] this->dataPtr->depthBuffer;
this->dataPtr->depthBuffer = new float[depthBufferSize];
this->dataPtr->imageMsg.set_width(_width);
this->dataPtr->imageMsg.set_height(_height);
}
memcpy(this->dataPtr->depthBuffer, _image, depthBufferSize);
}
/////////////////////////////////////////////////
void FollowerPlugin::UpdateFollower()
{
if (this->dataPtr->imageMsg.width() == 0u ||
this->dataPtr->imageMsg.height() == 0u)
{
return;
}
double minRange = 0.1;
double maxRange = 5;
// Find closest point.
int mid = this->dataPtr->imageMsg.height() * 0.5;
float minDepth = maxRange + 1;
int idx = -1;
for (unsigned int i = 0; i < this->dataPtr->imageMsg.width(); ++i)
{
float d =
this->dataPtr->depthBuffer[mid * this->dataPtr->imageMsg.width() + i];
if (d > minRange && d < maxRange && d < minDepth)
{
// Update minimum depth.
minDepth = d;
// Store index of pixel with min range.
idx = i;
}
}
// brake if too close
if (idx < 0 || minDepth < 0.4)
{
// Brakes on!
this->dataPtr->leftJoint->SetVelocity(0, 0);
this->dataPtr->rightJoint->SetVelocity(0, 0);
return;
}
// Set turn rate based on idx of min range in the image.
double turn = -(idx / (this->dataPtr->imageMsg.width() / 2.0)) + 1.0;
double vr = -0.1;
double maxTurnRate = 0.1;
double va = turn * maxTurnRate;
this->dataPtr->wheelSpeed[LEFT] =
vr + va * this->dataPtr->wheelSeparation / 2.0;
this->dataPtr->wheelSpeed[RIGHT] =
vr - va * this->dataPtr->wheelSeparation / 2.0;
double leftVelDesired =
(this->dataPtr->wheelSpeed[LEFT] / this->dataPtr->wheelRadius);
double rightVelDesired =
(this->dataPtr->wheelSpeed[RIGHT] / this->dataPtr->wheelRadius);
this->dataPtr->leftJoint->SetVelocity(0, leftVelDesired);
this->dataPtr->rightJoint->SetVelocity(0, rightVelDesired);
}