Skip to content

Commit

Permalink
Convert beluga_ros from header-only to a compiled library (#444)
Browse files Browse the repository at this point in the history
### Proposed changes

Related to #385.

Improves memory usage during compilation of the classic amcl node.

#### Type of change

- [ ] 🐛 Bugfix (change which fixes an issue)
- [x] 🚀 Feature (change which adds functionality)
- [ ] 📚 Documentation (change which fixes or extends documentation)

### Checklist

- [x] Lint and unit tests (if any) pass locally with my changes
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have added necessary documentation (if appropriate)
- [x] All commits have been signed for
[DCO](https://developercertificate.org/)

### Additional comments

The total memory usage does not improve when compiling the whole
`beluga_amcl` package as the NDT nodes are the bottleneck now. I will
work on that in follow-up PRs.

Signed-off-by: Nahuel Espinosa <[email protected]>
  • Loading branch information
nahueespinosa authored Oct 14, 2024
1 parent d72302b commit 6f13c9f
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 93 deletions.
31 changes: 17 additions & 14 deletions beluga_ros/cmake/ament.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,26 @@ find_package(tf2_eigen REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(visualization_msgs REQUIRED)

add_library(beluga_ros INTERFACE)
add_library(beluga_ros)
target_sources(beluga_ros PRIVATE src/amcl.cpp)
target_include_directories(
beluga_ros INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(beluga_ros INTERFACE beluga::beluga)
beluga_ros PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(beluga_ros PUBLIC beluga::beluga)
ament_target_dependencies(
beluga_ros
INTERFACE geometry_msgs
nav_msgs
sensor_msgs
std_msgs
tf2
tf2_eigen
tf2_geometry_msgs
visualization_msgs)
target_compile_definitions(beluga_ros INTERFACE BELUGA_ROS_VERSION=2)
target_compile_features(beluga_ros INTERFACE cxx_std_17)
PUBLIC geometry_msgs
nav_msgs
sensor_msgs
std_msgs
tf2
tf2_eigen
tf2_geometry_msgs
visualization_msgs)
target_compile_definitions(beluga_ros PUBLIC BELUGA_ROS_VERSION=2)
target_compile_features(beluga_ros PUBLIC cxx_std_17)

set_target_properties(beluga_ros PROPERTIES POSITION_INDEPENDENT_CODE ON)

ament_export_dependencies(
beluga
Expand Down
24 changes: 16 additions & 8 deletions beluga_ros/cmake/catkin.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,28 @@ catkin_package(
visualization_msgs
DEPENDS beluga
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CFG_EXTRAS ${PROJECT_NAME}-extras.cmake.in)

include_directories(include ${catkin_INCLUDE_DIRS})
add_definitions(${catkin_DEFINITIONS})

add_library(${PROJECT_NAME} INTERFACE)
add_library(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE src/amcl.cpp)
target_include_directories(
${PROJECT_NAME}
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(${PROJECT_NAME} INTERFACE beluga::beluga
${catkin_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} INTERFACE BELUGA_ROS_VERSION=1)
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17)
${PROJECT_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(${PROJECT_NAME} PUBLIC beluga::beluga ${catkin_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} PUBLIC BELUGA_ROS_VERSION=1)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)

set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)

install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})

install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
Expand Down
89 changes: 18 additions & 71 deletions beluga_ros/include/beluga_ros/amcl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,23 @@
#include <utility>
#include <variant>

#include <beluga/beluga.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/take_exactly.hpp>

#include <sophus/se2.hpp>

#include <beluga/algorithm/spatial_hash.hpp>
#include <beluga/algorithm/thrun_recovery_probability_estimator.hpp>
#include <beluga/containers.hpp>
#include <beluga/motion.hpp>
#include <beluga/policies.hpp>
#include <beluga/random.hpp>
#include <beluga/sensor.hpp>
#include <beluga/views/sample.hpp>

#include <beluga_ros/laser_scan.hpp>
#include <beluga_ros/occupancy_grid.hpp>

#include <range/v3/view/take_exactly.hpp>

/**
* \file
* \brief Generic two-dimensional implementation of the Adaptive Monte Carlo Localization (AMCL) algorithm in 2D.
Expand Down Expand Up @@ -116,21 +127,8 @@ class Amcl {
beluga_ros::OccupancyGrid map,
motion_model_variant motion_model,
sensor_model_variant sensor_model,
const AmclParams& params = AmclParams(),
execution_policy_variant execution_policy = std::execution::seq)
: params_{params},
map_distribution_{map},
motion_model_{std::move(motion_model)},
sensor_model_{std::move(sensor_model)},
execution_policy_{std::move(execution_policy)},
spatial_hasher_{params_.spatial_resolution_x, params_.spatial_resolution_y, params_.spatial_resolution_theta},
random_probability_estimator_{params_.alpha_slow, params_.alpha_fast},
update_policy_{beluga::policies::on_motion<Sophus::SE2d>(params_.update_min_d, params_.update_min_a)},
resample_policy_{beluga::policies::every_n(params_.resample_interval)} {
if (params_.selective_resampling) {
resample_policy_ = resample_policy_ && beluga::policies::on_effective_size_drop;
}
}
const AmclParams& params,
execution_policy_variant execution_policy);

/// Returns a reference to the current set of particles.
[[nodiscard]] const auto& particles() const { return particles_; }
Expand All @@ -157,10 +155,7 @@ class Amcl {
void initialize_from_map() { initialize(std::ref(map_distribution_)); }

/// Update the map used for localization.
void update_map(beluga_ros::OccupancyGrid map) {
map_distribution_ = beluga::MultivariateUniformDistribution{map};
std::visit([&](auto& sensor_model) { sensor_model.update_map(std::move(map)); }, sensor_model_);
}
void update_map(beluga_ros::OccupancyGrid map);

/// Update particles based on motion and sensor information.
/**
Expand All @@ -176,55 +171,7 @@ class Amcl {
* or std::nullopt if no update was performed.
*/
auto update(Sophus::SE2d base_pose_in_odom, beluga_ros::LaserScan laser_scan)
-> std::optional<std::pair<Sophus::SE2d, Sophus::Matrix3d>> {
if (particles_.empty()) {
return std::nullopt;
}

if (!update_policy_(base_pose_in_odom) && !force_update_) {
return std::nullopt;
}

// TODO(nahuel): Remove this once we update the measurement type.
auto measurement = laser_scan.points_in_cartesian_coordinates() | //
ranges::views::transform([&laser_scan](const auto& p) {
const auto result = laser_scan.origin() * Sophus::Vector3d{p.x(), p.y(), 0};
return std::make_pair(result.x(), result.y());
}) |
ranges::to<std::vector>;

std::visit(
[&, this](auto& policy, auto& motion_model, auto& sensor_model) {
particles_ |=
beluga::actions::propagate(policy, motion_model(control_action_window_ << base_pose_in_odom)) | //
beluga::actions::reweight(policy, sensor_model(std::move(measurement))) | //
beluga::actions::normalize(policy);
},
execution_policy_, motion_model_, sensor_model_);

const double random_state_probability = random_probability_estimator_(particles_);

if (resample_policy_(particles_)) {
auto random_state = ranges::compose(beluga::make_from_state<particle_type>, std::ref(map_distribution_));

if (random_state_probability > 0.0) {
random_probability_estimator_.reset();
}

particles_ |= beluga::views::sample |
beluga::views::random_intersperse(std::move(random_state), random_state_probability) |
beluga::views::take_while_kld(
spatial_hasher_, //
params_.min_particles, //
params_.max_particles, //
params_.kld_epsilon, //
params_.kld_z) |
beluga::actions::assign;
}

force_update_ = false;
return beluga::estimate(beluga::views::states(particles_), beluga::views::weights(particles_));
}
-> std::optional<std::pair<Sophus::SE2d, Sophus::Matrix3d>>;

/// Force a manual update of the particles on the next iteration of the filter.
void force_update() { force_update_ = true; }
Expand Down
103 changes: 103 additions & 0 deletions beluga_ros/src/amcl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2024 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.

#include <beluga_ros/amcl.hpp>

#include <beluga/actions/assign.hpp>
#include <beluga/actions/normalize.hpp>
#include <beluga/actions/propagate.hpp>
#include <beluga/actions/reweight.hpp>
#include <beluga/algorithm/estimation.hpp>
#include <beluga/views/random_intersperse.hpp>
#include <beluga/views/take_while_kld.hpp>

namespace beluga_ros {

Amcl::Amcl(
beluga_ros::OccupancyGrid map,
motion_model_variant motion_model,
sensor_model_variant sensor_model,
const AmclParams& params = AmclParams(),
execution_policy_variant execution_policy = std::execution::seq)
: params_{params},
map_distribution_{map},
motion_model_{std::move(motion_model)},
sensor_model_{std::move(sensor_model)},
execution_policy_{std::move(execution_policy)},
spatial_hasher_{params_.spatial_resolution_x, params_.spatial_resolution_y, params_.spatial_resolution_theta},
random_probability_estimator_{params_.alpha_slow, params_.alpha_fast},
update_policy_{beluga::policies::on_motion<Sophus::SE2d>(params_.update_min_d, params_.update_min_a)},
resample_policy_{beluga::policies::every_n(params_.resample_interval)} {
if (params_.selective_resampling) {
resample_policy_ = resample_policy_ && beluga::policies::on_effective_size_drop;
}
}

void Amcl::update_map(beluga_ros::OccupancyGrid map) {
map_distribution_ = beluga::MultivariateUniformDistribution{map};
std::visit([&](auto& sensor_model) { sensor_model.update_map(std::move(map)); }, sensor_model_);
}

auto Amcl::update(Sophus::SE2d base_pose_in_odom, beluga_ros::LaserScan laser_scan)
-> std::optional<std::pair<Sophus::SE2d, Sophus::Matrix3d>> {
if (particles_.empty()) {
return std::nullopt;
}

if (!update_policy_(base_pose_in_odom) && !force_update_) {
return std::nullopt;
}

// TODO(nahuel): Remove this once we update the measurement type.
auto measurement = laser_scan.points_in_cartesian_coordinates() | //
ranges::views::transform([&laser_scan](const auto& p) {
const auto result = laser_scan.origin() * Sophus::Vector3d{p.x(), p.y(), 0};
return std::make_pair(result.x(), result.y());
}) |
ranges::to<std::vector>;

std::visit(
[&, this](auto& policy, auto& motion_model, auto& sensor_model) {
particles_ |=
beluga::actions::propagate(policy, motion_model(control_action_window_ << base_pose_in_odom)) | //
beluga::actions::reweight(policy, sensor_model(std::move(measurement))) | //
beluga::actions::normalize(policy);
},
execution_policy_, motion_model_, sensor_model_);

const double random_state_probability = random_probability_estimator_(particles_);

if (resample_policy_(particles_)) {
auto random_state = ranges::compose(beluga::make_from_state<particle_type>, std::ref(map_distribution_));

if (random_state_probability > 0.0) {
random_probability_estimator_.reset();
}

particles_ |= beluga::views::sample |
beluga::views::random_intersperse(std::move(random_state), random_state_probability) |
beluga::views::take_while_kld(
spatial_hasher_, //
params_.min_particles, //
params_.max_particles, //
params_.kld_epsilon, //
params_.kld_z) |
beluga::actions::assign;
}

force_update_ = false;
return beluga::estimate(beluga::views::states(particles_), beluga::views::weights(particles_));
}

} // namespace beluga_ros

0 comments on commit 6f13c9f

Please sign in to comment.