1515#include < algorithm>
1616#include < cmath>
1717#include < numeric>
18-
19- #include < beluga_ros/particle_cloud.hpp>
20-
21- #include < sensor_msgs/msg/laser_scan.hpp>
22- #include < tf2_ros/buffer_interface.h>
23- #include < visualization_msgs/msg/marker_array.hpp>
24-
25- #include " beluga_demo_mh_amcl/particles_distribution.hpp"
18+ #include < range/v3/view.hpp>
2619
2720#include < beluga/random/multivariate_normal_distribution.hpp>
2821#include < beluga/actions/propagate.hpp>
3225#include < beluga/algorithm/estimation.hpp>
3326#include < beluga/motion/differential_drive_model.hpp>
3427#include < beluga/views/sample.hpp>
35- #include < beluga/views/particles.hpp>
3628
37- #include < range/v3/view.hpp>
29+ #include < beluga_ros/particle_cloud.hpp>
30+ #include < sensor_msgs/msg/laser_scan.hpp>
31+ #include < tf2_ros/buffer_interface.h>
32+ #include < visualization_msgs/msg/marker_array.hpp>
33+
34+ #include " beluga_demo_mh_amcl/particles_distribution.hpp"
3835
3936namespace mh_amcl
4037{
@@ -110,9 +107,9 @@ namespace mh_amcl
110107 {
111108 parent_node->declare_parameter (" good_hypo_threshold" , 0.6 );
112109 }
113- if (!parent_node->has_parameter (" good_hypo_threshold " ))
110+ if (!parent_node->has_parameter (" low_q_hypo_threshold " ))
114111 {
115- parent_node->declare_parameter (" low_q_hypo_threshold" , 0 .25f );
112+ parent_node->declare_parameter (" low_q_hypo_threshold" , 0.25 );
116113 }
117114 if (!parent_node->has_parameter (" particles_step" ))
118115 {
@@ -354,11 +351,11 @@ namespace mh_amcl
354351 std::shared_ptr<beluga_ros::OccupancyGrid> costmap)
355352 {
356353 std::string error;
357- if (tf_buffer_.canTransform (scan.header .frame_id , " base_footprint " ,
354+ if (tf_buffer_.canTransform (" base_footprint " , scan.header .frame_id ,
358355 tf2_ros::fromMsg (scan.header .stamp ), &error))
359356 {
360357 auto bf2laser_msg =
361- tf_buffer_.lookupTransform (scan.header .frame_id , " base_footprint " ,
358+ tf_buffer_.lookupTransform (" base_footprint " , scan.header .frame_id ,
362359 tf2_ros::fromMsg (scan.header .stamp ));
363360 tf2::fromMsg (bf2laser_msg, bf2laser_);
364361 }
@@ -370,52 +367,77 @@ namespace mh_amcl
370367 return ;
371368 }
372369
373- const double normal_comp_1 = utils:: INV_SQRT_2PI / distance_perception_error_;
374-
375- for ( auto &p : particles_)
370+ // ! Review sensor likelihood math and code
371+ // Build a sensor-model callable: given a Particle, returns a weight (likelihood)
372+ auto sensor_likelihood = [ this , &scan, &costmap]( const Sophus::SE2d &state) -> double
376373 {
377- p. hits = 0.0 ;
378- }
374+ // Convert state -> tf2 transform or world coords needed for your existing ray-cast:
375+ tf2::Transform map2bf = utils::se2dToTf2Transform (state);
379376
380- for (int j = 0 ; j < scan.ranges .size (); j++)
381- {
382- if (std::isnan (scan.ranges [j]) || std::isinf (scan.ranges [j]))
377+ // Loop over the scan to get the accumulated logarithmic likelihood and the valid beams (those that don't return infinite)
378+ double accum_log_likelihood = 0.0 ;
379+ int valid_beams = 0 ;
380+ // You may want to compute per-particle likelihood by summing log-likelihoods over beams
381+ for (size_t j = 0u ; j < scan.ranges .size (); ++j)
383382 {
384- continue ;
385- }
383+ double r = scan.ranges [j];
384+ if (!std::isfinite (r))
385+ continue ;
386386
387- tf2::Transform laser2point = get_transform_to_read (scan, j);
387+ tf2::Transform laser2point = get_transform_to_read (scan, static_cast <int >(j));
388+ double err_m = get_error_distance_to_obstacle (map2bf, bf2laser_, laser2point, scan, costmap, distance_perception_error_);
389+ if (!std::isinf (err_m))
390+ {
391+ double a = err_m / distance_perception_error_;
392+ double l = std::log (utils::INV_SQRT_2PI / distance_perception_error_) + (-0.5 * a * a);
393+ accum_log_likelihood += l;
394+ ++valid_beams;
395+ }
396+ }
388397
389- for ( int i = 0 ; i < particles_. size (); i++ )
398+ if (valid_beams == 0 )
390399 {
391- auto &p = particles_[i];
400+ // fallback small weight
401+ return 1e-9 ;
402+ }
403+ // Convert back from log-likelihood to linear scale (might underflow — you can return exp(accum_log_likelihood/valid_beams))
404+ double avg_log_l = accum_log_likelihood / static_cast <double >(valid_beams);
405+ double likelihood = std::exp (avg_log_l);
406+ return likelihood;
407+ };
392408
393- tf2::Transform particle_pose_tf2 = utils::se2dToTf2Transform (p.state );
409+ // Apply Beluga reweight action and normalize
410+ particles_ |= beluga::actions::reweight (sensor_likelihood) | beluga::actions::normalize ();
394411
395- double calculated_distance = get_error_distance_to_obstacle (
396- particle_pose_tf2, bf2laser_, laser2point, scan, costmap,
397- distance_perception_error_);
412+ // ! Study if this can be included somehow in the same loop as the likelihood (probably not because Beluga's reweight action receives a state, not the whole particle)
413+ // Calculate quality based on the average probability of hits, as per the original implementation.
414+ // This is calculated separately from the particle weights for robustness.
415+ quality_ = 0.0 ;
416+ for (auto &p : particles_)
417+ {
418+ tf2::Transform map2bf = utils::se2dToTf2Transform (p.state );
419+ double total_prob = 0.0 ;
420+ for (size_t j = 0u ; j < scan.ranges .size (); ++j)
421+ {
422+ if (std::isnan (scan.ranges [j]) || std::isinf (scan.ranges [j]))
423+ {
424+ continue ;
425+ }
398426
399- if (!std::isinf (calculated_distance))
427+ tf2::Transform laser2point = get_transform_to_read (scan, static_cast <int >(j));
428+ double err_m = get_error_distance_to_obstacle (map2bf, bf2laser_, laser2point, scan, costmap, distance_perception_error_);
429+
430+ if (!std::isinf (err_m))
400431 {
401- const double a = calculated_distance / distance_perception_error_;
432+ const double o = distance_perception_error_;
433+ const double normal_comp_1 = utils::INV_SQRT_2PI / o;
434+ const double a = err_m / o;
402435 const double normal_comp_2 = std::exp (-0.5 * a * a);
403-
404436 double prob = std::clamp (normal_comp_1 * normal_comp_2, 0.0 , 1.0 );
405- p.weight = std::max (p.weight + prob, 0.000001 );
406-
407- p.hits += prob;
437+ total_prob += prob;
408438 }
409439 }
410- }
411-
412- particles_ |= beluga::actions::normalize ();
413-
414- // Calculate quality
415- quality_ = 0.0 ;
416- for (auto &p : particles_)
417- {
418- p.hits = p.hits / static_cast <float >(scan.ranges .size ());
440+ p.hits = total_prob / static_cast <float >(scan.ranges .size ());
419441 quality_ = std::max (quality_, p.hits );
420442 }
421443 }
0 commit comments