diff --git a/include/mcl_3dl/pf.h b/include/mcl_3dl/pf.h index 0fd9b99..bb40c60 100644 --- a/include/mcl_3dl/pf.h +++ b/include/mcl_3dl/pf.h @@ -256,10 +256,16 @@ class ParticleFilter } if (sum > 0.0) { + entropy_ = 0; for (auto& p : particles_) { p.probability_ /= sum; + if (p.probability_ > 0) + { + entropy_ += p.probability_ * std::log(p.probability_); + } } + entropy_ *= -1; } else { @@ -437,12 +443,17 @@ class ParticleFilter { return particles_.end(); } + FLT_TYPE getEntropy() const + { + return entropy_; + } protected: std::vector> particles_; std::vector> particles_dup_; RANDOM_ENGINE engine_; T ie_; + FLT_TYPE entropy_; }; } // namespace pf diff --git a/src/mcl_3dl.cpp b/src/mcl_3dl.cpp index 5582d47..6a899bd 100644 --- a/src/mcl_3dl.cpp +++ b/src/mcl_3dl.cpp @@ -1119,25 +1119,8 @@ class MCL3dlNode pm.orientation.w = p.rot_.w_; pa.poses.push_back(pm); } - pub_particle_.publish(pa); - } - - float getEntropy() - { - float sum = 0.0f; - for (auto& particle : *pf_) - { - sum += particle.probability_; - } - - float entropy = 0.0f; - for (auto& particle : *pf_) - { - if (particle.probability_ / sum > 0.0) - entropy += particle.probability_ / sum * std::log(particle.probability_ / sum); - } - return -entropy; + pub_particle_.publish(pa); } void diagnoseStatus(diagnostic_updater::DiagnosticStatusWrapper& stat) @@ -1159,7 +1142,7 @@ class MCL3dlNode stat.add("Odometry Availability", has_odom_ ? "true" : "false"); stat.add("IMU Availability", has_imu_ ? "true" : "false"); - status_.entropy = getEntropy(); + status_.entropy = pf_->getEntropy(); pub_status_.publish(status_); } diff --git a/test/src/test_pf.cpp b/test/src/test_pf.cpp index de8e655..d977333 100644 --- a/test/src/test_pf.cpp +++ b/test/src/test_pf.cpp @@ -327,6 +327,69 @@ TEST(Pf, AppendParticles) ASSERT_EQ(pf.getParticle(i)[0], val1); } +TEST(Pf, Entropy) +{ + mcl_3dl::pf::ParticleFilter pf(10); + + // no uncertainty + { + unsigned int idx = 0; + auto likelihood = [&idx](const State& s) -> float + { + return idx++ == 0 ? 1.0 : 0.0; + }; + pf.init(State(0), State(0.1)); + pf.measure(likelihood); + ASSERT_EQ(pf.getEntropy(), 0); + } + + // uniform distribution + { + auto likelihood = [](const State& s) -> float + { + return 0.1; + }; + pf.init(State(0), State(0.1)); + pf.measure(likelihood); + ASSERT_NEAR(pf.getEntropy(), 2.303, 1e-3); + } + + // compare the entropy of two distributions with first one having a narrower peak + // i.e. less uncertainty that the other + { + unsigned int idx = 0; + auto likelihood1 = [&idx](const State& s) -> float + { + float lk = 0.025; + if (idx >= 4 && idx < 6) + { + lk = 0.4; + } + idx++; + return lk; + }; + pf.init(State(0), State(0.1)); + pf.measure(likelihood1); + const float entropy1 = pf.getEntropy(); + + idx = 0; + auto likelihood2 = [&idx](const State& s) -> float + { + float lk = 0.025; + if (idx >= 2 && idx < 8) + { + lk = 0.15; + } + idx++; + return lk; + }; + pf.init(State(0), State(0.1)); + pf.measure(likelihood2); + const float entropy2 = pf.getEntropy(); + ASSERT_GT(entropy2, entropy1); + } +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv);