Nav2 Navigation Stack - humble  humble
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 
23 #include <math.h>
24 #include <assert.h>
25 
26 #include "nav2_amcl/sensors/laser/laser.hpp"
27 
28 namespace nav2_amcl
29 {
30 
31 LikelihoodFieldModelProb::LikelihoodFieldModelProb(
32  double z_hit, double z_rand, double sigma_hit,
33  double max_occ_dist, bool do_beamskip,
34  double beam_skip_distance,
35  double beam_skip_threshold,
36  double beam_skip_error_threshold,
37  size_t max_beams, map_t * map)
38 : Laser(max_beams, map)
39 {
40  z_hit_ = z_hit;
41  z_rand_ = z_rand;
42  sigma_hit_ = sigma_hit;
43  do_beamskip_ = do_beamskip;
44  beam_skip_distance_ = beam_skip_distance;
45  beam_skip_threshold_ = beam_skip_threshold;
46  beam_skip_error_threshold_ = beam_skip_error_threshold;
47  map_update_cspace(map, max_occ_dist);
48 }
49 
50 // Determine the probability for the given pose
51 double
52 LikelihoodFieldModelProb::sensorFunction(LaserData * data, pf_sample_set_t * set)
53 {
54  LikelihoodFieldModelProb * self;
55  int i, j, step;
56  double z, pz;
57  double log_p;
58  double obs_range, obs_bearing;
59  double total_weight;
60  pf_sample_t * sample;
61  pf_vector_t pose;
62  pf_vector_t hit;
63 
64  self = reinterpret_cast<LikelihoodFieldModelProb *>(data->laser);
65 
66  total_weight = 0.0;
67 
68  step = ceil((data->range_count) / static_cast<double>(self->max_beams_));
69 
70  // Step size must be at least 1
71  if (step < 1) {
72  step = 1;
73  }
74 
75  // Pre-compute a couple of things
76  double z_hit_denom = 2 * self->sigma_hit_ * self->sigma_hit_;
77  double z_rand_mult = 1.0 / data->range_max;
78 
79  double max_dist_prob = exp(-(self->map_->max_occ_dist * self->map_->max_occ_dist) / z_hit_denom);
80 
81  // Beam skipping - ignores beams for which a majoirty of particles do not agree with the map
82  // prevents correct particles from getting down weighted because of unexpected obstacles
83  // such as humans
84 
85  bool do_beamskip = self->do_beamskip_;
86  double beam_skip_distance = self->beam_skip_distance_;
87  double beam_skip_threshold = self->beam_skip_threshold_;
88 
89  // we only do beam skipping if the filter has converged
90  if (do_beamskip && !set->converged) {
91  do_beamskip = false;
92  }
93 
94  // we need a count the no of particles for which the beam agreed with the map
95  int * obs_count = new int[self->max_beams_]();
96 
97  // we also need a mask of which observations to integrate (to decide which beams to integrate to
98  // all particles)
99  bool * obs_mask = new bool[self->max_beams_]();
100 
101  int beam_ind = 0;
102 
103  // realloc indicates if we need to reallocate the temp data structure needed to do beamskipping
104  bool realloc = false;
105 
106  if (do_beamskip) {
107  if (self->max_obs_ < self->max_beams_) {
108  realloc = true;
109  }
110 
111  if (self->max_samples_ < set->sample_count) {
112  realloc = true;
113  }
114 
115  if (realloc) {
116  self->reallocTempData(set->sample_count, self->max_beams_);
117  fprintf(stderr, "Reallocing temp weights %d - %d\n", self->max_samples_, self->max_obs_);
118  }
119  }
120 
121  // Compute the sample weights
122  for (j = 0; j < set->sample_count; j++) {
123  sample = set->samples + j;
124  pose = sample->pose;
125 
126  // Take account of the laser pose relative to the robot
127  pose = pf_vector_coord_add(self->laser_pose_, pose);
128 
129  log_p = 0;
130 
131  beam_ind = 0;
132 
133  for (i = 0; i < data->range_count; i += step, beam_ind++) {
134  obs_range = data->ranges[i][0];
135  obs_bearing = data->ranges[i][1];
136 
137  // This model ignores max range readings
138  if (obs_range >= data->range_max) {
139  continue;
140  }
141 
142  // Check for NaN
143  if (obs_range != obs_range) {
144  continue;
145  }
146 
147  pz = 0.0;
148 
149  // Compute the endpoint of the beam
150  hit.v[0] = pose.v[0] + obs_range * cos(pose.v[2] + obs_bearing);
151  hit.v[1] = pose.v[1] + obs_range * sin(pose.v[2] + obs_bearing);
152 
153  // Convert to map grid coords.
154  int mi, mj;
155  mi = MAP_GXWX(self->map_, hit.v[0]);
156  mj = MAP_GYWY(self->map_, hit.v[1]);
157 
158  // Part 1: Get distance from the hit to closest obstacle.
159  // Off-map penalized as max distance
160 
161  if (!MAP_VALID(self->map_, mi, mj)) {
162  pz += self->z_hit_ * max_dist_prob;
163  } else {
164  z = self->map_->cells[MAP_INDEX(self->map_, mi, mj)].occ_dist;
165  if (z < beam_skip_distance) {
166  obs_count[beam_ind] += 1;
167  }
168  pz += self->z_hit_ * exp(-(z * z) / z_hit_denom);
169  }
170 
171  // Gaussian model
172  // NOTE: this should have a normalization of 1/(sqrt(2pi)*sigma)
173 
174  // Part 2: random measurements
175  pz += self->z_rand_ * z_rand_mult;
176 
177  assert(pz <= 1.0);
178  assert(pz >= 0.0);
179 
180  // TODO(?): outlier rejection for short readings
181 
182  if (!do_beamskip) {
183  log_p += log(pz);
184  } else {
185  self->temp_obs_[j][beam_ind] = pz;
186  }
187  }
188  if (!do_beamskip) {
189  sample->weight *= exp(log_p);
190  total_weight += sample->weight;
191  }
192  }
193 
194  if (do_beamskip) {
195  int skipped_beam_count = 0;
196  for (beam_ind = 0; beam_ind < self->max_beams_; beam_ind++) {
197  if ((obs_count[beam_ind] / static_cast<double>(set->sample_count)) > beam_skip_threshold) {
198  obs_mask[beam_ind] = true;
199  } else {
200  obs_mask[beam_ind] = false;
201  skipped_beam_count++;
202  }
203  }
204 
205  // we check if there is at least a critical number of beams that agreed with the map
206  // otherwise it probably indicates that the filter converged to a wrong solution
207  // if that's the case we integrate all the beams and hope the filter might converge to
208  // the right solution
209  bool error = false;
210 
211  if (skipped_beam_count >= (beam_ind * self->beam_skip_error_threshold_)) {
212  fprintf(
213  stderr,
214  "Over %f%% of the observations were not in the map - pf may have converged to wrong pose -"
215  " integrating all observations\n",
216  (100 * self->beam_skip_error_threshold_));
217  error = true;
218  }
219 
220  for (j = 0; j < set->sample_count; j++) {
221  sample = set->samples + j;
222  pose = sample->pose;
223 
224  log_p = 0;
225 
226  for (beam_ind = 0; beam_ind < self->max_beams_; beam_ind++) {
227  if (error || obs_mask[beam_ind]) {
228  log_p += log(self->temp_obs_[j][beam_ind]);
229  }
230  }
231 
232  sample->weight *= exp(log_p);
233 
234  total_weight += sample->weight;
235  }
236  }
237 
238  delete[] obs_count;
239  delete[] obs_mask;
240  return total_weight;
241 }
242 
243 bool
244 LikelihoodFieldModelProb::sensorUpdate(pf_t * pf, LaserData * data)
245 {
246  if (max_beams_ < 2) {
247  return false;
248  }
249  pf_update_sensor(pf, (pf_sensor_model_fn_t) sensorFunction, data);
250 
251  return true;
252 }
253 
254 } // namespace nav2_amcl
Definition: pf.hpp:112
Definition: map.hpp:61