acoustic_pinger_plugin.cc
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Open Source Robotics Foundation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Open Source Robotics Foundation
18  * nor the names of its contributors may be
19  * used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
46 #include <usv_msgs/RangeBearing.h>
47 #include <cmath>
48 #include <ignition/math/Pose3.hh>
50 
51 using namespace gazebo;
52 
53 // Register this plugin with the simulator
55 
58 {
59 }
60 
63 {
64 }
65 
67 void AcousticPinger::Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
68 {
69  // Store pointer to model for later use.
70  this->model = _parent;
71 
72  // From gazebo_ros_color plugin.
73  GZ_ASSERT(_parent != nullptr, "Received NULL model pointer");
74 
75  // Make sure the ROS node for Gazebo has already been initialised.
76  if (!ros::isInitialized())
77  {
78  ROS_FATAL_STREAM_NAMED("usv_gazebo_acoustic_pinger_plugin", "A ROS node for"
79  " Gazebo hasn't been initialised, unable to load plugin. Load the Gazebo "
80  "system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
81  return;
82  }
83 
84  // From gazebo_ros_color plugin
85  // Load namespace from SDF if available. Otherwise, use the model name.
86  std::string modelName = _parent->GetName();
87  auto delim = modelName.find(":");
88  if (delim != std::string::npos)
89  modelName = modelName.substr(0, delim);
90 
91  // Initialise the namespace.
92  std::string ns = modelName;
93  if (_sdf->HasElement("robotNamespace"))
94  ns = _sdf->GetElement("robotNamespace")->Get<std::string>();
95  else
96  {
97  ROS_DEBUG_NAMED("usv_gazebo_acoustic_pinger_plugin",
98  "missing <robotNamespace>, defaulting to %s", ns.c_str());
99  }
100 
101  // Set the frame_id. Defaults to "pinger".
102  this->frameId = "pinger";
103  if (_sdf->HasElement("frameId"))
104  this->frameId = _sdf->GetElement("frameId")->Get<std::string>();
105 
106  // Load topic from SDF if available.
107  std::string topicName = "/pinger/range_bearing";
108  if (_sdf->HasElement("topicName"))
109  topicName = _sdf->GetElement("topicName")->Get<std::string>();
110  else
111  {
112  ROS_INFO_NAMED("usv_gazebo_acoustic_pinger_plugin",
113  "missing <topicName>, defaulting to %s", topicName.c_str());
114  }
115 
116  // Set the topic to be used to publish the sensor message.
117  std::string setPositionTopicName = "/pinger/set_pinger_position";
118  if (_sdf->HasElement("setPositionTopicName"))
119  {
120  setPositionTopicName =
121  _sdf->GetElement("setPositionTopicName")->Get<std::string>();
122  }
123  else
124  {
125  ROS_INFO_NAMED("usv_gazebo_acoustic_pinger_plugin",
126  "missing <setPositionTopicName>, defaulting to %s", topicName.c_str());
127  }
128 
129  // Initialise pinger position. Defaults to origin.
130  this->position = ignition::math::Vector3d::Zero;
131  if (_sdf->HasElement("position"))
132  this->position = _sdf->Get<ignition::math::Vector3d>("position");
133 
134  // Initialise update rate. Default to 1 reading per second.
135  this->updateRate = 1.0f;
136  if (_sdf->HasElement("updateRate"))
137  this->updateRate = _sdf->Get<float>("updateRate");
138 
139  // From Brian Bingham's rangebearing_gazebo_plugin.
140  // Noise setup and parse SDF.
141  if (_sdf->HasElement("rangeNoise"))
142  {
143  sdf::ElementPtr rangeNoiseElem = _sdf->GetElement("rangeNoise");
144  // Note - it is hardcoded into the NoiseFactory.cc that the SDF
145  // element be "noise".
146  if (rangeNoiseElem->HasElement("noise"))
147  {
148  this->rangeNoise = sensors::NoiseFactory::NewNoiseModel(
149  rangeNoiseElem->GetElement("noise"));
150  }
151  else
152  {
153  ROS_WARN("usv_gazebo_acoustic_pinger_plugin: "
154  "The rangeNoise SDF element must contain noise tag");
155  }
156  }
157  else
158  {
159  ROS_INFO("usv_gazebo_acoustic_pinger_plugin: "
160  "No rangeNoise tag found, no noise added to measurements");
161  }
162 
163  // Load the noise model from the SDF file.
164  if (_sdf->HasElement("bearingNoise"))
165  {
166  sdf::ElementPtr bearingNoiseElem = _sdf->GetElement("bearingNoise");
167  // Note - it is hardcoded into the NoiseFactory.cc that the SDF
168  // element be "noise".
169  if (bearingNoiseElem->HasElement("noise"))
170  {
171  this->bearingNoise = sensors::NoiseFactory::NewNoiseModel(
172  bearingNoiseElem->GetElement("noise"));
173  }
174  else
175  {
176  ROS_WARN("usv_gazebo_acoustic_pinger_plugin: "
177  "The bearingNoise SDF element must contain noise tag");
178  }
179  }
180  else
181  {
182  ROS_INFO("usv_gazebo_acoustic_pinger_plugin: "
183  "No bearingNoise tag found, no noise added to measurements");
184  }
185 
186  if (_sdf->HasElement("elevationNoise"))
187  {
188  sdf::ElementPtr elevationNoiseElem = _sdf->GetElement("elevationNoise");
189  // Note - it is hardcoded into the NoiseFactory.cc that the SDF
190  // element be "noise".
191  if (elevationNoiseElem->HasElement("noise"))
192  {
193  this->elevationNoise = sensors::NoiseFactory::NewNoiseModel(
194  elevationNoiseElem->GetElement("noise"));
195  }
196  else
197  {
198  ROS_WARN("usv_gazebo_acoustic_pinger_plugin: "
199  "The elevationNoise SDF element must contain noise tag");
200  }
201  }
202  else
203  {
204  ROS_INFO("usv_gazebo_acoustic_pinger_plugin: "
205  "No elevationNoise tag found, no noise added to measurements");
206  }
207 
208  // initialise the ros handle.
209  this->rosNodeHandle.reset(new ros::NodeHandle(ns));
210 
211  // setup the publisher.
212  this->rangeBearingPub =
213  this->rosNodeHandle->advertise<usv_msgs::RangeBearing>(
214  std::string(topicName), 1);
215 
216  this->setPositionSub = this->rosNodeHandle->subscribe(
217  setPositionTopicName, 1, &AcousticPinger::PingerPositionCallback, this);
218 
219  // Initialise the time with world time.
220 #if GAZEBO_MAJOR_VERSION >= 8
221  this->lastUpdateTime = this->model->GetWorld()->SimTime();
222 #else
223  this->lastUpdateTime = this->model->GetWorld()->GetSimTime();
224 #endif
225 
226  // connect the update function to the world update event.
227  this->updateConnection = event::Events::ConnectWorldUpdateBegin(
228  std::bind(&AcousticPinger::Update, this));
229 }
230 
233 {
234  // Test to see if it's time to generate a sensor reading.
235 #if GAZEBO_MAJOR_VERSION >= 8
236  if ((this->model->GetWorld()->SimTime() - this->lastUpdateTime) >
237  (1.0f / this->updateRate))
238 #else
239  if ((this->model->GetWorld()->GetSimTime() - this->lastUpdateTime) >
240  (1.0f / this->updateRate))
241 #endif
242  {
243  // lock the thread to protect this->position vector.
244  std::lock_guard<std::mutex> lock(this->mutex);
245 
246 #if GAZEBO_MAJOR_VERSION >= 8
247  this->lastUpdateTime = this->model->GetWorld()->SimTime();
248  // Find the pose of the model.
249  ignition::math::Pose3d modelPose = this->model->WorldPose();
250 #else
251  this->lastUpdateTime = this->model->GetWorld()->GetSimTime();
252  // Find the pose of the model.
253  ignition::math::Pose3d modelPose = this->model->GetWorldPose().Ign();
254 #endif
255 
256  // Direction vector to the pinger from the USV.
257  ignition::math::Vector3d direction = this->position - modelPose.Pos();
258 
259  // Sensor reading is in the sensor frame. Rotate the direction vector into
260  // the frame of reference of the sensor.
261  ignition::math::Vector3d directionSensorFrame =
262  modelPose.Rot().RotateVectorReverse(direction);
263 
264  // Generate a 2d vector for elevation calculation.
265  ignition::math::Vector3d directionSensorFrame2d =
266  ignition::math::Vector3d(
267  directionSensorFrame.X(), directionSensorFrame.Y(), 0);
268 
269  // bearing is calculated by finding the world frame direction vector
270  // and transforming into the pose of the sensor. Bearing is calculated
271  // using the atan2 function of the x and y components of the transformed
272  // vector. The elevation is calculated from the length of the 2D only
273  // and the z component of the sensor frame vector.
274  double bearing = atan2(directionSensorFrame.Y(), directionSensorFrame.X());
275  double range = directionSensorFrame.Length();
276  double elevation =
277  atan2(directionSensorFrame.Z(), directionSensorFrame2d.Length());
278 
279  // Apply noise to each measurement.
280  // From Brian Binghams rangebearing_gazebo_plugin.
281  if (this->rangeNoise != nullptr)
282  range = this->rangeNoise->Apply(range);
283  if (this->bearingNoise != nullptr)
284  bearing = this->bearingNoise->Apply(bearing);
285  if (this->elevationNoise != nullptr)
286  elevation = this->elevationNoise->Apply(elevation);
287 
288  // Publish a ROS message.
289  usv_msgs::RangeBearing msg;
290  // generate ROS header. Sequence number is automatically populated.
291  msg.header.stamp = ros::Time(this->lastUpdateTime.sec,
292  this->lastUpdateTime.nsec);
293  // frame_id is neccesary for finding the tf transform. The frame_id is
294  // specified in the sdf file.
295  msg.header.frame_id = this->frameId;
296  // Fill out the members of the message.
297  msg.range = range;
298  msg.bearing = bearing;
299  msg.elevation = elevation;
300 
301  // publish range and bearing message.
302  this->rangeBearingPub.publish(msg);
303  }
304 }
305 
308  const geometry_msgs::Vector3ConstPtr &_pos)
309 {
310  // Mutex added to prevent simulataneous reads and writes of mutex.
311  std::lock_guard<std::mutex> lock(this->mutex);
312  this->position = ignition::math::Vector3d(_pos->x, _pos->y, _pos->z);
313 }
physics::ModelPtr model
Pointer to model object.
ros::Subscriber setPositionSub
Subscribes to the topic that set the pinger position.
float updateRate
Sensor update rate.
#define ROS_INFO_NAMED(name,...)
event::ConnectionPtr updateConnection
Pointer used to connect gazebo callback to plugins update function.
void publish(const boost::shared_ptr< M > &message) const
f
virtual void Update()
Callback used by gazebo to update the plugin.
GZ_REGISTER_MODEL_PLUGIN(AcousticPinger)
ROSCPP_DECL bool isInitialized()
#define ROS_WARN(...)
gazebo::sensors::NoisePtr rangeNoise
rangeNoise - Gazebo noise object for range.
gazebo::sensors::NoisePtr elevationNoise
Gazebo noise object for elevation angle.
std::mutex mutex
Mutex to protect the position vector.
#define ROS_DEBUG_NAMED(name,...)
common::Time lastUpdateTime
Variable used to track time of last update. This is used to produce data at the correct rate...
virtual ~AcousticPinger()
Destructor.
#define ROS_FATAL_STREAM_NAMED(name, args)
Implements a simulated range and bearing pinger localisation system.
#define ROS_INFO(...)
ros::Publisher rangeBearingPub
Publisher used to send sensor messages generated by the plugin.
void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
std::string frameId
String holding the frame id of the sensor.
ignition::math::Vector3d position
Vector storing the position of the pinger.
std::unique_ptr< ros::NodeHandle > rosNodeHandle
Nodehandle used to integrate with the ROS system.
gazebo::sensors::NoisePtr bearingNoise
Gazebo noise object for bearing angle.
void PingerPositionCallback(const geometry_msgs::Vector3ConstPtr &_pos)
Callback function called when receiving a new pinger position via the pinger subscription callback...


usv_gazebo_plugins
Author(s): Brian Bingham , Carlos Aguero
autogenerated on Thu May 7 2020 03:54:46