Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
likelihood_field_model_prob.cpp
1 /*
2  * Player - One Hell of a Robot Server
3  * Copyright (C) 2000 Brian Gerkey & Kasper Stoy
4  * gerkey@usc.edu kaspers@robotics.usc.edu
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include <cassert>
23 #include <cmath>
24 
25 #include "nav2_amcl/sensors/laser/laser.hpp"
26 
27 namespace nav2_amcl
28 {
29 
30 LikelihoodFieldModelProb::LikelihoodFieldModelProb(
31  double z_hit, double z_rand, double sigma_hit,
32  double max_occ_dist, bool do_beamskip,
33  double beam_skip_distance,
34  double beam_skip_threshold,
35  double beam_skip_error_threshold,
36  size_t max_beams, map_t * map)
37 : Laser(max_beams, map)
38 {
39  z_hit_ = z_hit;
40  z_rand_ = z_rand;
41  sigma_hit_ = sigma_hit;
42  do_beamskip_ = do_beamskip;
43  beam_skip_distance_ = beam_skip_distance;
44  beam_skip_threshold_ = beam_skip_threshold;
45  beam_skip_error_threshold_ = beam_skip_error_threshold;
46 
47  // recompute cspace only when necessary, i.e. if:
48  // - max_occ_dist changed
49  // OR
50  // - cspace was not computed yet, i.e. when map->max_occ_dist == 0.0 (and hence different from
51  // max_occ_dist)
52  if (map->max_occ_dist != max_occ_dist) {
53  map_update_cspace(map, max_occ_dist);
54  }
55 }
56 
57 // Determine the probability for the given pose
58 double
59 LikelihoodFieldModelProb::sensorFunction(LaserData * data, pf_sample_set_t * set)
60 {
61  LikelihoodFieldModelProb * self;
62  int i, j, step;
63  double z, pz;
64  double log_p;
65  double obs_range, obs_bearing;
66  double total_weight;
67  pf_sample_t * sample;
68  pf_vector_t pose;
69  pf_vector_t hit;
70 
71  self = reinterpret_cast<LikelihoodFieldModelProb *>(data->laser);
72 
73  total_weight = 0.0;
74 
75  step = ceil((data->range_count) / static_cast<double>(self->max_beams_));
76 
77  // Step size must be at least 1
78  if (step < 1) {
79  step = 1;
80  }
81 
82  // Pre-compute a couple of things
83  double z_hit_denom = 2 * self->sigma_hit_ * self->sigma_hit_;
84  double z_rand_mult = 1.0 / data->range_max;
85 
86  double max_dist_prob = exp(-(self->map_->max_occ_dist * self->map_->max_occ_dist) / z_hit_denom);
87 
88  // Beam skipping - ignores beams for which a majoirty of particles do not agree with the map
89  // prevents correct particles from getting down weighted because of unexpected obstacles
90  // such as humans
91 
92  bool do_beamskip = self->do_beamskip_;
93  double beam_skip_distance = self->beam_skip_distance_;
94  double beam_skip_threshold = self->beam_skip_threshold_;
95 
96  // we only do beam skipping if the filter has converged
97  if (do_beamskip && !set->converged) {
98  do_beamskip = false;
99  }
100 
101  // we need a count the no of particles for which the beam agreed with the map
102  int * obs_count = new int[self->max_beams_]();
103 
104  // we also need a mask of which observations to integrate (to decide which beams to integrate to
105  // all particles)
106  bool * obs_mask = new bool[self->max_beams_]();
107 
108  int beam_ind = 0;
109 
110  // realloc indicates if we need to reallocate the temp data structure needed to do beamskipping
111  bool realloc = false;
112 
113  if (do_beamskip) {
114  if (self->max_obs_ < self->max_beams_) {
115  realloc = true;
116  }
117 
118  if (self->max_samples_ < set->sample_count) {
119  realloc = true;
120  }
121 
122  if (realloc) {
123  self->reallocTempData(set->sample_count, self->max_beams_);
124  fprintf(stderr, "Reallocing temp weights %d - %d\n", self->max_samples_, self->max_obs_);
125  }
126  }
127 
128  // Compute the sample weights
129  for (j = 0; j < set->sample_count; j++) {
130  sample = set->samples + j;
131  pose = sample->pose;
132 
133  // Take account of the laser pose relative to the robot
134  pose = pf_vector_coord_add(self->laser_pose_, pose);
135 
136  log_p = 0;
137 
138  beam_ind = 0;
139 
140  for (i = 0; i < data->range_count; i += step, beam_ind++) {
141  obs_range = data->ranges[i][0];
142  obs_bearing = data->ranges[i][1];
143 
144  // This model ignores max range readings
145  if (obs_range >= data->range_max) {
146  continue;
147  }
148 
149  // Check for NaN
150  if (obs_range != obs_range) {
151  continue;
152  }
153 
154  pz = 0.0;
155 
156  // Compute the endpoint of the beam
157  hit.v[0] = pose.v[0] + obs_range * cos(pose.v[2] + obs_bearing);
158  hit.v[1] = pose.v[1] + obs_range * sin(pose.v[2] + obs_bearing);
159 
160  // Convert to map grid coords.
161  int mi, mj;
162  mi = MAP_GXWX(self->map_, hit.v[0]);
163  mj = MAP_GYWY(self->map_, hit.v[1]);
164 
165  // Part 1: Get distance from the hit to closest obstacle.
166  // Off-map penalized as max distance
167 
168  if (!MAP_VALID(self->map_, mi, mj)) {
169  pz += self->z_hit_ * max_dist_prob;
170  } else {
171  z = self->map_->cells[MAP_INDEX(self->map_, mi, mj)].occ_dist;
172  if (z < beam_skip_distance) {
173  obs_count[beam_ind] += 1;
174  }
175  pz += self->z_hit_ * exp(-(z * z) / z_hit_denom);
176  }
177 
178  // Gaussian model
179  // NOTE: this should have a normalization of 1/(sqrt(2pi)*sigma)
180 
181  // Part 2: random measurements
182  pz += self->z_rand_ * z_rand_mult;
183 
184  assert(pz <= 1.0);
185  assert(pz >= 0.0);
186 
187  // TODO(?): outlier rejection for short readings
188 
189  if (!do_beamskip) {
190  log_p += log(pz);
191  } else {
192  self->temp_obs_[j][beam_ind] = pz;
193  }
194  }
195  if (!do_beamskip) {
196  sample->weight *= exp(log_p);
197  total_weight += sample->weight;
198  }
199  }
200 
201  if (do_beamskip) {
202  int skipped_beam_count = 0;
203  for (beam_ind = 0; beam_ind < self->max_beams_; beam_ind++) {
204  if ((obs_count[beam_ind] / static_cast<double>(set->sample_count)) > beam_skip_threshold) {
205  obs_mask[beam_ind] = true;
206  } else {
207  obs_mask[beam_ind] = false;
208  skipped_beam_count++;
209  }
210  }
211 
212  // we check if there is at least a critical number of beams that agreed with the map
213  // otherwise it probably indicates that the filter converged to a wrong solution
214  // if that's the case we integrate all the beams and hope the filter might converge to
215  // the right solution
216  bool error = false;
217 
218  if (skipped_beam_count >= (beam_ind * self->beam_skip_error_threshold_)) {
219  fprintf(
220  stderr,
221  "Over %f%% of the observations were not in the map - pf may have converged to wrong pose -"
222  " integrating all observations\n",
223  (100 * self->beam_skip_error_threshold_));
224  error = true;
225  }
226 
227  for (j = 0; j < set->sample_count; j++) {
228  sample = set->samples + j;
229  pose = sample->pose;
230 
231  log_p = 0;
232 
233  for (beam_ind = 0; beam_ind < self->max_beams_; beam_ind++) {
234  if (error || obs_mask[beam_ind]) {
235  log_p += log(self->temp_obs_[j][beam_ind]);
236  }
237  }
238 
239  sample->weight *= exp(log_p);
240 
241  total_weight += sample->weight;
242  }
243  }
244 
245  delete[] obs_count;
246  delete[] obs_mask;
247  return total_weight;
248 }
249 
250 bool
251 LikelihoodFieldModelProb::sensorUpdate(pf_t * pf, LaserData * data)
252 {
253  if (max_beams_ < 2) {
254  return false;
255  }
256  pf_update_sensor(pf, (pf_sensor_model_fn_t) sensorFunction, data);
257 
258  return true;
259 }
260 
261 } // namespace nav2_amcl
Definition: pf.hpp:112
Definition: map.hpp:62