laser_simulator.cpp
Go to the documentation of this file.
1 #include "ros/ros.h"
2 
4 
6 {
7  nh_ptr = nh;
8  // get parameters
9  get_params();
10  laser_pub = nh_ptr->advertise<sensor_msgs::LaserScan>(l_scan_topic,10); // scan publisher
11  // get map
12  get_map();
13  ROS_INFO("Initialized laser scanner simulator");
14 }
15 
17 {
18  if (is_running) stop();
19 }
20 
22 {
23  nh_ptr->param<std::string>("laser_topic", l_scan_topic, "scan");
24  nh_ptr->param<std::string>("map_service", map_service, "static_map");
25  //laser parameters - defaults are appriximately that of a Sick S300
26  nh_ptr->param<std::string>("laser_frame_id", l_frame, "base_link");
27  nh_ptr->param<double>("laser_fov", l_fov, 1.5*M_PI);
28  nh_ptr->param<int>("laser_beam_count", l_beams, 541);
29  nh_ptr->param<double>("laser_max_range", l_max_range, 30.0);
30  nh_ptr->param<double>("laser_min_range", l_min_range, 0.05);
31  nh_ptr->param<double>("laser_frequency", l_frequency, 10.0);
32  // noise model parameters (see Probabilistic Robotics eq. 6.12)
33  nh_ptr->param<bool>("apply_noise", use_noise_model, true);
34  nh_ptr->param<double>("sigma_hit", sigma_hit, 0.005);
35  nh_ptr->param<double>("lambda_short", lambda_short, 2.0);
36  nh_ptr->param<double>("z_hit", z_mix[0], 0.995);
37  nh_ptr->param<double>("z_short", z_mix[1], 0.0);
38  nh_ptr->param<double>("z_max", z_mix[2], 0.005);
39  nh_ptr->param<double>("z_rand", z_mix[3], 0.0);
40  // update the noise model internally
42 }
43 
45 {
47  loop_timer.start(); // should not be necessary
48  is_running = true;
49  ROS_INFO("Started laser scanner simulator update loop");
50 }
51 
53 {
54  loop_timer.stop();
55  is_running = false;
56  ROS_INFO("Stopped laser scanner simulator");
57 }
58 
60 {
61  // If we don't have a map, try to get one
62  if (!have_map) get_map();
63  // first, get the pose of the laser in the map frame
64  double l_x, l_y, l_theta;
65  get_laser_pose(&l_x,&l_y,&l_theta);
66  //ROS_INFO_STREAM_THROTTLE(2,"x: " << l_x << " y: " << l_y << " theta: " << l_theta);
67  update_scan(l_x,l_y,l_theta);
68  output_scan.header.stamp = event.current_real;
70 }
71 
73 {
74  nav_msgs::GetMapRequest req;
75  nav_msgs::GetMapResponse resp;
76  if (ros::service::call(map_service, req, resp))
77  {
78  map = resp.map;
79  ROS_INFO_STREAM("Got a " << map.info.width << "x" << map.info.height << " map with resolution " << map.info.resolution);
80  have_map = true;
81  }
82  else
83  {
84  ROS_WARN_THROTTLE(10,"No map received - service '/static_map' not available (will publish only max_range)");
85  have_map = false;
86  }
87 }
88 
89 void LaserScannerSimulator::set_laser_params(std::string frame_id, double fov, unsigned int beam_count, double max_range, double min_range, double update_frequency)
90 {
91  l_frame = frame_id;
92  l_fov = fov;
93  l_beams = beam_count;
94  l_max_range = max_range;
95  l_min_range = min_range;
96  l_frequency = update_frequency;
97  ROS_INFO("Updated parameters of simulated laser");
98 }
99 
100 void LaserScannerSimulator::get_laser_pose(double * x, double * y, double * theta)
101 {
102  ros::Time now = ros::Time::now();
103  tf::StampedTransform transf;
104  try
105  {
106  tl.waitForTransform("/map",l_frame,now,ros::Duration(1.0));
107  tl.lookupTransform("/map",l_frame,now,transf);
108  }
109  catch (tf::TransformException ex)
110  {
111  ROS_ERROR("%s",ex.what());
112  *x = 0.0; *y = 0.0, *theta * 0.0;
113  return;
114  }
115  *x = transf.getOrigin().getX();
116  *y = transf.getOrigin().getY();
117  *theta = tf::getYaw(transf.getRotation());
118 }
119 
120 void LaserScannerSimulator::update_scan(double x, double y, double theta)
121 {
122  //timing
123  //ros::Time start = ros::Time::now();
124  // laser params
125  output_scan.angle_min = -l_fov/2.0;
126  output_scan.angle_max = l_fov/2.0;
127  output_scan.angle_increment = l_fov/l_beams;
128  output_scan.range_min = l_min_range;
129  output_scan.range_max = l_max_range+0.001;
130  output_scan.time_increment = (1.0/l_frequency) / l_beams;
131  output_scan.scan_time = (1.0/l_frequency);
132 
133  std::vector<float> ranges;
134  double this_range;
135  double this_ang;
136  // header
137  output_scan.header.frame_id = l_frame;
138  output_scan.header.stamp = ros::Time::now();
139 
140  for (unsigned int i=0; i<=l_beams; i++)
141  {
142  if (!have_map)
143  {
144  ranges.push_back((float)l_max_range);
145  continue;
146  }
147  this_ang = theta + output_scan.angle_min + i*output_scan.angle_increment;
148  this_range = find_map_range(x,y,this_ang);
149  //ROS_INFO_STREAM_THROTTLE(1,"x: " << x << " y: " << y << " theta: " << this_ang);
150  ranges.push_back(this_range);
151  }
152  output_scan.ranges = ranges;
153  //ROS_INFO_STREAM_THROTTLE(2, "Simulated scan in " << (ros::Time::now()-start).toSec() << "s");
154 }
155 
156 double LaserScannerSimulator::find_map_range(double x, double y, double theta)
157 {
158  // using "A Faster Voxel Traversal Algorithm for Ray Tracing" by Amanatides & Woo
159  // ======== initialization phase ========
160  //ROS_INFO_STREAM_THROTTLE(1,"x,y,theta " << x << ", " << y << ", " << theta);
161  double origin[2]; // u
162  origin[0] = x;
163  origin[1] = y;
164  //ROS_INFO_STREAM_THROTTLE(1,"origin " << origin[0] << ", " << origin[1]);
165  double dir[2]; // v
166  dir[0] = cos(theta);
167  dir[1] = sin(theta);
168  //ROS_INFO_STREAM_THROTTLE(1,"dir " << dir[0] << ", " << dir[1]);
169  int start_x, start_y;
170  get_world2map_coordinates(x,y,&start_x,&start_y);
171  //ROS_INFO_STREAM_THROTTLE(1,"start " << start_x << ", " << start_y);
172  if (start_x<0 || start_y<0 || start_x >= map.info.width || start_y >= map.info.height)
173  {
174  //outside the map, find entry point
175  double delta_x = abs(map.info.origin.position.x - x);
176  double delta_y = abs(map.info.origin.position.y - y);
177  double intersect_x = x + (dir[0] * delta_x);
178  double intersect_y = y + (dir[1] * delta_y);
179  get_world2map_coordinates(intersect_x,intersect_y,&start_x,&start_y);
180  }
181  int current[2]; // X, Y
182  current[0] = start_x;
183  current[1] = start_y;
184  //ROS_INFO_STREAM_THROTTLE(1,"current " << current[0] << ", " << current[1]);
185 
186  int step[2]; // stepX, stepY
187  double tMax[2]; // tMaxX, tMaxY
188  double tDelta[2]; // tDeltaX, tDeltaY
189 
190  double voxel_border[2];
191  get_map2world_coordinates(current[0], current[1], &voxel_border[0], &voxel_border[1]);
192  voxel_border[0] -= 0.5 * map.info.resolution; //this is the lower left voxel corner
193  voxel_border[1] -= 0.5 * map.info.resolution; //this is the lower left voxel corner
194  //ROS_INFO_STREAM_THROTTLE(1,"voxel_border " << voxel_border[0] << ", " << voxel_border[1]);
195 
196  for (unsigned int i=0;i<2;i++) // for each dimension (x,y)
197  {
198  // determine step direction
199  if (dir[i] > 0.0) step[i] = 1;
200  else if (dir[i] < 0.0) step[i] = -1;
201  else step[i] = 0;
202 
203  // determine tMax, tDelta
204  if (step[i] != 0)
205  {
206  // corner point of voxel (in direction of ray)
207  if (step[i] == 1)
208  {
209  voxel_border[i] += (float) (step[i] * map.info.resolution);
210  }
211  // tMax - voxel boundary crossing
212  tMax[i] = (voxel_border[i] - origin[i]) / dir[i];
213  // tDelta - distance along ray so that veritcal/horizontal component equals one voxel
214  tDelta[i] = map.info.resolution / fabs(dir[i]);
215  }
216  else
217  {
218  tMax[i] = std::numeric_limits<double>::max();
219  tDelta[i] = std::numeric_limits<double>::max();
220  }
221 
222  }
223  //ROS_INFO_STREAM_THROTTLE(1,"step " << step[0] << ", " << step[1]);
224  //ROS_INFO_STREAM_THROTTLE(1,"tMax " << tMax[0] << ", " << tMax[1]);
225  //ROS_INFO_STREAM_THROTTLE(1,"tDelta " << tDelta[0] << ", " << tDelta[1]);
226 
227  //ROS_DEBUG_STREAM("Starting at index " << start_x << "," << start_y);
228 
229  // ======== incremental traversal ========
230  while (true)
231  {
232  // advance
233  unsigned int dim; // X or Y direction
234  if (tMax[0] < tMax[1]) dim = 0;
235  else dim = 1;
236  // advance one voxel
237  current[dim] += step[dim];
238  tMax[dim] += tDelta[dim];
239  //ROS_DEBUG_STREAM("Examining index " << current[0] << "," << current[1]);
240  // are we outside the map?
241  if (current[0] < 0 || current[0] >= map.info.width || current[1] < 0 || current[1] >= map.info.height)
242  {
243  return l_max_range;
244  }
245  // determine current range
246  double current_range = sqrt(pow((current[0] - start_x),2) + pow((current[1] - start_y),2)) * map.info.resolution;
247  // are we at max range?
248  if (current_range > l_max_range) return l_max_range;
249  else {
250  int occ = get_map_occupancy(current[0],current[1]);
251  if (occ >= 60) // current cell is occupied
252  {
253  // are we below the minimum range of the laser scanner?
254  if (current_range < l_min_range) continue;
255  // if not, return the current range, with noise (optionally) applied
256  if (use_noise_model) return apply_range_noise(current_range);
257  else return current_range;
258  }
259  }
260  } // end while
261 }
262 
263 double LaserScannerSimulator::apply_range_noise(double range_reading)
264 {
265  // using Measurement model of laser range scanner, following method from chapter 6.3.1 of Probabilistic Robotics
266  double noise_modifier = selector(rand_gen);
267  // hit: gaussian noise on reading
268  if (noise_modifier < z_mix[0]) return range_reading + p_hit(rand_gen);
269  // short: short readings, exponential function
270  else if (noise_modifier < z_mix[0] + z_mix[1]) return p_short(rand_gen);
271  // rand: random reading, uniform distribution
272  else if (noise_modifier < z_mix[0] + z_mix[1] + z_mix[3]) return p_rand(rand_gen);
273  // max: no detection, max reading
274  else return l_max_range;
275 }
276 
277 void LaserScannerSimulator::set_noise_params(bool use_model, double sigma_hit_reading, double lambda_short_reading, double z_hit, double z_short, double z_max, double z_rand)
278 {
279  use_noise_model = use_model;
280  sigma_hit = sigma_hit_reading;
281  lambda_short = lambda_short_reading;
282  z_mix[0] = z_hit;
283  z_mix[1] = z_short;
284  z_mix[2] = z_max;
285  z_mix[3] = z_rand;
286  // check that weights are normalized
287  double z_sum = z_mix[0]+z_mix[1]+z_mix[2]+z_mix[3];
288  if (z_sum != 1)
289  {
290  ROS_WARN_STREAM("Noise model weighting sums not normalized (sum is " << z_sum << "), normalizing them");
291  z_mix[0] = z_mix[0]/z_sum;
292  z_mix[1] = z_mix[1]/z_sum;
293  z_mix[2] = z_mix[2]/z_sum;
294  z_mix[3] = z_mix[3]/z_sum;
295  ROS_WARN_STREAM("After normalization - z_hit " << z_mix[0] << ", z_short " << z_mix[1] << ", z_max " << z_mix[2] << ", z_rand " << z_mix[3]);
296 
297  }
298  // reset distributions
299  p_hit = std::normal_distribution<double>(0.0,sigma_hit);
300  p_short = std::exponential_distribution<double>(lambda_short);
301  p_rand = std::uniform_real_distribution<double>(0.0,l_max_range);
302  selector = std::uniform_real_distribution<double>(0.0,1.0);
303 }
304 
305 void LaserScannerSimulator::get_world2map_coordinates(double world_x, double world_y, int * map_x, int * map_y)
306 {
307  *map_x = (int) (std::floor((world_x - map.info.origin.position.x) / map.info.resolution));
308  *map_y = (int) (std::floor((world_y - map.info.origin.position.y) / map.info.resolution));
309  //ROS_INFO_STREAM_THROTTLE(1, "world2map - x: " << world_x << " map_x: " << *map_x);
310 }
311 
312 void LaserScannerSimulator::get_map2world_coordinates(int map_x, int map_y, double * world_x, double * world_y)
313 {
314  *world_x = (map_x * map.info.resolution) + map.info.origin.position.x + 0.5*map.info.resolution;
315  *world_y = (map_y * map.info.resolution) + map.info.origin.position.y + 0.5*map.info.resolution;
316  //ROS_INFO_STREAM_THROTTLE(1, "map2world - map_x: " << map_x << " x: " << *world_x);
317 }
318 
320 {
321  //ROS_DEBUG_STREAM("x: " << x << " y: " << y << " index: " << y*map.info.width + x);
322  return map.data[y*map.info.width + x];
323 }
324 
tf::Transform::getRotation
Quaternion getRotation() const
LaserScannerSimulator::apply_range_noise
double apply_range_noise(double range_reading)
Definition: laser_simulator.cpp:263
LaserScannerSimulator::l_scan_topic
std::string l_scan_topic
Definition: laser_simulator.h:81
ros::service::call
bool call(const std::string &service_name, MReq &req, MRes &res)
LaserScannerSimulator::use_noise_model
bool use_noise_model
Definition: laser_simulator.h:93
LaserScannerSimulator::get_params
void get_params()
Definition: laser_simulator.cpp:21
LaserScannerSimulator::get_map
void get_map()
Definition: laser_simulator.cpp:72
LaserScannerSimulator::laser_pub
ros::Publisher laser_pub
Definition: laser_simulator.h:70
ROS_WARN_THROTTLE
#define ROS_WARN_THROTTLE(period,...)
ros.h
LaserScannerSimulator::start
void start()
Definition: laser_simulator.cpp:44
LaserScannerSimulator::lambda_short
double lambda_short
Definition: laser_simulator.h:95
ros::Timer::stop
void stop()
LaserScannerSimulator::set_noise_params
void set_noise_params(bool use_model, double sigma_hit_reading, double lambda_short_reading, double z_hit, double z_short, double z_max, double z_rand)
Definition: laser_simulator.cpp:277
LaserScannerSimulator::update_loop
void update_loop(const ros::TimerEvent &event)
Definition: laser_simulator.cpp:59
LaserScannerSimulator::update_scan
void update_scan(double x, double y, double theta)
Definition: laser_simulator.cpp:120
LaserScannerSimulator::have_map
bool have_map
Definition: laser_simulator.h:79
step
unsigned int step
LaserScannerSimulator::sigma_hit
double sigma_hit
Definition: laser_simulator.h:94
LaserScannerSimulator::get_map_occupancy
int get_map_occupancy(int x, int y)
Definition: laser_simulator.cpp:319
tf::StampedTransform
LaserScannerSimulator::find_map_range
double find_map_range(double x, double y, double theta)
Definition: laser_simulator.cpp:156
LaserScannerSimulator::nh_ptr
ros::NodeHandle * nh_ptr
Definition: laser_simulator.h:69
LaserScannerSimulator::output_scan
sensor_msgs::LaserScan output_scan
Definition: laser_simulator.h:105
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
LaserScannerSimulator::stop
void stop()
Definition: laser_simulator.cpp:52
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
LaserScannerSimulator::l_min_range
double l_min_range
Definition: laser_simulator.h:88
LaserScannerSimulator::l_beams
int l_beams
Definition: laser_simulator.h:86
tf::getYaw
static double getYaw(const geometry_msgs::Quaternion &msg_q)
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
LaserScannerSimulator::l_frequency
double l_frequency
Definition: laser_simulator.h:89
LaserScannerSimulator::loop_timer
ros::Timer loop_timer
Definition: laser_simulator.h:73
LaserScannerSimulator::p_short
std::exponential_distribution< double > p_short
Definition: laser_simulator.h:101
LaserScannerSimulator::get_laser_pose
void get_laser_pose(double *x, double *y, double *theta)
Definition: laser_simulator.cpp:100
LaserScannerSimulator::l_frame
std::string l_frame
Definition: laser_simulator.h:84
LaserScannerSimulator::map
nav_msgs::OccupancyGrid map
Definition: laser_simulator.h:78
LaserScannerSimulator::l_fov
double l_fov
Definition: laser_simulator.h:85
tf::Transformer::waitForTransform
bool waitForTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
ros::TimerEvent
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
LaserScannerSimulator::is_running
bool is_running
Definition: laser_simulator.h:74
LaserScannerSimulator::~LaserScannerSimulator
~LaserScannerSimulator()
Definition: laser_simulator.cpp:16
LaserScannerSimulator::p_hit
std::normal_distribution< double > p_hit
Definition: laser_simulator.h:100
LaserScannerSimulator::set_laser_params
void set_laser_params(std::string frame_id, double fov, unsigned int beam_count, double max_range, double min_range, double l_frequency)
Definition: laser_simulator.cpp:89
LaserScannerSimulator::z_mix
double z_mix[4]
Definition: laser_simulator.h:96
laser_simulator.h
ros::Time
LaserScannerSimulator::tl
tf::TransformListener tl
Definition: laser_simulator.h:71
LaserScannerSimulator::selector
std::uniform_real_distribution< double > selector
Definition: laser_simulator.h:99
ROS_ERROR
#define ROS_ERROR(...)
ros::NodeHandle::param
T param(const std::string &param_name, const T &default_val) const
LaserScannerSimulator::get_map2world_coordinates
void get_map2world_coordinates(int map_x, int map_y, double *x, double *y)
Definition: laser_simulator.cpp:312
LaserScannerSimulator::map_service
std::string map_service
Definition: laser_simulator.h:77
tf::Transformer::lookupTransform
void lookupTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, StampedTransform &transform) const
LaserScannerSimulator::rand_gen
std::default_random_engine rand_gen
Definition: laser_simulator.h:98
ros::Timer::start
void start()
LaserScannerSimulator::get_world2map_coordinates
void get_world2map_coordinates(double x, double y, int *map_x, int *map_y)
Definition: laser_simulator.cpp:305
tf2::TransformException
ROS_INFO
#define ROS_INFO(...)
ros::NodeHandle::createTimer
Timer createTimer(Duration period, const TimerCallback &callback, bool oneshot=false, bool autostart=true) const
ros::Duration
LaserScannerSimulator::LaserScannerSimulator
LaserScannerSimulator(ros::NodeHandle *nh)
Definition: laser_simulator.cpp:5
LaserScannerSimulator::p_rand
std::uniform_real_distribution< double > p_rand
Definition: laser_simulator.h:102
tf::Transform::getOrigin
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
ros::NodeHandle
LaserScannerSimulator::l_max_range
double l_max_range
Definition: laser_simulator.h:87
ros::Time::now
static Time now()


mobile_robot_simulator
Author(s): Mikkel Rath Pedersen
autogenerated on Thu Jun 2 2022 02:16:58