15 #ifndef BELUGA_ROS_PARTICLE_CLOUD_HPP
16 #define BELUGA_ROS_PARTICLE_CLOUD_HPP
21 #include <type_traits>
23 #include <range/v3/range/primitives.hpp>
24 #include <range/v3/view/take_exactly.hpp>
26 #include <sophus/se2.hpp>
27 #include <sophus/se3.hpp>
28 #include <sophus/types.hpp>
38 #include <geometry_msgs/msg/pose_array.hpp>
39 #include <std_msgs/msg/color_rgba.hpp>
40 #include <visualization_msgs/msg/marker.hpp>
41 #include <visualization_msgs/msg/marker_array.hpp>
57 std_msgs::msg::ColorRGBA message;
61 const auto kr = std::fmod(5.0F + hue / 60.0F, 6.0F);
62 const auto kg = std::fmod(3.0F + hue / 60.0F, 6.0F);
63 const auto kb = std::fmod(1.0F + hue / 60.0F, 6.0F);
64 message.r = 1.0F - 1.0F * std::max(0.0F, std::min({kr, 4.0F - kr, 1.0F}));
65 message.g = 1.0F - 1.0F * std::max(0.0F, std::min({kg, 4.0F - kg, 1.0F}));
66 message.b = 1.0F - 1.0F * std::max(0.0F, std::min({kb, 4.0F - kb, 1.0F}));
76 template <
typename Scalar>
84 : linear_resolution(_linear_resolution), angular_resolution(_angular_resolution) {}
87 bool operator()(
const Sophus::SE2<Scalar>& a,
const Sophus::SE2<Scalar>& b)
const {
89 const Sophus::SE2<Scalar> diff = a * b.inverse();
90 return (abs(diff.translation().x()) < linear_resolution) && (abs(diff.translation().y()) < linear_resolution) &&
91 (abs(diff.so2().log()) < angular_resolution);
99 template <
typename Scalar>
107 : linear_resolution(_linear_resolution), angular_resolution(_angular_resolution) {}
110 bool operator()(
const Sophus::SE3<Scalar>& a,
const Sophus::SE3<Scalar>& b)
const {
112 const Sophus::SE3<Scalar> diff = a * b.inverse();
113 return (abs(diff.translation().x()) < linear_resolution) && (abs(diff.translation().y()) < linear_resolution) &&
114 (abs(diff.translation().z()) < linear_resolution) && (abs(diff.so3().angleX()) < angular_resolution) &&
115 (abs(diff.so3().angleY()) < angular_resolution) && (abs(diff.so3().angleZ()) < angular_resolution);
133 class Particle = ranges::range_value_t<Particles>,
135 class Scalar =
typename State::Scalar,
136 typename = std::enable_if_t<
137 std::is_same_v<State, typename Sophus::SE2<Scalar>> || std::is_same_v<State, typename Sophus::SE3<Scalar>>>>
138 geometry_msgs::msg::PoseArray&
140 static_assert(ranges::sized_range<decltype(particles)>);
141 message.poses.clear();
142 if (ranges::size(particles) > 0) {
143 message.poses.reserve(size);
144 for (
const auto& particle : particles | beluga::views::sample | ranges::views::take_exactly(size)) {
145 auto& pose = message.poses.emplace_back();
160 template <
class Particles,
class Message>
162 static_assert(ranges::sized_range<decltype(particles)>);
176 class Particle = ranges::range_value_t<Particles>,
179 class Scalar =
typename State::Scalar,
180 typename = std::enable_if_t<
181 std::is_same_v<State, typename Sophus::SE2<Scalar>> || std::is_same_v<State, typename Sophus::SE3<Scalar>>>>
183 Particles&& particles,
184 Scalar linear_resolution,
185 Scalar angular_resolution,
186 visualization_msgs::msg::MarkerArray& message) {
197 auto max_bin_weight =
Weight{1e-3};
198 auto states = beluga::views::states(particles);
199 auto weights = beluga::views::weights(particles);
201 auto histogram = StateHistogram{
205 auto& bin_weight = histogram[
state];
207 if (bin_weight > max_bin_weight) {
208 max_bin_weight = bin_weight;
212 message.markers.clear();
214 if (histogram.empty()) {
221 constexpr
auto kArrowBodyLength = Scalar{0.5};
222 constexpr
auto kArrowHeadLength = Scalar{0.1};
223 constexpr
auto kArrowHeadWidth = kArrowHeadLength / Scalar{5.0};
224 constexpr
auto kArrowLength = kArrowBodyLength + kArrowHeadLength;
226 using Translation =
typename State::TranslationType;
227 const auto arrow_body_base = Translation::Zero();
228 const auto arrow_head_tip = kArrowLength * Translation::UnitX();
229 const auto arrow_head_base = kArrowBodyLength * Translation::UnitX();
230 const auto arrow_head_right_corner =
231 kArrowBodyLength * Translation::UnitX() - (kArrowHeadWidth / Scalar{2.0}) * Translation::UnitY();
232 const auto arrow_head_left_corner =
233 kArrowBodyLength * Translation::UnitX() + (kArrowHeadWidth / Scalar{2.0}) * Translation::UnitY();
235 message.markers.reserve(2);
236 auto& arrow_bodies = message.markers.emplace_back();
238 arrow_bodies.ns =
"bodies";
239 arrow_bodies.color.a = 1.0;
240 arrow_bodies.pose.orientation.w = 1.0;
241 arrow_bodies.lifetime.sec = 1;
242 arrow_bodies.type = visualization_msgs::msg::Marker::LINE_LIST;
243 arrow_bodies.action = visualization_msgs::msg::Marker::ADD;
244 arrow_bodies.points.reserve(histogram.size() * 2);
245 arrow_bodies.colors.reserve(histogram.size() * 2);
247 auto& arrow_heads = message.markers.emplace_back();
249 arrow_heads.ns =
"heads";
250 arrow_heads.scale.x = 1.0;
251 arrow_heads.scale.y = 1.0;
252 arrow_heads.scale.z = 1.0;
253 arrow_heads.color.a = 1.0;
254 arrow_heads.pose.orientation.w = 1.0;
255 arrow_heads.lifetime.sec = 1;
256 arrow_heads.type = visualization_msgs::msg::Marker::TRIANGLE_LIST;
257 arrow_heads.action = visualization_msgs::msg::Marker::ADD;
258 arrow_heads.points.reserve(histogram.size() * 3);
259 arrow_heads.colors.reserve(histogram.size() * 3);
261 auto min_scale_factor =
Weight{1.0};
265 const auto scale_factor = max(
weight / max_bin_weight, 1e-1);
266 if (scale_factor < min_scale_factor) {
267 min_scale_factor = scale_factor;
272 const auto vertex_color = detail::alphaHueToRGBA(
273 static_cast<float>((1.0 - scale_factor) * 270.0),
static_cast<float>(0.25 + 0.75 * scale_factor));
276 arrow_bodies.points.push_back(
tf2::toMsg(
state * (scale_factor * arrow_body_base)));
277 arrow_bodies.points.push_back(
tf2::toMsg(
state * (scale_factor * arrow_head_base)));
278 arrow_bodies.colors.insert(arrow_bodies.colors.end(), 2, vertex_color);
280 arrow_heads.points.push_back(
tf2::toMsg(
state * (scale_factor * arrow_head_left_corner)));
281 arrow_heads.points.push_back(
tf2::toMsg(
state * (scale_factor * arrow_head_right_corner)));
282 arrow_heads.points.push_back(
tf2::toMsg(
state * (scale_factor * arrow_head_tip)));
284 arrow_heads.colors.insert(arrow_heads.colors.end(), 3, vertex_color);
287 arrow_bodies.scale.x =
static_cast<double>(min_scale_factor * kArrowHeadWidth) * 0.8;
300 class Particle = ranges::range_value_t<Particles>,
302 class Scalar =
typename State::Scalar>
304 Particles&& particles,
305 visualization_msgs::msg::MarkerArray& message) {
306 constexpr
auto kDefaultLinearResolution = Scalar{1e-3};
307 constexpr
auto kDefaultAngularResolution = Scalar{1e-3};
309 std::forward<Particles>(particles), kDefaultLinearResolution, kDefaultAngularResolution, message);
Sophus namespace extension for message conversion function overload resolution (ADL enabled).
Definition: tf2_sophus.hpp:332
The main Beluga ROS namespace.
Definition: amcl.hpp:47
geometry_msgs::msg::PoseArray & assign_particle_cloud(Particles &&particles, std::size_t size, geometry_msgs::msg::PoseArray &message)
Assign a pose distribution to a particle cloud message.
Definition: particle_cloud.hpp:139
typename particle_traits< T >::weight_type weight_t
constexpr state_detail::state_fn state
Numeric< double, struct WeightTag > Weight
constexpr weight_detail::weight_fn weight
typename particle_traits< T >::state_type state_t
geometry_msgs::msg::Point toMsg(const Eigen::Matrix< Scalar, 2, 1 > &in)
Converts an Eigen Vector2 type to a Point message.
Definition: tf2_eigen.hpp:39
std_msgs::msg::ColorRGBA alphaHueToRGBA(float hue, float alpha)
Make an RGBA color based on hue alone.
Definition: particle_cloud.hpp:56
bool operator()(const Sophus::SE2< Scalar > &a, const Sophus::SE2< Scalar > &b) const
Compares a and b for near equality.
Definition: particle_cloud.hpp:87
const Scalar angular_resolution
Resolution for rotational coordinates, in radians.
Definition: particle_cloud.hpp:95
almost_equal_to(Scalar _linear_resolution, Scalar _angular_resolution)
Constructs near equality functor.
Definition: particle_cloud.hpp:83
const Scalar linear_resolution
Resolution for translational coordinates, in meters.
Definition: particle_cloud.hpp:94
const Scalar angular_resolution
Resolution for rotational coordinates, in radians.
Definition: particle_cloud.hpp:119
almost_equal_to(Scalar _linear_resolution, Scalar _angular_resolution)
Constructs near equality functor.
Definition: particle_cloud.hpp:106
bool operator()(const Sophus::SE3< Scalar > &a, const Sophus::SE3< Scalar > &b) const
Compares a and b for near equality.
Definition: particle_cloud.hpp:110
const Scalar linear_resolution
Resolution for translational coordinates, in meters.
Definition: particle_cloud.hpp:118
std::equal_to equivalent for near equality operations.
Definition: particle_cloud.hpp:73
Message conversion API overloads for Eigen types.
Message conversion API overloads for Sophus types.