pfuclt_particles.cpp
Go to the documentation of this file.
2 #include <boost/foreach.hpp>
3 #include <angles/angles.h>
4 
5 //#define RECONFIGURE_ALPHAS true
6 
7 namespace pfuclt_omni_dataset
8 {
9 
11  : dynamicVariables_(data.nh, data.nRobots),
12  nh_(data.nh), nParticles_((uint)dynamicVariables_.nParticles), mainRobotID_(data.mainRobotID - 1),
13  nTargets_(data.nTargets), nStatesPerRobot_(data.statesPerRobot), nRobots_(data.nRobots),
14  nSubParticleSets_(data.nTargets * STATES_PER_TARGET + data.nRobots * data.statesPerRobot + 1),
15  nLandmarks_(data.nLandmarks),
16  particles_(nSubParticleSets_, subparticles_t(nParticles_)),
17  weightComponents_(data.nRobots, subparticles_t(nParticles_, 0.0)),
18  seed_(time(0)), initialized_(false),
19  landmarksMap_(data.landmarksMap),
20  robotsUsed_(data.robotsUsed),
21  bufLandmarkObservations_(data.nRobots, std::vector<LandmarkObservation>(data.nLandmarks)),
22  bufTargetObservations_(data.nRobots),
23  durationSum(ros::WallDuration(0)),
24  numberIterations(0),
25  state_(data.statesPerRobot, data.nRobots),
26  iteration_oss(new std::ostringstream("")),
27  O_TARGET(data.nRobots * data.statesPerRobot),
28  O_WEIGHT(nSubParticleSets_ - 1)
29 {
30  ROS_INFO("Created particle filter with dimensions %d, %d",
31  (int)particles_.size(), (int)particles_[0].size());
32 
33  // Bind dynamic reconfigure callback
34  dynamic_reconfigure::Server<DynamicConfig>::CallbackType
35  callback;
36  callback = boost::bind(&ParticleFilter::dynamicReconfigureCallback, this, _1);
37  dynamicServer_.setCallback(callback);
38 }
39 
40 void ParticleFilter::dynamicReconfigureCallback(DynamicConfig& config)
41 {
42  // Skip first callback which is done automatically for some reason
44  {
46  return;
47  }
48 
49  if (!initialized_)
50  return;
51 
52  ROS_INFO("Dynamic Reconfigure Callback:\n\tparticles = "
53  "%d\n\tresampling_percentage_to_keep = "
54  "%f\n\tpredict_model_stddev = "
55  "%f\n\tOMNI1_alpha=%s\n\tOMNI3_alpha=%s\n\tOMNI4_alpha=%s\n\tOMNI5_"
56  "alpha=%s",
57  config.particles,
58  config.groups.resampling.percentage_to_keep,
59  config.groups.target.predict_model_stddev,
60  config.groups.alphas.OMNI1_alpha.c_str(),
61  config.groups.alphas.OMNI3_alpha.c_str(),
62  config.groups.alphas.OMNI4_alpha.c_str(),
63  config.groups.alphas.OMNI5_alpha.c_str());
64 
65  // Resize particles and re-initialize the pf if value changed
66  if (dynamicVariables_.nParticles != config.particles)
67  {
68  ROS_INFO("Resizing particles to %d and re-initializing the pf",
69  config.particles);
70 
71  resize_particles(config.particles);
72  nParticles_ = config.particles;
73  }
74 
75  // Update with desired values
76  dynamicVariables_.nParticles = config.particles;
78  config.groups.resampling.percentage_to_keep;
80  config.groups.target.predict_model_stddev;
81 
82 // Alpha values updated only if using the original dataset
83 #ifdef RECONFIGURE_ALPHAS
84  dynamicVariables_.fill_alpha(0, config.groups.alphas.OMNI1_alpha);
85  dynamicVariables_.fill_alpha(2, config.groups.alphas.OMNI3_alpha);
86  dynamicVariables_.fill_alpha(3, config.groups.alphas.OMNI4_alpha);
87  dynamicVariables_.fill_alpha(4, config.groups.alphas.OMNI5_alpha);
88 #endif
89 }
90 
92  pdata_t center[3],
93  float radius)
94 {
95  uint particlesToSpread = nParticles_ * particlesRatio;
96 
97  boost::random::uniform_real_distribution<> dist(-radius, radius);
98 
99  for (uint p = 0; p < particlesToSpread; ++p)
100  {
101  for (uint s = 0; s < STATES_PER_TARGET; ++s)
102  particles_[O_TARGET + s][p] = center[s] + dist(seed_);
103  }
104 }
105 
107 {
108  *iteration_oss << "predictTarget() -> ";
109 
110  using namespace boost::random;
111 
112  // Random acceleration model
113  normal_distribution<> targetAcceleration(TARGET_RAND_MEAN,
115 
116  for (uint p = 0; p < nParticles_; p++)
117  {
118  // Get random accelerations
119  pdata_t accel[STATES_PER_TARGET] = { (pdata_t)targetAcceleration(seed_),
120  (pdata_t)targetAcceleration(seed_),
121  (pdata_t)targetAcceleration(seed_) };
122 
123  // Use random acceleration model
124  for (uint s = 0; s < STATES_PER_TARGET - 1; ++s)
125  {
126  particles_[O_TARGET + s][p] += 0.5 * accel[s] * pow(targetIterationTime_.diff, 2);
127  }
128  }
129 }
130 
132 {
133  *iteration_oss << "fuseRobots() -> ";
134 
135  // Save the latest observation time to be used when publishing
137 
138  // Keeps track of number of landmarks seen for each robot
139  std::vector<uint> landmarksSeen(nRobots_, 0);
140 
141  // Will track the probability propagation based on the landmark observations
142  // for each robot
143  std::vector<subparticles_t> probabilities(nRobots_,
145 
146  // For every robot
147  for (uint r = 0; r < nRobots_; ++r)
148  {
149  // If not used, skip
150  if (false == robotsUsed_[r])
151  continue;
152 
153  // Index offset for this robot in the particles vector
154  uint o_robot = r * nStatesPerRobot_;
155 
156  // For every landmark
157  for (uint l = 0; l < nLandmarks_; ++l)
158  {
159  // If landmark not seen, skip
160  if (false == bufLandmarkObservations_[r][l].found)
161  continue;
162  else
163  ++(landmarksSeen[r]);
164 
165  // Reference to the observation for easier access
167 
168  // Observation in robot frame
169  Eigen::Matrix<pdata_t, 2, 1> Zrobot(m.x, m.y);
170 
171  // Landmark in global frame
172  Eigen::Matrix<pdata_t, 2, 1> LMglobal(landmarksMap_[l].x,
173  landmarksMap_[l].y);
174 
175 #pragma omp parallel for
176  for (uint p = 0; p < nParticles_; ++p)
177  {
178 
179  // Robot pose <=> frame
180  Eigen::Rotation2D<pdata_t> Rrobot(-particles_[o_robot + O_THETA][p]);
181  Eigen::Matrix<pdata_t, 2, 1> Srobot(particles_[o_robot + O_X][p],
182  particles_[o_robot + O_Y][p]);
183 
184  // Landmark to robot frame
185  Eigen::Matrix<pdata_t, 2, 1> LMrobot = Rrobot * (LMglobal - Srobot);
186 
187  // Error in observation
188  Eigen::Matrix<pdata_t, 2, 1> Zerr = LMrobot - Zrobot;
189 
190  // The values of interest to the particle weights
191  // Note: using Eigen wasn't of particular interest here since it does
192  // not allow for transposing a non-dynamic matrix
193  float expArg = -0.5 * (Zerr(O_X) * Zerr(O_X) / m.covXX +
194  Zerr(O_Y) * Zerr(O_Y) / m.covYY);
195  float detValue = 1.0; // pow((2 * M_PI * m.covXX * m.covYY), -0.5);
196 
197  /*
198  ROS_DEBUG_COND(
199  p == 0,
200  "OMNI%d's particle 0 is at {%f;%f;%f}, sees landmark %d with "
201  "certainty %f%%, and error {%f;%f}",
202  r + 1, particles_[o_robot + O_X][p], particles_[o_robot + O_Y][p],
203  particles_[o_robot + O_THETA][p], l, 100 * (detValue * exp(expArg)),
204  Zerr(0), Zerr(1));
205  */
206 
207  // Update weight component for this robot and particular particle
208  probabilities[r][p] *= detValue * exp(expArg);
209  }
210  }
211  }
212 
213  // Reset weights, later will be multiplied by weightComponents of each robot
214  resetWeights(1.0);
215 
216  // Duplicate particles
217  particles_t dupParticles(particles_);
218 
219  for (uint r = 0; r < nRobots_; ++r)
220  {
221  // Again, if robot not used, skip
222  if (false == robotsUsed_[r])
223  continue;
224 
225  // Check that at least one landmark was seen, if not send warning
226  // If seen use probabilities vector, if not keep using the previous
227  // weightComponents for this robot
228  if (0 == landmarksSeen[r])
229  {
230  ROS_WARN("In this iteration, OMNI%d didn't see any landmarks", r + 1);
231 
232  // weightComponent stays from previous iteration
233  }
234 
235  else
236  {
237  weightComponents_[r] = probabilities[r];
238  }
239 
240  // Index offset for this robot in the particles vector
241  uint o_robot = r * nStatesPerRobot_;
242 
243  // Create a vector of indexes according to a descending order of the weights
244  // components of robot r
245  std::vector<uint> sorted = order_index<pdata_t>(weightComponents_[r], DESC);
246 
247  // For every particle
248  for (uint p = 0; p < nParticles_; ++p)
249  {
250  // Re-order the particle subsets of this robot
251  uint sort_index = sorted[p];
252 
253  // Copy this sub-particle set from dupParticles' sort_index particle
254  copyParticle(particles_, dupParticles, p, sort_index, o_robot,
255  o_robot + nStatesPerRobot_ - 1);
256 
257  // Update the particle weight (will get multiplied nRobots times and get a
258  // lower value)
259  particles_[O_WEIGHT][p] *= weightComponents_[r][sort_index];
260  }
261  }
262 }
263 
265 {
266  *iteration_oss << "fuseTarget() -> ";
267 
268  // If ball not seen by any robot, just skip all of this
269  bool ballSeen = false;
270  for (std::vector<TargetObservation>::iterator it =
271  bufTargetObservations_.begin();
272  it != bufTargetObservations_.end(); ++it)
273  {
274  if (it->found)
275  {
276  ballSeen = true;
277  break;
278  }
279  }
280 
281  // Update ball state
282  state_.target.seen = ballSeen;
283 
284  // exit if ball not seen by any robot
285  if (!ballSeen)
286  {
287  *iteration_oss << "Ball not seen ->";
288  return;
289  }
290  // If program is here, at least one robot saw the ball
291 
292  // Instance variables to be worked in the loops
293  pdata_t maxTargetSubParticleWeight, totalWeight;
294  uint m, p, mStar, r, o_robot;
295  float expArg, detValue, Z[3], Zcap[3], Z_Zcap[3];
296  TargetObservation* obs;
297 
298  // For every particle m in the particle set [1:M]
299  for (m = 0; m < nParticles_; ++m)
300  {
301  // Keep track of the maximum contributed weight and that particle's index
302  maxTargetSubParticleWeight = -1.0f;
303  mStar = m;
304 
305 // Find the particle m* in the set [m:M] for which the weight contribution
306 // by the target subparticle to the full weight is maximum
307 #pragma omp parallel for private(p, r, o_robot, obs, expArg, detValue, Z, \
308  Zcap, Z_Zcap)
309  for (p = m; p < nParticles_; ++p)
310  {
311  // Vector with probabilities for each robot, starting at 0.0 in case the
312  // robot hasn't seen the ball
313  std::vector<pdata_t> probabilities(nRobots_, 0.0);
314 
315  // Observations of the target by all robots
316  for (r = 0; r < nRobots_; ++r)
317  {
318  if (false == robotsUsed_[r] || false == bufTargetObservations_[r].found)
319  continue;
320 
321  // Usefull variables
322  obs = &bufTargetObservations_[r];
323  o_robot = r * nStatesPerRobot_;
324 
325  // Observation model
326  Z[0] = obs->x;
327  Z[1] = obs->y;
328  Z[2] = obs->z;
329  Zcap[0] =
330  (particles_[O_TARGET + O_TX][p] - particles_[o_robot + O_X][m]) *
331  (cos(particles_[o_robot + O_THETA][m])) +
332  (particles_[O_TARGET + O_TY][p] - particles_[o_robot + O_Y][m]) *
333  (sin(particles_[o_robot + O_THETA][m]));
334  Zcap[1] =
335  -(particles_[O_TARGET + O_TX][p] - particles_[o_robot + O_X][m]) *
336  (sin(particles_[o_robot + O_THETA][m])) +
337  (particles_[O_TARGET + O_TY][p] - particles_[o_robot + O_Y][m]) *
338  (cos(particles_[o_robot + O_THETA][m]));
339  Zcap[2] = particles_[O_TARGET + O_TZ][p];
340  Z_Zcap[0] = Z[0] - Zcap[0];
341  Z_Zcap[1] = Z[1] - Zcap[1];
342  Z_Zcap[2] = Z[2] - Zcap[2];
343 
344  expArg = -0.5 * (Z_Zcap[0] * Z_Zcap[0] / obs->covXX +
345  Z_Zcap[1] * Z_Zcap[1] / obs->covYY +
346  Z_Zcap[2] * Z_Zcap[2] / .04);
347  detValue =
348  1.0; // pow((2 * M_PI * obs->covXX * obs->covYY * 10.0), -0.5);
349 
350  // Probability value for this robot and this particle
351  probabilities[r] = detValue * exp(expArg);
352  }
353 
354  // Total weight contributed by this particle
355  totalWeight =
356  std::accumulate(probabilities.begin(), probabilities.end(), 0.0);
357 
358  // If the weight is the maximum as of now, update the maximum and set
359  // particle p as mStar
360  if (totalWeight > maxTargetSubParticleWeight)
361  {
362 // Swap particle m with m* so that the most relevant (in terms of
363 // weight)
364 // target subparticle is at the lowest indexes
365 #pragma omp critical
366  {
367  if (totalWeight > maxTargetSubParticleWeight)
368  {
369  maxTargetSubParticleWeight = totalWeight;
370  mStar = p;
371  }
372  }
373  }
374  }
375 
376  // Particle m* has been found, let's swap the subparticles
377  for (uint i = 0; i < STATES_PER_TARGET; ++i)
378  std::swap(particles_[O_TARGET + i][m], particles_[O_TARGET + i][mStar]);
379 
380  // Update the weight of this particle
381  particles_[O_WEIGHT][m] *= maxTargetSubParticleWeight;
382 
383  // The target subparticles are now reordered according to their weight
384  // contribution
385 
386  // printWeights("After fuseTarget(): ");
387  }
388 }
389 
391 {
392  // Implementing a very basic resampler... a particle gets selected
393  // proportional to its weight and startAt% of the top particles are kept
394 
395  particles_t duplicate(particles_);
396 
397  std::vector<pdata_t> cumulativeWeights(nParticles_);
398  cumulativeWeights[0] = duplicate[O_WEIGHT][0];
399 
400  for (uint par = 1; par < nParticles_; par++)
401  {
402  cumulativeWeights[par] =
403  cumulativeWeights[par - 1] + duplicate[O_WEIGHT][par];
404  }
405 
406  int startParticle = nParticles_ * startAt;
407 
408  // Robot particle resampling starts only at startParticle
409  for (uint par = startParticle; par < nParticles_; par++)
410  {
411  boost::random::uniform_real_distribution<> dist(0, 1);
412  double randNo = dist(seed_);
413 
414  int m = 0;
415  while (randNo > cumulativeWeights[m])
416  m++;
417 
418  copyParticle(particles_, duplicate, par, m, 0, O_TARGET - 1);
419  }
420 
421  // Target resampling is done for all particles
422  for (uint par = 0; par < nParticles_; par++)
423  {
424  boost::random::uniform_real_distribution<> dist(0, 1);
425  double randNo = dist(seed_);
426 
427  int m = 0;
428  while (randNo > cumulativeWeights[m])
429  m++;
430 
431  copyParticle(particles_, duplicate, par, m, O_TARGET,
432  nSubParticleSets_ - 1);
433  }
434 
435  // ROS_DEBUG("End of modifiedMultinomialResampler()");
436 }
437 
439 {
440  *iteration_oss << "resample() -> ";
441 
442  for (uint r = 0; r < nRobots_; ++r)
443  {
444  if (false == robotsUsed_[r])
445  continue;
446 
447  uint o_robot = r * nStatesPerRobot_;
448 
449  pdata_t stdX = calc_stdDev<pdata_t>(particles_[o_robot + O_X]);
450  pdata_t stdY = calc_stdDev<pdata_t>(particles_[o_robot + O_Y]);
451  pdata_t stdTheta = calc_stdDev<pdata_t>(particles_[o_robot + O_THETA]);
452 
453  state_.robots[r].conf = 1 / (stdX + stdY + stdTheta);
454 
455  // ROS_DEBUG("OMNI%d stdX = %f, stdY = %f, stdTheta = %f", r + 1, stdX,
456  // stdY,
457  // stdTheta);
458  }
459 
460  // Calc. sum of weights
461  double weightSum = std::accumulate(particles_[O_WEIGHT].begin(),
462  particles_[O_WEIGHT].end(), 0.0);
463 
464  // ROS_DEBUG("WeightSum before resampling = %f", weightSum);
465 
466  // printWeights("before resampling: ");
467 
468  if (weightSum < MIN_WEIGHTSUM)
469  {
470  ROS_WARN("Zero weightsum - returning without resampling");
471 
472  // Print iteration and state information
473  *iteration_oss << "FAIL! -> ";
474 
475  converged_ = false;
476  resetWeights(1.0 / nParticles_);
477  return;
478  }
479 
480  converged_ = true;
481 
482  // All resamplers use normalized weights
483  for (uint p = 0; p < nParticles_; ++p)
484  particles_[O_WEIGHT][p] = (pdata_t)(particles_[O_WEIGHT][p] / weightSum);
485 
487  100.0);
488 
489  // printWeights("after resampling: ");
490 }
491 
493 {
494  *iteration_oss << "estimate() -> ";
495 
496  pdata_t weightSum = std::accumulate(particles_[O_WEIGHT].begin(),
497  particles_[O_WEIGHT].end(), 0.0);
498 
499  // ROS_DEBUG("WeightSum when estimating = %f", weightSum);
500 
501  subparticles_t normalizedWeights(particles_[O_WEIGHT]);
502 
503  // Normalize the weights
504  for (uint p = 0; p < nParticles_; ++p)
505  normalizedWeights[p] = normalizedWeights[p] / weightSum;
506 
507  if (weightSum < MIN_WEIGHTSUM)
508  {
509  *iteration_oss << "DONE without estimating!";
510 
511  // Increase standard deviation for target prediction
513  {
517  }
518 
519  // Don't estimate
520  return;
521  }
522 
523  // Return (if necessary) to old target prediction model stddev
526 
527  // For each robot
528  for (uint r = 0; r < nRobots_; ++r)
529  {
530  // If the robot isn't playing, skip it
531  if (false == robotsUsed_[r])
532  continue;
533 
534  uint o_robot = r * nStatesPerRobot_;
535 
536  // A vector of weighted means that will be calculated in the next loop
537  std::vector<double> weightedMeans(nStatesPerRobot_ - 1, 0.0);
538 
539  // For theta we will obtain the mean of circular quantities, by converting
540  // to cartesian coordinates, placing each angle in the unit circle,
541  // averaging these points and finally converting again to polar
542  double weightedMeanThetaCartesian[2] = { 0, 0 };
543 
544  // ..and each particle
545  for (uint p = 0; p < nParticles_; ++p)
546  {
547  // Accumulate the state proportionally to the particle's normalized weight
548  for (uint g = 0; g < nStatesPerRobot_ - 1; ++g)
549  {
550  weightedMeans[g] += particles_[o_robot + g][p] * normalizedWeights[p];
551  }
552 
553  // Mean of circular quantities for theta
554  weightedMeanThetaCartesian[O_X] +=
555  cos(particles_[o_robot + O_THETA][p]) * normalizedWeights[p];
556  weightedMeanThetaCartesian[O_Y] +=
557  sin(particles_[o_robot + O_THETA][p]) * normalizedWeights[p];
558  }
559 
560  // Put the angle back in polar coordinates
561  double weightedMeanThetaPolar =
562  atan2(weightedMeanThetaCartesian[O_Y], weightedMeanThetaCartesian[O_X]);
563 
564  // Save in the robot state
565  // Can't use easy copy since one is using double precision
566  state_.robots[r].pose[O_X] = weightedMeans[O_X];
567  state_.robots[r].pose[O_Y] = weightedMeans[O_Y];
568  state_.robots[r].pose[O_THETA] = weightedMeanThetaPolar;
569  }
570 
571  // Target weighted means
572  std::vector<double> targetWeightedMeans(STATES_PER_TARGET, 0.0);
573 
574  // For each particle
575  for (uint p = 0; p < nParticles_; ++p)
576  {
577  for (uint t = 0; t < STATES_PER_TARGET; ++t)
578  {
579  targetWeightedMeans[t] +=
580  particles_[O_TARGET + t][p] * normalizedWeights[p];
581  }
582  }
583 
584  // Update position
585  // Can't use easy copy since one is using double precision
586  state_.target.pos[O_TX] = targetWeightedMeans[O_TX];
587  state_.target.pos[O_TY] = targetWeightedMeans[O_TY];
588  state_.target.pos[O_TZ] = targetWeightedMeans[O_TZ];
589 
590  *iteration_oss << "DONE!";
591 }
592 
593 void ParticleFilter::printWeights(std::string pre)
594 {
595  std::ostringstream debug;
596  debug << "Weights " << pre;
597  for (uint i = 0; i < nParticles_; ++i)
598  debug << particles_[O_WEIGHT][i] << " ";
599 
600  ROS_DEBUG("%s", debug.str().c_str());
601 }
602 
603 // TODO set different values for position and orientation, targets, etc
604 // Simple version, use default values - randomize all values between [-10,10]
606 {
607  if (initialized_)
608  return;
609 
610  int lvalue = -10;
611  int rvalue = 10;
612 
613  std::vector<double> defRand((nSubParticleSets_ - 1) * 2);
614 
615  for (size_t i = 0; i < defRand.size(); i += 2)
616  {
617  defRand[i] = lvalue;
618  defRand[i + 1] = rvalue;
619  }
620 
621  std::vector<double> defPos((nRobots_ * 2), 0.0);
622 
623  // Call the custom function with these values
624  init(defRand, defPos);
625 }
626 
627 // Overloaded function when using custom values
628 void ParticleFilter::init(const std::vector<double>& customRandInit,
629  const std::vector<double>& customPosInit)
630 {
631  if (initialized_)
632  return;
633 
634  // Set flag
635  initialized_ = true;
636 
637  bool flag_theta_given = (customPosInit.size() == nRobots_ * 3 &&
638  customRandInit.size() == nSubParticleSets_ * 3);
639  size_t numVars =
640  flag_theta_given ? customRandInit.size() / 3 : customRandInit.size() / 2;
641 
642  ROS_INFO("Initializing particle filter");
643 
644  // For all subparticle sets except the particle weights
645  for (size_t i = 0; i < numVars; ++i)
646  {
647  ROS_DEBUG("Values for distribution: %.4f %.4f", customRandInit[2 * i],
648  customRandInit[2 * i + 1]);
649 
650  boost::random::uniform_real_distribution<> dist(customRandInit[2 * i],
651  customRandInit[2 * i + 1]);
652 
653  // Sample a value from the uniform distribution for each particle
654  for (uint p = 0; p < nParticles_; ++p)
655  particles_[i][p] = (pdata_t)dist(seed_);
656  }
657 
658  // Particle weights init with same weight (1/nParticles)
659  resetWeights(1.0 / nParticles_);
660 
661  // Initialize pose with initial belief
662  for (uint r = 0; r < nRobots_; ++r)
663  {
664  pdata_t tmp[] = { (pdata_t)customPosInit[2 * r + O_X], (pdata_t)customPosInit[2 * r + O_Y],
665  (pdata_t)-M_PI };
666  state_.robots[r].pose = std::vector<pdata_t>(tmp, tmp + nStatesPerRobot_);
667  if (flag_theta_given)
668  state_.robots[r].pose.push_back(customPosInit[2 * r + O_THETA]);
669  }
670 
671  // State should have the initial belief
672 
673  ROS_INFO("Particle filter initialized");
674 }
675 
676 void ParticleFilter::predict(const uint robotNumber, const Odometry odom,
677  const ros::Time stamp)
678 {
679  if (!initialized_)
680  return;
681 
682  *iteration_oss << "predict(OMNI" << robotNumber + 1 << ") -> ";
683 
684  // If this is the main robot, update the odometry time
685  if (mainRobotID_ == robotNumber)
686  {
687  odometryTime_.updateTime(ros::WallTime::now());
688  iterationEvalTime_ = ros::WallTime::now();
689  }
690  using namespace boost::random;
691 
692  // Variables concerning this robot specifically
693  int robot_offset = robotNumber * nStatesPerRobot_;
694  std::vector<float>& alpha = dynamicVariables_.alpha[robotNumber];
695 
696  // Determining the propagation of the robot state through odometry
697  pdata_t deltaRot = atan2(odom.y, odom.x);
698  pdata_t deltaTrans = sqrt(odom.x * odom.x + odom.y * odom.y);
699  pdata_t deltaFinalRot = odom.theta - deltaRot;
700 
701  // Create an error model based on a gaussian distribution
702  normal_distribution<> deltaRotEffective(deltaRot, alpha[0] * fabs(deltaRot) +
703  alpha[1] * deltaTrans);
704 
705  normal_distribution<> deltaTransEffective(
706  deltaTrans,
707  alpha[2] * deltaTrans + alpha[3] * fabs(deltaRot + deltaFinalRot));
708 
709  normal_distribution<> deltaFinalRotEffective(
710  deltaFinalRot, alpha[0] * fabs(deltaFinalRot) + alpha[1] * deltaTrans);
711 
712  for (uint i = 0; i < nParticles_; i++)
713  {
714  // Rotate to final position
715  particles_[O_THETA + robot_offset][i] += deltaRotEffective(seed_);
716 
717  pdata_t sampleTrans = deltaTransEffective(seed_);
718 
719  // Translate to final position
720  particles_[O_X + robot_offset][i] +=
721  sampleTrans * cos(particles_[O_THETA + robot_offset][i]);
722  particles_[O_Y + robot_offset][i] +=
723  sampleTrans * sin(particles_[O_THETA + robot_offset][i]);
724 
725  // Rotate to final position and normalize angle
726  particles_[O_THETA + robot_offset][i] = angles::normalize_angle(
727  particles_[O_THETA + robot_offset][i] + deltaFinalRotEffective(seed_));
728  }
729 
730  // Check if we should activate robotRandom
731  // Only if no landmarks and no target seen
732  uint nLandmarksSeen = 0;
733  for (std::vector<LandmarkObservation>::iterator it =
734  bufLandmarkObservations_[robotNumber].begin();
735  it != bufLandmarkObservations_[robotNumber].end(); ++it)
736  {
737  if (it->found)
738  nLandmarksSeen++;
739  }
740 
741  if (nLandmarksSeen == 0 && !bufTargetObservations_[robotNumber].found)
742  {
743  // Randomize a bit for this robot since it does not see landmarks and target
744  // isn't seen
745  boost::random::uniform_real_distribution<> randPar(-0.05, 0.05);
746 
747  for (uint p = 0; p < nParticles_; ++p)
748  {
749  for (uint s = 0; s < nStatesPerRobot_; ++s)
750  particles_[robot_offset + s][p] += randPar(seed_);
751  }
752  }
753 
754  // If this is the main robot, perform one PF-UCLT iteration
755  if (mainRobotID_ == robotNumber)
756  {
757  // Lock mutex
758  boost::mutex::scoped_lock(mutex_);
759 
760  // All the PF-UCLT steps
761  predictTarget();
762  fuseRobots();
763  fuseTarget();
764  resample();
765  estimate();
766 
767  ROS_INFO("(WALL TIME) Odometry analyzed with = %fms",
768  1e3 * odometryTime_.diff);
769 
770  deltaIteration_ = ros::WallTime::now() - iterationEvalTime_;
773 
776 
777  ROS_INFO_STREAM("(WALL TIME) Iteration time: "
778  << 1e-6 * deltaIteration_.toNSec() << "ms ::: Worst case: "
779  << 1e-6 * maxDeltaIteration_.toNSec() << "ms ::: Average: "
780  << 1e-6 * (durationSum.toNSec() / numberIterations) << "s");
781 
782  // ROS_DEBUG("Iteration: %s", iteration_oss->str().c_str());
783  // Clear ostringstream
784  iteration_oss->str("");
785  iteration_oss->clear();
786 
787  // Start next iteration
788  nextIteration();
789  }
790 }
791 
793 {
794  *iteration_oss << "allLandmarks(OMNI" << robotNumber + 1 << ") -> ";
795 }
796 
798 {
799  *iteration_oss << "allTargets(OMNI" << robotNumber + 1 << ") -> ";
800 }
801 
803  const uint nRobots)
804  : firstCallback(true), alpha(nRobots, std::vector<float>(NUM_ALPHAS))
805 {
806  // Get node parameters, assume they exist
807  readParam<double>(nh, "percentage_to_keep", resamplingPercentageToKeep);
808 
809  readParam<int>(nh, "particles", nParticles);
810 
811  readParam<double>(nh, "predict_model_stddev", targetRandStddev);
813 
814  // Get alpha values for some robots (hard-coded for our 4 robots..)
815  for (uint r = 0; r < nRobots; ++r)
816  {
817  std::string paramName =
818  "OMNI" + boost::lexical_cast<std::string>(r + 1) + "_alpha";
819 
820  std::string str;
821  if (readParam<std::string>(nh, paramName, str))
822  fill_alpha(r, str); // value was provided
823  else
824  fill_alpha(r, "0.015,0.1,0.5,0.001"); // default
825  }
826 }
827 
829  const std::string& str)
830 {
831  // Tokenize the string of comma-separated values
832  std::istringstream iss(str);
833  std::string token;
834  uint tokenCount = 0;
835  while (std::getline(iss, token, ','))
836  {
837  if (tokenCount >= NUM_ALPHAS)
838  break;
839 
840  std::istringstream tokss(token);
841  float val;
842  tokss >> val;
843 
844  if (val < 0)
845  {
846  ROS_WARN("Invalid alpha value %f", val);
847  continue;
848  }
849 
850  alpha[robot][tokenCount] = val;
851  ++tokenCount;
852  }
853 
854  ROS_WARN_COND(tokenCount != NUM_ALPHAS,
855  "Number of alpha values provided is not the required number %d",
856  NUM_ALPHAS);
857 }
858 
859 // end of namespace pfuclt_omni_dataset
860 }
struct pfuclt_omni_dataset::ParticleFilter::dynamicVariables_s dynamicVariables_
void fill_alpha(const uint robot, const std::string &str)
dynamic_reconfigure::Server< DynamicConfig > dynamicServer_
void resetWeights(pdata_t val)
resetWeights - assign the value val to all particle weights
#define O_X
std::vector< pdata_t > subparticles_t
#define O_TX
#define STATES_PER_TARGET
void printWeights(std::string pre)
printWeights
std::vector< TargetObservation > bufTargetObservations_
void fuseRobots()
fuseRobots - fuse robot states step
std::size_t size()
size - interface to the size of the particle filter
dynamicVariables_s(ros::NodeHandle &nh, const uint nRobots)
struct pfuclt_omni_dataset::ParticleFilter::State::targetState_s target
ParticleFilter(struct PFinitData &data)
ParticleFilter - constructor.
#define O_Y
#define TARGET_RAND_MEAN
#define O_TY
void resample()
resample - the resampling step
void init()
init - initialize the particle filter set with the default randomized values
#define NUM_ALPHAS
void modifiedMultinomialResampler(uint startAt)
modifiedMultinomialResampler - a costly resampler that keeps 50% of the particles and implements the ...
std::vector< std::vector< LandmarkObservation > > bufLandmarkObservations_
void spreadTargetParticlesSphere(float particlesRatio, pdata_t center[3], float radius)
spreadTargetParticlesSphere - spread a percentage of the target particle in a sphere around center ...
#define TARGET_RAND_STDDEV_LOST
void predictTarget()
predictTarget - predict target state step
void estimate()
estimate - state estimation through weighted means of particle weights
The PFinitData struct - provides encapsulation to the initial data necessary to construct a ParticleF...
void saveAllTargetMeasurementsDone(const uint robotNumber)
saveAllTargetMeasurementsDone - call this function when all target measurements have been performed b...
#define O_THETA
void fuseTarget()
fuseTarget - fuse target state step
void dynamicReconfigureCallback(pfuclt_omni_dataset::DynamicConfig &)
dynamicReconfigureCallback - Dynamic reconfigure callback for dynamically setting variables during ru...
const std::vector< bool > & robotsUsed_
boost::shared_ptr< std::ostringstream > iteration_oss
virtual void nextIteration()
nextIteration - perform final steps before next iteration
#define O_TZ
std::vector< subparticles_t > particles_t
void predict(const uint robotNumber, const Odometry odom, const ros::Time stamp)
predict - prediction step in the particle filter set with the received odometry
virtual void resize_particles(const uint n)
resize_particles - change to a different number of particles
#define MIN_WEIGHTSUM
double updateTime(ros::Time t)
updateTime - adds the newest time available to the struct and returns the time difference ...
Definition: pfuclt_aux.h:178
void copyParticle(particles_t &p_To, particles_t &p_From, uint i_To, uint i_From)
copyParticle - copies a whole particle from one particle set to another
void saveAllLandmarkMeasurementsDone(const uint robotNumber)
saveAllLandmarkMeasurementsDone - call this function when all landmark measurements have been perform...
const std::vector< Landmark > & landmarksMap_


pfuclt_omni_dataset
Author(s):
autogenerated on Tue Jul 4 2017 19:38:38