CPCSensor.cc
Go to the documentation of this file.
1 // Copyright (c) 2016 The UUV Simulator Authors.
2 // All rights reserved.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
17 
18 using namespace uuv_plume_simulator;
19 
22 {
23  ROS_INFO("Particle concentration sensor is starting");
24 
25  this->nodeHandle.reset(new ros::NodeHandle("~"));
26 
27  // Create ROS node
28  if (!ros::isInitialized())
29  {
30  ROS_ERROR("Not loading sensor plugin since ROS has not been properly initialized");
31  throw 1;
32  }
33 
34  // Set seed for the noise generator
35  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
36  this->rndGen = std::default_random_engine(seed);
37 
38  this->nodeHandle->getParam("~gamma", this->gamma);
39  ROS_ASSERT(this->gamma > 0);
40  ROS_INFO_NAMED("CPCSensor", "Gamma: %3f", this->gamma);
41 
42  this->nodeHandle->getParam("~gain", this->gain);
43  ROS_ASSERT(this->gain >= 0);
44  ROS_INFO_NAMED("CPCSensor", "Gain: %3f", this->gain);
45 
46  this->nodeHandle->getParam("~radius", this->smoothingLength);
47  ROS_ASSERT(this->smoothingLength >= 0);
48  ROS_INFO_NAMED("CPCSensor", "Radius [m]: %3f", this->smoothingLength);
49 
50  this->nodeHandle->getParam("~update_rate", this->updateRate);
51  ROS_ASSERT(this->updateRate > 0);
52  ROS_INFO_NAMED("CPCSensor", "Update rate [Hz]: %3f", this->updateRate);
53 
54  this->saturation = 1.0;
55  if (this->nodeHandle->hasParam("~saturation"))
56  this->nodeHandle->getParam("~saturation", this->saturation);
57  ROS_ASSERT(this->saturation >= 0);
58 
59  this->noiseAmp = 0.0;
60  if (this->nodeHandle->hasParam("~noise_amplitude"))
61  this->nodeHandle->getParam("~noise_amplitude", this->noiseAmp);
62  ROS_ASSERT(this->noiseAmp >= 0);
63 
64  this->noiseSigma = 0.0;
65  if (this->nodeHandle->hasParam("~noise_sigma"))
66  this->nodeHandle->getParam("~noise_sigma", this->noiseSigma);
67  ROS_ASSERT(this->noiseSigma >= 0);
68 
69  // Initialize noise model
70  this->noiseModel = std::normal_distribution<double>(0.0, this->noiseSigma);
71 
72  // Read the geodetic reference as WGS84
73  this->useGeoCoordinates = false;
74  if (this->nodeHandle->hasParam("~use_geo_coordinates"))
75  this->nodeHandle->getParam(
76  "~use_geo_coordinates", this->useGeoCoordinates);
77 
78  // Create the coordinate converter
79  if (this->useGeoCoordinates)
80  {
81  GeographicLib::Geocentric earth(
82  GeographicLib::Constants::WGS84_a(),
83  GeographicLib::Constants::WGS84_f());
84 
85  double latitude = -1;
86  double longitude = -1;
87  ROS_ASSERT(this->nodeHandle->hasParam("~latitude"));
88  ROS_ASSERT(this->nodeHandle->hasParam("~longitude"));
89 
90  this->nodeHandle->getParam("~latitude", latitude);
91  this->nodeHandle->getParam("~longitude", longitude);
92 
93  this->projection = GeographicLib::LocalCartesian(
94  latitude, longitude, 0, earth);
95  }
96 
97  // Loading the salinity unit for the salinity output
99  if (this->nodeHandle->hasParam("~salinity_unit"))
100  this->nodeHandle->getParam("~salinity_unit", this->salinityUnit);
101 
103  !this->salinityUnit.compare(CONCENTRATION_UNIT_PPM) ||
104  !this->salinityUnit.compare(CONCENTRATION_UNIT_PSU));
105 
106  // Loading the reference salinity value to be computed from the particle
107  // concentration value
108  if (this->nodeHandle->hasParam("~reference_salinity_value"))
109  this->nodeHandle->getParam("~reference_salinity_value",
110  this->referenceSalinityValue);
111  else
112  {
113  // If no reference is given, the sea water reference is provided
114  if (this->salinityUnit.compare(CONCENTRATION_UNIT_PPT) == 0)
115  this->referenceSalinityValue = 35.0;
116  else if (this->salinityUnit.compare(CONCENTRATION_UNIT_PPM) == 0)
117  this->referenceSalinityValue = 35000.0;
118  else
119  this->referenceSalinityValue = 35.0;
120  }
121 
122  this->updatingCloud = false;
123 
124  // Subscribing to the particles topic (remap the topic name as desired)
125  this->particlesSub = this->nodeHandle->subscribe<sensor_msgs::PointCloud>(
126  "particles", 1, &CPCSensor::OnPlumeParticlesUpdate, this);
127 
128  // Publish the particle concentration value
129  this->concentrationPub = this->nodeHandle->advertise<
130  uuv_plume_msgs::ParticleConcentration>(
131  "concentration", 1);
132 
133  this->useOdom = false;
134  this->useGPS = false;
135  this->useTFUpdate = false;
136  if (this->nodeHandle->hasParam("~use_odom"))
137  this->nodeHandle->getParam("~use_odom", this->useOdom);
138 
139  if (this->nodeHandle->hasParam("~use_gps"))
140  this->nodeHandle->getParam("~use_gps", this->useGPS);
141 
142  if (this->useOdom)
143  {
144  // Subscribe to the odometry topic
145  this->odometrySub = this->nodeHandle->subscribe<nav_msgs::Odometry>(
146  "odom", 1, &CPCSensor::OnOdometryUpdate, this);
147  ROS_INFO("Using the odometry <nav_msgs::Odometry> as input for sensor position");
148  }
149  else if (this->useGPS && this->useGeoCoordinates)
150  {
151  // Subscribe to the GPS topic
152  this->gpsSub = this->nodeHandle->subscribe<sensor_msgs::NavSatFix>(
153  "gps", 1, &CPCSensor::OnGPSUpdate, this);
154  ROS_INFO("Using the GPS <sensor_msgs::NatSatFix> as input for sensor position");
155  }
156  else
157  {
158  this->useTFUpdate = true;
159  this->nodeHandle->getParam("~sensor_frame_id", this->sensorFrameID);
160  ROS_INFO("Using the a frame ID as input for sensor position");
161  }
162 
163  this->areParticlesInit = false;
164  // Initializing the particle concentration message
165  this->concentrationMsg.header.frame_id = "world";
166  this->concentrationMsg.header.stamp = ros::Time::now();
167 
168  this->tfListener.reset(new tf2_ros::TransformListener(this->tfBuffer));
169 
170  // Initialize the salinity topic
171  this->publishSalinity = false;
172  if (this->nodeHandle->hasParam("~publish_salinity"))
173  this->nodeHandle->getParam("~publish_salinity", this->publishSalinity);
174 
175  if (this->publishSalinity)
176  this->salinityPub = this->nodeHandle->advertise<
177  uuv_plume_msgs::Salinity>("salinity", 1);
178 
179  this->updateTimer = this->nodeHandle->createTimer(
180  ros::Duration(1.0 / this->updateRate),
181  boost::bind(&CPCSensor::OnSensorUpdate, this, _1));
182 
183  ROS_INFO("Particle concentration sensor running");
184 }
185 
188 {
189 
190 }
191 
194  const sensor_msgs::PointCloud::ConstPtr &_msg)
195 {
196  if (this->concentrationPub.getNumSubscribers() > 0)
197  {
198  this->updateMeasurement = true;
199  this->areParticlesInit = true;
200 
201  if (this->useTFUpdate)
202  {
203  // Read the current position of the sensor frame
204  geometry_msgs::TransformStamped childTransform;
205  std::string targetFrame = _msg->header.frame_id;
206  std::string sourceFrame = this->sensorFrameID;
207  try
208  {
209  childTransform = this->tfBuffer.lookupTransform(
210  targetFrame, sourceFrame, ros::Time(0));
211  }
212  catch(tf2::TransformException &ex)
213  {
214  ROS_ERROR_NAMED("CPCSensor", "Transform between %s and %s",
215  targetFrame.c_str(), sourceFrame.c_str());
216  return;
217  }
218 
219  this->cartPos.x = childTransform.transform.translation.x;
220  this->cartPos.y = childTransform.transform.translation.y;
221  this->cartPos.z = childTransform.transform.translation.z;
222  }
223 
224  this->concentrationMsg.header.frame_id = _msg->header.frame_id;
225  this->concentrationMsg.header.stamp = ros::Time::now();
226 
227  this->concentrationMsg.position.x = this->cartPos.x;
228  this->concentrationMsg.position.y = this->cartPos.y;
229  this->concentrationMsg.position.z = this->cartPos.z;
230 
231  if (this->useGeoCoordinates)
232  {
233  if (this->useGPS)
234  this->concentrationMsg.geo_point = this->geoPos;
235  else
236  {
237  double latitude, longitude, altitude;
238  this->projection.Reverse(
239  this->cartPos.x, this->cartPos.y, this->cartPos.z,
240  latitude, longitude, altitude);
241  this->concentrationMsg.geo_point.latitude = latitude;
242  this->concentrationMsg.geo_point.longitude = longitude;
243  this->concentrationMsg.geo_point.altitude = altitude;
244  }
245  }
246  else
247  {
248  this->concentrationMsg.geo_point.latitude = 0;
249  this->concentrationMsg.geo_point.longitude = 0;
250  this->concentrationMsg.geo_point.altitude = 0;
251  }
252 
253  double totalParticleConc = 0.0;
254  double smoothingParam;
255  double particleConc;
256  double distToParticle;
257 
258  double currentTime = ros::Time::now().toSec();
259 
260  double initSmoothingLength = std::pow(this->smoothingLength, 2.0 / 3);
261 
262  for (int i = 0; i < _msg->points.size(); i++)
263  {
264  // Compute the distance to the sensor
265  distToParticle = std::sqrt(
266  std::pow(_msg->points[i].x - this->cartPos.x, 2) +
267  std::pow(_msg->points[i].y - this->cartPos.y, 2) +
268  std::pow(_msg->points[i].z - this->cartPos.z, 2));
269 
270  smoothingParam = std::pow(initSmoothingLength +
271  this->gamma * (currentTime - _msg->channels[0].values[i]), 1.5);
272  }
273 
274  // Compute particle concentration
275  if (distToParticle >= 0 && distToParticle < smoothingParam)
276  particleConc = 4.0 -
277  6.0 * std::pow(distToParticle / smoothingParam, 2) +
278  3.0 * std::pow(distToParticle / smoothingParam, 3);
279  else if (distToParticle >= smoothingParam && distToParticle < 2 * smoothingParam)
280  particleConc = std::pow(2 - distToParticle / smoothingParam, 3);
281  else
282  particleConc = 0.0;
283 
284  particleConc *= 1 / (4 * M_PI * std::pow(smoothingParam, 3));
285  totalParticleConc += particleConc;
286 
287  // Applying saturation
288  this->concentrationMsg.concentration = std::min(
289  this->gain * totalParticleConc, this->saturation);
290 
291  if (this->publishSalinity)
292  {
293  this->salinityMsg.header.frame_id = _msg->header.frame_id;
294  this->salinityMsg.header.stamp = ros::Time::now();
295  this->salinityMsg.position = this->concentrationMsg.position;
296  this->salinityMsg.geo_point = this->concentrationMsg.geo_point;
297 
298  // Calculating salinity
299  this->salinityMsg.salinity = this->referenceSalinityValue * \
300  (this->saturation - this->concentrationMsg.concentration) + \
301  this->concentrationMsg.concentration * this->plumeSalinityValue;
302 
303  // Adding noise to the salinity value
304  this->salinityMsg.salinity += this->noiseAmp \
305  * this->noiseModel(this->rndGen);
306  }
307 
308  // Adding noise to concentration value
309  this->concentrationMsg.concentration += this->noiseAmp \
310  * this->noiseModel(this->rndGen);
311  this->updateMeasurement = false;
312  }
313 }
314 
317  const nav_msgs::Odometry::ConstPtr &_msg)
318 {
319  if (this->concentrationPub.getNumSubscribers() > 0)
320  {
321  if (this->updateMeasurement)
322  return;
323  this->cartPos.x = _msg->pose.pose.position.x;
324  this->cartPos.y = _msg->pose.pose.position.y;
325  this->cartPos.z = _msg->pose.pose.position.z;
326  }
327 }
328 
331  const sensor_msgs::NavSatFix::ConstPtr &_msg)
332 {
334  if (this->concentrationPub.getNumSubscribers() > 0)
335  {
336  if (this->updateMeasurement)
337  return;
338  this->geoPos.latitude = _msg->latitude;
339  this->geoPos.longitude = _msg->longitude;
340  this->geoPos.altitude = _msg->altitude;
341  this->projection.Forward(
342  _msg->latitude, _msg->longitude, _msg->altitude,
343  this->cartPos.x, this->cartPos.y, this->cartPos.z);
344  }
345 }
346 
349 {
350  if (!this->areParticlesInit)
351  return;
353  if (this->publishSalinity)
354  this->salinityPub.publish(this->salinityMsg);
355 }
ros::Subscriber odometrySub
Subscriber for odometry topic.
Definition: CPCSensor.hh:136
ros::Subscriber particlesSub
Subscriber for the plume particle point cloud.
Definition: CPCSensor.hh:133
#define ROS_INFO_NAMED(name,...)
double updateRate
Output topic&#39;s update rate.
Definition: CPCSensor.hh:106
void publish(const boost::shared_ptr< M > &message) const
uuv_plume_msgs::ParticleConcentration concentrationMsg
Plume concentration message.
Definition: CPCSensor.hh:160
bool useTFUpdate
Flag set if the TF update wrt the sensor frame ID.
Definition: CPCSensor.hh:124
bool publishSalinity
Flag to activate publishing the simulated salinity.
Definition: CPCSensor.hh:94
~CPCSensor()
Class destructor.
Definition: CPCSensor.cc:187
ROSCPP_DECL bool isInitialized()
bool areParticlesInit
Flag set to true after the first set of plume particles is received.
Definition: CPCSensor.hh:113
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
std::normal_distribution< double > noiseModel
Normal distribution describing the noise model.
Definition: CPCSensor.hh:172
geographic_msgs::GeoPoint geoPos
Measured geodetic position.
Definition: CPCSensor.hh:130
ros::Subscriber gpsSub
Subscriber to the GPS update topic.
Definition: CPCSensor.hh:139
double gamma
Gamma velocity parameter for the smoothing function.
Definition: CPCSensor.hh:71
double gain
Sensor gain.
Definition: CPCSensor.hh:74
bool useGPS
Flag set if the sensor position update must be read from the vehicle&#39;s GPS topic. ...
Definition: CPCSensor.hh:121
void OnSensorUpdate(const ros::TimerEvent &)
Update the output concentration and salinity topics.
Definition: CPCSensor.cc:348
ros::Timer updateTimer
Sensor update timer.
Definition: CPCSensor.hh:166
double smoothingLength
Radius of the kernel to identify particles that will be taken into account in the concentration compu...
Definition: CPCSensor.hh:78
#define ROS_INFO(...)
#define CONCENTRATION_UNIT_PPT
Definition: CPCSensor.hh:40
double noiseSigma
Noise standard deviation.
Definition: CPCSensor.hh:178
ros::Publisher concentrationPub
Output topic for particle concentration.
Definition: CPCSensor.hh:142
double referenceSalinityValue
Default salinity value for the fluid e.g. sea water.
Definition: CPCSensor.hh:97
double saturation
Sensor saturation.
Definition: CPCSensor.hh:87
ros::Publisher salinityPub
Output topic for salinity.
Definition: CPCSensor.hh:145
std::default_random_engine rndGen
Pseudo random number generator.
Definition: CPCSensor.hh:169
GeographicLib::LocalCartesian projection
Local Cartesian projection.
Definition: CPCSensor.hh:157
uuv_plume_msgs::Salinity salinityMsg
Salinity message.
Definition: CPCSensor.hh:163
bool useGeoCoordinates
Flag that will allow storing the geodetic coordinates with the measurement message.
Definition: CPCSensor.hh:91
std::shared_ptr< ros::NodeHandle > nodeHandle
ROS node handle.
Definition: CPCSensor.hh:148
std::string sensorFrameID
Name of the sensor frame.
Definition: CPCSensor.hh:109
void OnGPSUpdate(const sensor_msgs::NavSatFix::ConstPtr &_msg)
Update the GPS update callback.
Definition: CPCSensor.cc:330
bool updateMeasurement
Set to true to avoid particle update.
Definition: CPCSensor.hh:103
tf2_ros::Buffer tfBuffer
TF buffer instance.
Definition: CPCSensor.hh:151
uint32_t getNumSubscribers() const
std::string salinityUnit
Salinity unit to be used. Options are.
Definition: CPCSensor.hh:84
void OnPlumeParticlesUpdate(const sensor_msgs::PointCloud::ConstPtr &_msg)
Update callback from the plume particles.
Definition: CPCSensor.cc:193
std::shared_ptr< tf2_ros::TransformListener > tfListener
TF listener pointer.
Definition: CPCSensor.hh:154
void OnOdometryUpdate(const nav_msgs::Odometry::ConstPtr &_msg)
Update the odometry callback.
Definition: CPCSensor.cc:316
#define ROS_ERROR_NAMED(name,...)
static Time now()
bool updatingCloud
Flag to ensure the cloud and measurement update don&#39;t coincide.
Definition: CPCSensor.hh:68
geometry_msgs::Vector3 cartPos
Measured Cartesian position.
Definition: CPCSensor.hh:127
#define ROS_ASSERT(cond)
bool useOdom
Flag set if the sensor position update must be read from the vehicle&#39;s odometry input topic...
Definition: CPCSensor.hh:117
#define CONCENTRATION_UNIT_PSU
Definition: CPCSensor.hh:42
#define ROS_ERROR(...)
#define CONCENTRATION_UNIT_PPM
Definition: CPCSensor.hh:41
CPCSensor()
Class constructor.
Definition: CPCSensor.cc:21
double noiseAmp
Noise amplitude.
Definition: CPCSensor.hh:175


uuv_cpc_sensor
Author(s): Musa Morena Marcusso Manhaes
autogenerated on Mon Jun 10 2019 15:41:26