Skip to content

Commit

Permalink
Add two new resampling policies and refactor resample on motion into …
Browse files Browse the repository at this point in the history
…a policy

Signed-off-by: Gerardo Puga <[email protected]>
  • Loading branch information
glpuga committed Mar 4, 2023
1 parent b8e28fc commit 9bc7915
Show file tree
Hide file tree
Showing 20 changed files with 1,110 additions and 23 deletions.
22 changes: 22 additions & 0 deletions beluga/docs/doxygen_cites.bib
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,25 @@ @book{thrun2005probabilistic
year={2005},
publisher={MIT Press}
}

@ARTICLE{grisetti2007selectiveresampling,
author={Grisetti, Giorgio and Stachniss, Cyrill and Burgard, Wolfram},
journal={IEEE Transactions on Robotics},
title={Improved Techniques for Grid Mapping With Rao-Blackwellized Particle Filters},
year={2007},
volume={23},
number={1},
pages={34-46},
doi={10.1109/TRO.2006.889486}
}

@ARTICLE{tiacheng2015resamplingmethods,
author={Li, Tiancheng and Bolic, Miodrag and Djuric, Petar M.},
journal={IEEE Signal Processing Magazine},
title={Resampling Methods for Particle Filtering: Classification, implementation, and strategies},
year={2015},
volume={32},
number={3},
pages={70-86},
doi={10.1109/MSP.2014.2330626}
}
36 changes: 31 additions & 5 deletions beluga/include/beluga/algorithm/particle_filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@
#include <execution>
#include <utility>

#include <beluga/algorithm/sampling.hpp>
#include <beluga/tuple_vector.hpp>
#include <beluga/type_traits.hpp>
#include <ciabatta/ciabatta.hpp>
#include <range/v3/algorithm/copy.hpp>
#include <range/v3/algorithm/transform.hpp>
#include <range/v3/view/const.hpp>

#include <beluga/algorithm/sampling.hpp>
#include <beluga/resampling_policies/resample_interval_policy.hpp>
#include <beluga/resampling_policies/resample_on_motion_policy.hpp>
#include <beluga/resampling_policies/resampling_policies_poller.hpp>
#include <beluga/resampling_policies/selective_resampling_policy.hpp>
#include <beluga/tuple_vector.hpp>
#include <beluga/type_traits.hpp>

/**
* \file
* \brief Implementation of particle filters.
Expand Down Expand Up @@ -85,6 +90,7 @@
* The following is satisfied:
* - `p.particles()` is valid and returns a view to a container that satisfies the
* \ref ParticleContainerPage "ParticleContainer" requirements.
* - `p.weights()` is valid and returns a view to a container to the weights of the particles.
* - `p.sample()` updates the particle filter particles based on the last motion update.
* - `p.importance_sample()` updates the particle filter particles weight.
* - `p.resample()` updates the particle filter, generating new particles from the old ones
Expand Down Expand Up @@ -233,10 +239,14 @@ struct BootstrapParticleFilter : public Mixin {
/**
* The update will be done based on the Mixin implementation of the
* \ref ParticleSampledGenerationPage "ParticleSampledGeneration" and
* \ref ParticleResamplingPage "ParticleResampling" named requirements.
* \ref ParticleResamplingPage "ParticleResampling" named requirements
* and the active resampling policies.
*/
void resample() {
particles_ = initialize_container(this->self().generate_samples_from(particles_) | this->self().take_samples());
const auto resampling_vote_result = this->self().do_resampling_vote();
if (resampling_vote_result) {
particles_ = initialize_container(this->self().generate_samples_from(particles_) | this->self().take_samples());
}
}

private:
Expand Down Expand Up @@ -285,6 +295,11 @@ struct MCL : public ciabatta::mixin<
ciabatta::curry<BaselineGeneration>::template mixin,
ciabatta::curry<NaiveGeneration>::template mixin,
ciabatta::curry<FixedResampling>::template mixin,
ciabatta::curry<
ResamplingPoliciesPoller,
ResampleOnMotionPolicy,
ResampleIntervalPolicy,
SelectiveResamplingPolicy>::template mixin,
MotionModel,
SensorModel> {
using ciabatta::mixin<
Expand All @@ -293,6 +308,9 @@ struct MCL : public ciabatta::mixin<
ciabatta::curry<BaselineGeneration>::template mixin,
ciabatta::curry<NaiveGeneration>::template mixin,
ciabatta::curry<FixedResampling>::template mixin,
ciabatta::
curry<ResamplingPoliciesPoller, ResampleOnMotionPolicy, ResampleIntervalPolicy, SelectiveResamplingPolicy>::
mixin,
MotionModel,
SensorModel>::mixin;
};
Expand Down Expand Up @@ -321,6 +339,11 @@ struct AMCL : public ciabatta::mixin<
ciabatta::curry<BaselineGeneration>::template mixin,
ciabatta::curry<AdaptiveGeneration>::template mixin,
ciabatta::curry<KldResampling>::template mixin,
ciabatta::curry<
ResamplingPoliciesPoller,
ResampleOnMotionPolicy,
ResampleIntervalPolicy,
SelectiveResamplingPolicy>::template mixin,
MotionModel,
SensorModel> {
using ciabatta::mixin<
Expand All @@ -329,6 +352,9 @@ struct AMCL : public ciabatta::mixin<
ciabatta::curry<BaselineGeneration>::template mixin,
ciabatta::curry<AdaptiveGeneration>::template mixin,
ciabatta::curry<KldResampling>::template mixin,
ciabatta::
curry<ResamplingPoliciesPoller, ResampleOnMotionPolicy, ResampleIntervalPolicy, SelectiveResamplingPolicy>::
template mixin,
MotionModel,
SensorModel>::mixin;
};
Expand Down
1 change: 1 addition & 0 deletions beluga/include/beluga/beluga.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <beluga/algorithm.hpp>
#include <beluga/motion.hpp>
#include <beluga/resampling_policies.hpp>
#include <beluga/sensor.hpp>
#include <beluga/tuple_vector.hpp>
#include <beluga/type_traits.hpp>
Expand Down
2 changes: 2 additions & 0 deletions beluga/include/beluga/motion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
* will be used in subsequent calls to the `apply_motion()` method.
* - `cp.apply_motion(s)` returns a `T::state_type`, that is the result of applying the motion model
* to `s` based on the updates.
* - `cp.latest_motion_update() returns a std::optional<update_type> with the latest motion update
* received through motion_update().
*/

#endif
7 changes: 5 additions & 2 deletions beluga/include/beluga/motion/differential_drive_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class DifferentialDriveModel : public Mixin {
* That is done by the particle filter using the apply_motion() method
* provided by this class.
*
* \param pose Last odometry udpate.
* \param pose Last odometry update.
*/
void update_motion(const update_type& pose) {
if (last_pose_) {
Expand Down Expand Up @@ -146,11 +146,14 @@ class DifferentialDriveModel : public Mixin {
last_pose_ = pose;
}

/// recovers latest motion update.
[[nodiscard]] std::optional<update_type> latest_motion_update() const { return last_pose_; }

private:
using DistributionParam = typename std::normal_distribution<double>::param_type;

DifferentialDriveModelParam params_;
std::optional<Sophus::SE2d> last_pose_;
std::optional<update_type> last_pose_;

DistributionParam first_rotation_params_{0.0, 0.0};
DistributionParam second_rotation_params_{0.0, 0.0};
Expand Down
6 changes: 6 additions & 0 deletions beluga/include/beluga/motion/stationary_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class StationaryModel : public Mixin {
* For the stationary model, updates are ignored.
*/
void update_motion(const update_type&) {}

/// Recovers latest motion update.
/**
* For the stationary model, we don't ever have motion updates.
*/
[[nodiscard]] std::optional<update_type> latest_motion_update() const { return std::nullopt; }
};

} // namespace beluga
Expand Down
46 changes: 46 additions & 0 deletions beluga/include/beluga/resampling_policies.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2022 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_RESAMPLING_POLICIES_HPP
#define BELUGA_RESAMPLING_POLICIES_HPP

#include <beluga/resampling_policies/resample_interval_policy.hpp>
#include <beluga/resampling_policies/resample_on_motion_policy.hpp>
#include <beluga/resampling_policies/resampling_policies_poller.hpp>
#include <beluga/resampling_policies/selective_resampling_policy.hpp>

/**
* \file
* \brief Includes all resampling policy related headers.
*/

/**
* \page ResamplingPolicyPage beluga named requirements: ResamplingPolicy
* Requirements for a resampling policy to be used a beluga `ParticleFilter`.
*
* \section ResamplingPolicyRequirements Requirements
* A type `T` satisfies the `ResamplingPolicy` requirements if the following is satisfied.
*
* Given:
* - An instance `p` of `T`.
* - An instance `c` of `C`, where is C a particle filter that meets the requirements listed in the policy.
*
* Then:
* - `p.do_resampling(c)` will return true if resampling must be done according to the policy, false otherwise. This
* function is called in cascade when multiple policies are installed in the filter, and a given filter's
* do_resampling() function may not be called during a given iteration if a previously queried policy has already voted
* "false" (short-circuit evaluation of policies).
* */

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2023 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_RESAMPLING_POLICIES_RESAMPLE_INTERVAL_POLICY_HPP
#define BELUGA_RESAMPLING_POLICIES_RESAMPLE_INTERVAL_POLICY_HPP

#include <optional>

#include <beluga/type_traits/resampling_policy_traits.hpp>

namespace beluga {

/// Parameters used to construct a ResampleIntervalPolicy instance.
struct ResampleIntervalPolicyParam {
/// Interval of calls to do_resampling() out of which only the last iteration will do resampling.
std::size_t resample_interval_count{1};
};

/// Implementation of the Resample Interval algorithm for resampling.
/**
* ResampleIntervalPolicy is an implementation of the \ref ResamplingPolicyPage "ResamplingPolicy" named requirements.
* */
struct ResampleIntervalPolicy {
public:
/// Parameter type that the constructor uses to configure the policy
using param_type = ResampleIntervalPolicyParam;

/// @brief Constructor
/// @param configuration Policy configuration data.
explicit ResampleIntervalPolicy(const param_type& configuration) : configuration_{configuration} {}

/// Vote whether resampling must be done according to this policy./
/**
* \tparam Concrete Type representing the concrete implementation of the filter.
*/
template <typename Concrete>
[[nodiscard]] bool do_resampling([[maybe_unused]] Concrete& filter) {
filter_update_counter_ = (filter_update_counter_ + 1) % configuration_.resample_interval_count;
return (filter_update_counter_ == 0);
}

private:
param_type configuration_; //< Policy configuration
std::size_t filter_update_counter_{0}; //< Current cycle phase
};

/// Specialization for a ResampleIntervalPolicy, see also \ref resampling_policy_traits.hpp.
template <>
struct resampling_policy_traits<ResampleIntervalPolicy> {
/// configuration struct type associated to the policy
using config_type = ResampleIntervalPolicyParam;
};

} // namespace beluga

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2023 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_RESAMPLING_POLICIES_RESAMPLE_ON_MOTION_POLICY_HPP
#define BELUGA_RESAMPLING_POLICIES_RESAMPLE_ON_MOTION_POLICY_HPP

#include <optional>

#include <sophus/se2.hpp>

#include <beluga/type_traits/resampling_policy_traits.hpp>

namespace beluga {

/// Parameters used to construct a ResampleOnMotionPolicy instance.
struct ResampleOnMotionPolicyParam {
/// distance threshold along x an y (independently) to trigger a resample
double update_min_d{0.};
/// angular threshold to trigger a resample
double update_min_a{0.};
};

/// Implementation of the Resample-On-Motion algorithm for resampling.
/**
* ResampleOnMotionPolicy is an implementation of the \ref ResamplingPolicyPage "ResamplingPolicy" named requirements.
* */
struct ResampleOnMotionPolicy {
public:
/// Parameter type that the constructor uses to configure the policy
using param_type = ResampleOnMotionPolicyParam;
/// Type used to exchange and store motion updates by the motion model
using motion_event = Sophus::SE2d;

/// @brief Constructor
/// @param configuration Policy configuration data.
explicit ResampleOnMotionPolicy(const param_type& configuration) : configuration_{configuration} {}

/// Vote whether resampling must be done according to this policy./
/**
* \tparam Concrete Type representing the concrete implementation of the filter.
* It must satisfy the \ref MotionModelPage MotionModel requirements.
*/
template <typename Concrete>
[[nodiscard]] bool do_resampling(Concrete& filter) {
// To avoid loss of diversity in the particle population, don't
// resample when the state is known to be static.
// Probabilistic Robotics \cite thrun2005probabilistic Chapter 4.2.4.
auto current_pose = filter.latest_motion_update();

// default to letting other policies decide
bool must_do_resample{true};

if (current_pose && latest_resample_pose_) {
// calculate relative transform between previous pose and the current one
const auto delta = latest_resample_pose_->inverse() * current_pose.value();

// only resample if movement is above thresholds
must_do_resample = //
std::abs(delta.translation().x()) > configuration_.update_min_d ||
std::abs(delta.translation().y()) > configuration_.update_min_d ||
std::abs(delta.so2().log()) > configuration_.update_min_a;
}

// we always measure the distance traveled since the last time we did resample
if (must_do_resample) {
latest_resample_pose_ = current_pose;
}

return must_do_resample;
}

private:
param_type configuration_;
std::optional<motion_event> latest_resample_pose_;
};

/// Specialization for a ResampleOnMotionPolicy, see also \ref resampling_policy_traits.hpp.
template <>
struct resampling_policy_traits<ResampleOnMotionPolicy> {
/// configuration struct type associated to the policy
using config_type = ResampleOnMotionPolicyParam;
};

} // namespace beluga

#endif
Loading

0 comments on commit 9bc7915

Please sign in to comment.