add humble-navigation2

This commit is contained in:
X-lanni
2025-05-27 19:03:40 +08:00
parent 974abb5e1e
commit e74ec539c2
1280 changed files with 204114 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
add_library(${library_name} SHARED
costmap.cpp
node_utils.cpp
lifecycle_service_client.cpp
string_utils.cpp
lifecycle_utils.cpp
lifecycle_node.cpp
robot_utils.cpp
node_thread.cpp
odometry_utils.cpp
)
ament_target_dependencies(${library_name}
rclcpp
nav2_msgs
tf2
tf2_ros
nav_msgs
geometry_msgs
lifecycle_msgs
rclcpp_lifecycle
tf2_geometry_msgs
bondcpp
)
add_executable(lifecycle_bringup
lifecycle_bringup_commandline.cpp
)
target_link_libraries(lifecycle_bringup ${library_name})
find_package(Boost REQUIRED COMPONENTS program_options)
install(TARGETS
${library_name}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(TARGETS
lifecycle_bringup
RUNTIME DESTINATION lib/${PROJECT_NAME}
)
+266
View File
@@ -0,0 +1,266 @@
// Copyright (c) 2018 Intel Corporation
//
// 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 <vector>
#include <algorithm>
#include "nav2_util/costmap.hpp"
#include "tf2/LinearMath/Quaternion.h"
#include "nav2_util/geometry_utils.hpp"
using std::vector;
namespace nav2_util
{
using nav2_util::geometry_utils::orientationAroundZAxis;
const Costmap::CostValue Costmap::no_information = 255;
const Costmap::CostValue Costmap::lethal_obstacle = 254;
const Costmap::CostValue Costmap::inscribed_inflated_obstacle = 253;
const Costmap::CostValue Costmap::medium_cost = 128;
const Costmap::CostValue Costmap::free_space = 0;
// TODO(orduno): Port ROS1 Costmap package
Costmap::Costmap(
rclcpp::Node * node, bool trinary_costmap, bool track_unknown_space,
int lethal_threshold, int unknown_cost_value)
: node_(node), trinary_costmap_(trinary_costmap), track_unknown_space_(track_unknown_space),
lethal_threshold_(lethal_threshold), unknown_cost_value_(unknown_cost_value)
{
if (lethal_threshold_ < 0. || lethal_threshold_ > 100.) {
RCLCPP_WARN(
node_->get_logger(), "Costmap: Lethal threshold set to %d, it should be within"
" bounds 0-100. This could result in potential collisions!", lethal_threshold_);
// lethal_threshold_ = std::max(std::min(lethal_threshold_, 100), 0);
}
}
Costmap::~Costmap()
{
}
void Costmap::set_static_map(const nav_msgs::msg::OccupancyGrid & occupancy_grid)
{
RCLCPP_INFO(node_->get_logger(), "Costmap: Setting static costmap");
costmap_properties_.map_load_time = node_->now();
costmap_properties_.update_time = node_->now();
costmap_properties_.layer = "Master";
// Store the properties of the occupancy grid
costmap_properties_.resolution = occupancy_grid.info.resolution;
costmap_properties_.size_x = occupancy_grid.info.width;
costmap_properties_.size_y = occupancy_grid.info.height;
costmap_properties_.origin = occupancy_grid.info.origin;
uint32_t size_x = costmap_properties_.size_x;
uint32_t size_y = costmap_properties_.size_y;
costs_.resize(size_x * size_y);
// TODO(orduno): for now just doing a direct mapping of values from the original static map
// i.e. no cell inflation, etc.
std::vector<int8_t> static_map_cell_values = occupancy_grid.data;
unsigned int index = 0;
for (unsigned int i = 0; i < size_y; ++i) {
for (unsigned int j = 0; j < size_x; ++j) {
unsigned char value = static_map_cell_values[index];
costs_[index] = interpret_value(value);
++index;
}
}
map_provided_ = true;
}
void Costmap::set_test_costmap(const TestCostmap & testCostmapType)
{
costmap_properties_.map_load_time = node_->now();
costmap_properties_.update_time = node_->now();
costmap_properties_.layer = "master";
costmap_properties_.resolution = 1;
costmap_properties_.size_x = 10;
costmap_properties_.size_y = 10;
costmap_properties_.origin.position.x = 0.0;
costmap_properties_.origin.position.y = 0.0;
costmap_properties_.origin.position.z = 0.0;
// Define map rotation
// Provided as yaw with counterclockwise rotation, with yaw = 0 meaning no rotation
costmap_properties_.origin.orientation = orientationAroundZAxis(0.0);
costs_ = get_test_data(testCostmapType);
using_test_map_ = true;
}
nav2_msgs::msg::Costmap Costmap::get_costmap(
const nav2_msgs::msg::CostmapMetaData & /*specifications*/)
{
if (!map_provided_ && !using_test_map_) {
throw std::runtime_error("Costmap has not been set.");
}
// TODO(orduno): build a costmap given the specifications
// for now using the specs of the static map
nav2_msgs::msg::Costmap costmap;
costmap.header.stamp = node_->now();
costmap.header.frame_id = "map";
costmap.metadata = costmap_properties_;
costmap.data = costs_;
return costmap;
}
vector<uint8_t> Costmap::get_test_data(const TestCostmap testCostmapType)
{
// TODO(orduno): alternatively use a mathematical function
const uint8_t n = no_information;
const uint8_t x = lethal_obstacle;
const uint8_t i = inscribed_inflated_obstacle;
const uint8_t u = medium_cost;
const uint8_t o = free_space;
vector<uint8_t> costmapFree =
// 0 1 2 3 4 5 6 7 8 9
{o, o, o, o, o, o, o, o, o, o, // 0
o, o, o, o, o, o, o, o, o, o, // 1
o, o, o, o, o, o, o, o, o, o, // 2
o, o, o, o, o, o, o, o, o, o, // 3
o, o, o, o, o, o, o, o, o, o, // 4
o, o, o, o, o, o, o, o, o, o, // 5
o, o, o, o, o, o, o, o, o, o, // 6
o, o, o, o, o, o, o, o, o, o, // 7
o, o, o, o, o, o, o, o, o, o, // 8
o, o, o, o, o, o, o, o, o, o}; // 9
vector<uint8_t> costmapBounded =
// 0 1 2 3 4 5 6 7 8 9
{n, n, n, n, n, n, n, n, n, n, // 0
n, o, o, o, o, o, o, o, o, n, // 1
n, o, o, o, o, o, o, o, o, n, // 2
n, o, o, o, o, o, o, o, o, n, // 3
n, o, o, o, o, o, o, o, o, n, // 4
n, o, o, o, o, o, o, o, o, n, // 5
n, o, o, o, o, o, o, o, o, n, // 6
n, o, o, o, o, o, o, o, o, n, // 7
n, o, o, o, o, o, o, o, o, n, // 8
n, n, n, n, n, n, n, n, n, n}; // 9
vector<uint8_t> costmapObstacleBL =
// 0 1 2 3 4 5 6 7 8 9
{n, n, n, n, n, n, n, n, n, n, // 0
n, o, o, o, o, o, o, o, o, n, // 1
n, o, o, o, o, o, o, o, o, n, // 2
n, o, o, o, o, o, o, o, o, n, // 3
n, o, o, o, o, o, o, o, o, n, // 4
n, o, x, x, x, o, o, o, o, n, // 5
n, o, x, x, x, o, o, o, o, n, // 6
n, o, x, x, x, o, o, o, o, n, // 7
n, o, o, o, o, o, o, o, o, n, // 8
n, n, n, n, n, n, n, n, n, n}; // 9
vector<uint8_t> costmapObstacleTL =
// 0 1 2 3 4 5 6 7 8 9
{n, n, n, n, n, n, n, n, n, n, // 0
n, o, o, o, o, o, o, o, o, n, // 1
n, o, x, x, x, o, o, o, o, n, // 2
n, o, x, x, x, o, o, o, o, n, // 3
n, o, x, x, x, o, o, o, o, n, // 4
n, o, o, o, o, o, o, o, o, n, // 5
n, o, o, o, o, o, o, o, o, n, // 6
n, o, o, o, o, o, o, o, o, n, // 7
n, o, o, o, o, o, o, o, o, n, // 8
n, n, n, n, n, n, n, n, n, n}; // 9
vector<uint8_t> costmapMaze =
// 0 1 2 3 4 5 6 7 8 9
{n, n, n, n, n, n, n, n, n, n, // 0
n, o, o, o, o, o, o, o, o, n, // 1
n, x, x, o, x, x, x, o, x, n, // 2
n, o, o, o, o, x, o, o, o, n, // 3
n, o, x, x, o, x, o, x, o, n, // 4
n, o, x, x, o, x, o, x, o, n, // 5
n, o, o, x, o, x, o, x, o, n, // 6
n, x, o, x, o, x, o, x, o, n, // 7
n, o, o, o, o, o, o, x, o, n, // 8
n, n, n, n, n, n, n, n, n, n}; // 9
vector<uint8_t> costmapMaze2 =
// 0 1 2 3 4 5 6 7 8 9
{n, n, n, n, n, n, n, n, n, n, // 0
n, o, o, o, o, o, o, o, o, n, // 1
n, x, x, u, x, x, x, o, x, n, // 2
n, o, o, o, o, o, o, o, u, n, // 3
n, o, x, x, o, x, x, x, u, n, // 4
n, o, x, x, o, o, o, x, u, n, // 5
n, o, o, x, u, x, o, x, u, n, // 6
n, x, o, x, u, x, i, x, u, n, // 7
n, o, o, o, o, o, o, o, o, n, // 8
n, n, n, n, n, n, n, n, n, n}; // 9
switch (testCostmapType) {
case TestCostmap::open_space:
return costmapFree;
case TestCostmap::bounded:
return costmapBounded;
case TestCostmap::bottom_left_obstacle:
return costmapObstacleBL;
case TestCostmap::top_left_obstacle:
return costmapObstacleTL;
case TestCostmap::maze1:
return costmapMaze;
case TestCostmap::maze2:
return costmapMaze2;
default:
return costmapFree;
}
}
uint8_t Costmap::interpret_value(const int8_t value) const
{
if (track_unknown_space_ && value == unknown_cost_value_) {
return no_information;
} else if (!track_unknown_space_ && value == unknown_cost_value_) {
return free_space;
} else if (value >= lethal_threshold_) {
return lethal_obstacle;
} else if (trinary_costmap_) {
return free_space;
}
double scale = static_cast<double>(value / lethal_threshold_);
return static_cast<uint8_t>(scale * lethal_obstacle);
}
bool Costmap::is_free(const unsigned int x_coordinate, const unsigned int y_coordinate) const
{
unsigned int index = y_coordinate * costmap_properties_.size_x + x_coordinate;
return is_free(index);
}
bool Costmap::is_free(const unsigned int index) const
{
if (costs_[index] < Costmap::inscribed_inflated_obstacle) {
return true;
}
return false;
}
} // namespace nav2_util
@@ -0,0 +1,47 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 <vector>
#include <string>
#include <iostream>
#include <cstdlib>
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "nav2_util/lifecycle_utils.hpp"
using std::cerr;
using namespace std::chrono_literals;
void usage()
{
cerr << "Invalid command line.\n\n";
cerr << "This command will take a set of unconfigured lifecycle nodes through the\n";
cerr << "CONFIGURED to the ACTIVATED state\n";
cerr << "The nodes are brought up in the order listed on the command line\n\n";
cerr << "Usage:\n";
cerr << " > lifecycle_startup <node name> ...\n";
std::exit(1);
}
int main(int argc, char * argv[])
{
if (argc == 1) {
usage();
}
rclcpp::init(0, nullptr);
nav2_util::startup_lifecycle_nodes(
std::vector<std::string>(argv + 1, argv + argc),
10s);
rclcpp::shutdown();
}
@@ -0,0 +1,129 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 "nav2_util/lifecycle_node.hpp"
#include <memory>
#include <string>
#include <vector>
#include "lifecycle_msgs/msg/state.hpp"
namespace nav2_util
{
LifecycleNode::LifecycleNode(
const std::string & node_name,
const std::string & ns,
const rclcpp::NodeOptions & options)
: rclcpp_lifecycle::LifecycleNode(node_name, ns, options)
{
// server side never times out from lifecycle manager
this->declare_parameter(bond::msg::Constants::DISABLE_HEARTBEAT_TIMEOUT_PARAM, true);
this->set_parameter(
rclcpp::Parameter(
bond::msg::Constants::DISABLE_HEARTBEAT_TIMEOUT_PARAM, true));
printLifecycleNodeNotification();
register_rcl_preshutdown_callback();
}
LifecycleNode::~LifecycleNode()
{
RCLCPP_INFO(get_logger(), "Destroying");
runCleanups();
if (rcl_preshutdown_cb_handle_) {
rclcpp::Context::SharedPtr context = get_node_base_interface()->get_context();
context->remove_pre_shutdown_callback(*(rcl_preshutdown_cb_handle_.get()));
rcl_preshutdown_cb_handle_.reset();
}
}
void LifecycleNode::createBond()
{
RCLCPP_INFO(get_logger(), "Creating bond (%s) to lifecycle manager.", this->get_name());
bond_ = std::make_unique<bond::Bond>(
std::string("bond"),
this->get_name(),
shared_from_this());
bond_->setHeartbeatPeriod(0.10);
bond_->setHeartbeatTimeout(4.0);
bond_->start();
}
void LifecycleNode::runCleanups()
{
/*
* In case this lifecycle node wasn't properly shut down, do it here.
* We will give the user some ability to clean up properly here, but it's
* best effort; i.e. we aren't trying to account for all possible states.
*/
if (get_current_state().id() ==
lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
{
this->deactivate();
}
if (get_current_state().id() ==
lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE)
{
this->cleanup();
}
}
void LifecycleNode::on_rcl_preshutdown()
{
RCLCPP_INFO(
get_logger(), "Running Nav2 LifecycleNode rcl preshutdown (%s)",
this->get_name());
runCleanups();
destroyBond();
}
void LifecycleNode::register_rcl_preshutdown_callback()
{
rclcpp::Context::SharedPtr context = get_node_base_interface()->get_context();
rcl_preshutdown_cb_handle_ = std::make_unique<rclcpp::PreShutdownCallbackHandle>(
context->add_pre_shutdown_callback(
std::bind(&LifecycleNode::on_rcl_preshutdown, this))
);
}
void LifecycleNode::destroyBond()
{
RCLCPP_INFO(get_logger(), "Destroying bond (%s) to lifecycle manager.", this->get_name());
if (bond_) {
bond_.reset();
}
}
void LifecycleNode::printLifecycleNodeNotification()
{
RCLCPP_INFO(
get_logger(),
"\n\t%s lifecycle node launched. \n"
"\tWaiting on external lifecycle transitions to activate\n"
"\tSee https://design.ros2.org/articles/node_lifecycle.html for more information.", get_name());
}
} // namespace nav2_util
@@ -0,0 +1,102 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 "nav2_util/lifecycle_service_client.hpp"
#include <string>
#include <chrono>
#include <memory>
#include "lifecycle_msgs/srv/change_state.hpp"
#include "lifecycle_msgs/srv/get_state.hpp"
using nav2_util::generate_internal_node;
using std::chrono::seconds;
using std::make_shared;
using std::string;
using namespace std::chrono_literals;
namespace nav2_util
{
LifecycleServiceClient::LifecycleServiceClient(const string & lifecycle_node_name)
: node_(generate_internal_node(lifecycle_node_name + "_lifecycle_client")),
change_state_(lifecycle_node_name + "/change_state", node_),
get_state_(lifecycle_node_name + "/get_state", node_)
{
// Block until server is up
rclcpp::Rate r(20);
while (!get_state_.wait_for_service(2s)) {
RCLCPP_INFO(
node_->get_logger(), "Waiting for service %s...", get_state_.getServiceName().c_str());
r.sleep();
}
}
LifecycleServiceClient::LifecycleServiceClient(
const string & lifecycle_node_name,
rclcpp::Node::SharedPtr parent_node)
: node_(parent_node),
change_state_(lifecycle_node_name + "/change_state", node_),
get_state_(lifecycle_node_name + "/get_state", node_)
{
// Block until server is up
rclcpp::Rate r(20);
while (!get_state_.wait_for_service(2s)) {
RCLCPP_INFO(
node_->get_logger(), "Waiting for service %s...", get_state_.getServiceName().c_str());
r.sleep();
}
}
bool LifecycleServiceClient::change_state(
const uint8_t transition,
const seconds timeout)
{
if (!change_state_.wait_for_service(timeout)) {
throw std::runtime_error("change_state service is not available!");
}
auto request = std::make_shared<lifecycle_msgs::srv::ChangeState::Request>();
request->transition.id = transition;
auto response = change_state_.invoke(request, timeout);
return response.get();
}
bool LifecycleServiceClient::change_state(
std::uint8_t transition)
{
if (!change_state_.wait_for_service(5s)) {
throw std::runtime_error("change_state service is not available!");
}
auto request = std::make_shared<lifecycle_msgs::srv::ChangeState::Request>();
auto response = std::make_shared<lifecycle_msgs::srv::ChangeState::Response>();
request->transition.id = transition;
return change_state_.invoke(request, response);
}
uint8_t LifecycleServiceClient::get_state(
const seconds timeout)
{
if (!get_state_.wait_for_service(timeout)) {
throw std::runtime_error("get_state service is not available!");
}
auto request = std::make_shared<lifecycle_msgs::srv::GetState::Request>();
auto result = get_state_.invoke(request, timeout);
return result->current_state.id;
}
} // namespace nav2_util
@@ -0,0 +1,101 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 <chrono>
#include <string>
#include <thread>
#include <vector>
#include "lifecycle_msgs/srv/change_state.hpp"
#include "lifecycle_msgs/srv/get_state.hpp"
#include "nav2_util/lifecycle_service_client.hpp"
using std::string;
using lifecycle_msgs::msg::Transition;
namespace nav2_util
{
#define RETRY(fn, retries) \
{ \
int count = 0; \
while (true) { \
try { \
fn; \
break; \
} catch (std::runtime_error & e) { \
++count; \
if (count > (retries)) { \
throw e;} \
} \
} \
}
static void startupLifecycleNode(
const std::string & node_name,
const std::chrono::seconds service_call_timeout,
const int retries)
{
LifecycleServiceClient sc(node_name);
// Despite waiting for the service to be available and using reliable transport
// service calls still frequently hang. To get reliable startup it's necessary
// to timeout the service call and retry it when that happens.
RETRY(
sc.change_state(Transition::TRANSITION_CONFIGURE, service_call_timeout),
retries);
RETRY(
sc.change_state(Transition::TRANSITION_ACTIVATE, service_call_timeout),
retries);
}
void startup_lifecycle_nodes(
const std::vector<std::string> & node_names,
const std::chrono::seconds service_call_timeout,
const int retries)
{
for (const auto & node_name : node_names) {
startupLifecycleNode(node_name, service_call_timeout, retries);
}
}
static void resetLifecycleNode(
const std::string & node_name,
const std::chrono::seconds service_call_timeout,
const int retries)
{
LifecycleServiceClient sc(node_name);
// Despite waiting for the service to be available and using reliable transport
// service calls still frequently hang. To get reliable reset it's necessary
// to timeout the service call and retry it when that happens.
RETRY(
sc.change_state(Transition::TRANSITION_DEACTIVATE, service_call_timeout),
retries);
RETRY(
sc.change_state(Transition::TRANSITION_CLEANUP, service_call_timeout),
retries);
}
void reset_lifecycle_nodes(
const std::vector<std::string> & node_names,
const std::chrono::seconds service_call_timeout,
const int retries)
{
for (const auto & node_name : node_names) {
resetLifecycleNode(node_name, service_call_timeout, retries);
}
}
} // namespace nav2_util
+47
View File
@@ -0,0 +1,47 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 <memory>
#include "nav2_util/node_thread.hpp"
namespace nav2_util
{
NodeThread::NodeThread(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base)
: node_(node_base)
{
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
thread_ = std::make_unique<std::thread>(
[&]()
{
executor_->add_node(node_);
executor_->spin();
executor_->remove_node(node_);
});
}
NodeThread::NodeThread(rclcpp::executors::SingleThreadedExecutor::SharedPtr executor)
: executor_(executor)
{
thread_ = std::make_unique<std::thread>([&]() {executor_->spin();});
}
NodeThread::~NodeThread()
{
executor_->cancel();
thread_->join();
}
} // namespace nav2_util
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 "nav2_util/node_utils.hpp"
#include <chrono>
#include <string>
#include <algorithm>
#include <cctype>
using std::chrono::high_resolution_clock;
using std::to_string;
using std::string;
using std::replace_if;
using std::isalnum;
namespace nav2_util
{
string sanitize_node_name(const string & potential_node_name)
{
string node_name(potential_node_name);
// read this as `replace` characters in `node_name` `if` not alphanumeric.
// replace with '_'
replace_if(
begin(node_name), end(node_name),
[](auto c) {return !isalnum(c);},
'_');
return node_name;
}
string add_namespaces(const string & top_ns, const string & sub_ns)
{
if (!top_ns.empty() && top_ns.back() == '/') {
if (top_ns.front() == '/') {
return top_ns + sub_ns;
} else {
return "/" + top_ns + sub_ns;
}
}
return top_ns + "/" + sub_ns;
}
std::string time_to_string(size_t len)
{
string output(len, '0'); // prefill the string with zeros
auto timepoint = high_resolution_clock::now();
auto timecount = timepoint.time_since_epoch().count();
auto timestring = to_string(timecount);
if (timestring.length() >= len) {
// if `timestring` is shorter, put it at the end of `output`
output.replace(
0, len,
timestring,
timestring.length() - len, len);
} else {
// if `output` is shorter, just copy in the end of `timestring`
output.replace(
len - timestring.length(), timestring.length(),
timestring,
0, timestring.length());
}
return output;
}
std::string generate_internal_node_name(const std::string & prefix)
{
return sanitize_node_name(prefix) + "_" + time_to_string(8);
}
rclcpp::Node::SharedPtr generate_internal_node(const std::string & prefix)
{
auto options =
rclcpp::NodeOptions()
.start_parameter_services(false)
.start_parameter_event_publisher(false)
.arguments({"--ros-args", "-r", "__node:=" + generate_internal_node_name(prefix), "--"});
return rclcpp::Node::make_shared("_", options);
}
} // namespace nav2_util
@@ -0,0 +1,121 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2020 Sarthak Mittal
//
// 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 <string>
#include "nav2_util/odometry_utils.hpp"
using namespace std::chrono; // NOLINT
using namespace std::chrono_literals; // NOLINT
namespace nav2_util
{
OdomSmoother::OdomSmoother(
const rclcpp::Node::WeakPtr & parent,
double filter_duration,
const std::string & odom_topic)
: odom_history_duration_(rclcpp::Duration::from_seconds(filter_duration))
{
auto node = parent.lock();
odom_sub_ = node->create_subscription<nav_msgs::msg::Odometry>(
odom_topic,
rclcpp::SystemDefaultsQoS(),
std::bind(&OdomSmoother::odomCallback, this, std::placeholders::_1));
odom_cumulate_.twist.twist.linear.x = 0;
odom_cumulate_.twist.twist.linear.y = 0;
odom_cumulate_.twist.twist.linear.z = 0;
odom_cumulate_.twist.twist.angular.x = 0;
odom_cumulate_.twist.twist.angular.y = 0;
odom_cumulate_.twist.twist.angular.z = 0;
}
OdomSmoother::OdomSmoother(
const nav2_util::LifecycleNode::WeakPtr & parent,
double filter_duration,
const std::string & odom_topic)
: odom_history_duration_(rclcpp::Duration::from_seconds(filter_duration))
{
auto node = parent.lock();
odom_sub_ = node->create_subscription<nav_msgs::msg::Odometry>(
odom_topic,
rclcpp::SystemDefaultsQoS(),
std::bind(&OdomSmoother::odomCallback, this, std::placeholders::_1));
odom_cumulate_.twist.twist.linear.x = 0;
odom_cumulate_.twist.twist.linear.y = 0;
odom_cumulate_.twist.twist.linear.z = 0;
odom_cumulate_.twist.twist.angular.x = 0;
odom_cumulate_.twist.twist.angular.y = 0;
odom_cumulate_.twist.twist.angular.z = 0;
}
void OdomSmoother::odomCallback(const nav_msgs::msg::Odometry::SharedPtr msg)
{
std::lock_guard<std::mutex> lock(odom_mutex_);
// update cumulated odom only if history is not empty
if (!odom_history_.empty()) {
// to store current time
auto current_time = rclcpp::Time(msg->header.stamp);
// to store time of the first odom in history
auto front_time = rclcpp::Time(odom_history_.front().header.stamp);
// update cumulated odom when duration has exceeded and pop earliest msg
while (current_time - front_time > odom_history_duration_) {
const auto & odom = odom_history_.front();
odom_cumulate_.twist.twist.linear.x -= odom.twist.twist.linear.x;
odom_cumulate_.twist.twist.linear.y -= odom.twist.twist.linear.y;
odom_cumulate_.twist.twist.linear.z -= odom.twist.twist.linear.z;
odom_cumulate_.twist.twist.angular.x -= odom.twist.twist.angular.x;
odom_cumulate_.twist.twist.angular.y -= odom.twist.twist.angular.y;
odom_cumulate_.twist.twist.angular.z -= odom.twist.twist.angular.z;
odom_history_.pop_front();
if (odom_history_.empty()) {
break;
}
// update with the timestamp of earliest odom message in history
front_time = rclcpp::Time(odom_history_.front().header.stamp);
}
}
odom_history_.push_back(*msg);
updateState();
}
void OdomSmoother::updateState()
{
const auto & odom = odom_history_.back();
odom_cumulate_.twist.twist.linear.x += odom.twist.twist.linear.x;
odom_cumulate_.twist.twist.linear.y += odom.twist.twist.linear.y;
odom_cumulate_.twist.twist.linear.z += odom.twist.twist.linear.z;
odom_cumulate_.twist.twist.angular.x += odom.twist.twist.angular.x;
odom_cumulate_.twist.twist.angular.y += odom.twist.twist.angular.y;
odom_cumulate_.twist.twist.angular.z += odom.twist.twist.angular.z;
vel_smooth_.header = odom.header;
vel_smooth_.twist.linear.x = odom_cumulate_.twist.twist.linear.x / odom_history_.size();
vel_smooth_.twist.linear.y = odom_cumulate_.twist.twist.linear.y / odom_history_.size();
vel_smooth_.twist.linear.z = odom_cumulate_.twist.twist.linear.z / odom_history_.size();
vel_smooth_.twist.angular.x = odom_cumulate_.twist.twist.angular.x / odom_history_.size();
vel_smooth_.twist.angular.y = odom_cumulate_.twist.twist.angular.y / odom_history_.size();
vel_smooth_.twist.angular.z = odom_cumulate_.twist.twist.angular.z / odom_history_.size();
}
} // namespace nav2_util
+174
View File
@@ -0,0 +1,174 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2019 Steven Macenski
// Copyright (c) 2019 Samsung Research America
//
// 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 <string>
#include <cmath>
#include <memory>
#include "nav2_util/robot_utils.hpp"
#include "rclcpp/logger.hpp"
namespace nav2_util
{
bool getCurrentPose(
geometry_msgs::msg::PoseStamped & global_pose,
tf2_ros::Buffer & tf_buffer, const std::string global_frame,
const std::string robot_frame, const double transform_timeout,
const rclcpp::Time stamp)
{
tf2::toMsg(tf2::Transform::getIdentity(), global_pose.pose);
global_pose.header.frame_id = robot_frame;
global_pose.header.stamp = stamp;
return transformPoseInTargetFrame(
global_pose, global_pose, tf_buffer, global_frame, transform_timeout);
}
bool transformPoseInTargetFrame(
const geometry_msgs::msg::PoseStamped & input_pose,
geometry_msgs::msg::PoseStamped & transformed_pose,
tf2_ros::Buffer & tf_buffer, const std::string target_frame,
const double transform_timeout)
{
static rclcpp::Logger logger = rclcpp::get_logger("transformPoseInTargetFrame");
try {
transformed_pose = tf_buffer.transform(
input_pose, target_frame,
tf2::durationFromSec(transform_timeout));
return true;
} catch (tf2::LookupException & ex) {
RCLCPP_ERROR(
logger,
"No Transform available Error looking up target frame: %s\n", ex.what());
} catch (tf2::ConnectivityException & ex) {
RCLCPP_ERROR(
logger,
"Connectivity Error looking up target frame: %s\n", ex.what());
} catch (tf2::ExtrapolationException & ex) {
RCLCPP_ERROR(
logger,
"Extrapolation Error looking up target frame: %s\n", ex.what());
} catch (tf2::TimeoutException & ex) {
RCLCPP_ERROR(
logger,
"Transform timeout with tolerance: %.4f", transform_timeout);
} catch (tf2::TransformException & ex) {
RCLCPP_ERROR(
logger, "Failed to transform from %s to %s",
input_pose.header.frame_id.c_str(), target_frame.c_str());
}
return false;
}
bool getTransform(
const std::string & source_frame_id,
const std::string & target_frame_id,
const tf2::Duration & transform_tolerance,
const std::shared_ptr<tf2_ros::Buffer> tf_buffer,
tf2::Transform & tf2_transform)
{
geometry_msgs::msg::TransformStamped transform;
tf2_transform.setIdentity(); // initialize by identical transform
if (source_frame_id == target_frame_id) {
// We are already in required frame
return true;
}
try {
// Obtaining the transform to get data from source to target frame
transform = tf_buffer->lookupTransform(
target_frame_id, source_frame_id,
tf2::TimePointZero, transform_tolerance);
} catch (tf2::TransformException & e) {
RCLCPP_ERROR(
rclcpp::get_logger("getTransform"),
"Failed to get \"%s\"->\"%s\" frame transform: %s",
source_frame_id.c_str(), target_frame_id.c_str(), e.what());
return false;
}
// Convert TransformStamped to TF2 transform
tf2::fromMsg(transform.transform, tf2_transform);
return true;
}
bool getTransform(
const std::string & source_frame_id,
const rclcpp::Time & source_time,
const std::string & target_frame_id,
const rclcpp::Time & target_time,
const std::string & fixed_frame_id,
const tf2::Duration & transform_tolerance,
const std::shared_ptr<tf2_ros::Buffer> tf_buffer,
tf2::Transform & tf2_transform)
{
geometry_msgs::msg::TransformStamped transform;
tf2_transform.setIdentity(); // initialize by identical transform
try {
// Obtaining the transform to get data from source to target frame.
// This also considers the time shift between source and target.
transform = tf_buffer->lookupTransform(
target_frame_id, target_time,
source_frame_id, source_time,
fixed_frame_id, transform_tolerance);
} catch (tf2::TransformException & ex) {
RCLCPP_ERROR(
rclcpp::get_logger("getTransform"),
"Failed to get \"%s\"->\"%s\" frame transform: %s",
source_frame_id.c_str(), target_frame_id.c_str(), ex.what());
return false;
}
// Convert TransformStamped to TF2 transform
tf2::fromMsg(transform.transform, tf2_transform);
return true;
}
bool validateTwist(const geometry_msgs::msg::Twist & msg)
{
if (std::isinf(msg.linear.x) || std::isnan(msg.linear.x)) {
return false;
}
if (std::isinf(msg.linear.y) || std::isnan(msg.linear.y)) {
return false;
}
if (std::isinf(msg.linear.z) || std::isnan(msg.linear.z)) {
return false;
}
if (std::isinf(msg.angular.x) || std::isnan(msg.angular.x)) {
return false;
}
if (std::isinf(msg.angular.y) || std::isnan(msg.angular.y)) {
return false;
}
if (std::isinf(msg.angular.z) || std::isnan(msg.angular.z)) {
return false;
}
return true;
}
} // end namespace nav2_util
@@ -0,0 +1,48 @@
// Copyright (c) 2019 Intel Corporation
//
// 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 "nav2_util/string_utils.hpp"
#include <string>
using std::string;
namespace nav2_util
{
std::string strip_leading_slash(const string & in)
{
string out = in;
if ((!in.empty()) && (in[0] == '/')) {
out.erase(0, 1);
}
return out;
}
Tokens split(const string & tokenstring, char delimiter)
{
Tokens tokens;
size_t current_pos = 0;
size_t pos = 0;
while ((pos = tokenstring.find(delimiter, current_pos)) != string::npos) {
tokens.push_back(tokenstring.substr(current_pos, pos - current_pos));
current_pos = pos + 1;
}
tokens.push_back(tokenstring.substr(current_pos));
return tokens;
}
} // namespace nav2_util