Skip to content
This repository was archived by the owner on Oct 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gazebo_ros2_control/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ find_package(gazebo_ros REQUIRED)
find_package(hardware_interface REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl_yaml_param_parser REQUIRED)
find_package(urdf REQUIRED)
find_package(yaml_cpp_vendor REQUIRED)

Expand Down Expand Up @@ -37,6 +38,7 @@ ament_target_dependencies(${PROJECT_NAME}
hardware_interface
pluginlib
rclcpp
rcl_yaml_param_parser
urdf
yaml_cpp_vendor
)
Expand All @@ -49,6 +51,7 @@ ament_target_dependencies(gazebo_hardware_plugins
gazebo_dev
hardware_interface
rclcpp
rcl_yaml_param_parser
)

## Install
Expand Down
1 change: 1 addition & 0 deletions gazebo_ros2_control/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<depend>hardware_interface</depend>
<depend>pluginlib</depend>
<depend>rclcpp</depend>
<depend>rcl_yaml_param_parser</depend>
<depend>std_msgs</depend>
<depend>urdf</depend>
<depend>yaml_cpp_vendor</depend>
Expand Down
264 changes: 62 additions & 202 deletions gazebo_ros2_control/src/gazebo_ros2_control_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include "pluginlib/class_loader.hpp"

#include "rclcpp/rclcpp.hpp"
#include "rclcpp/parameter_map.hpp"
#include "rcl_yaml_param_parser/parser.h"

#include "hardware_interface/resource_manager.hpp"
#include "hardware_interface/component_parser.hpp"
Expand Down Expand Up @@ -180,6 +182,8 @@ void GazeboRosControlPlugin::Load(gazebo::physics::ModelPtr parent, sdf::Element

if (sdf->HasElement("parameters")) {
impl_->param_file_ = sdf->GetElement("parameters")->Get<std::string>();
RCLCPP_INFO(
impl_->model_nh_->get_logger(), "Loading parameter file %s\n", impl_->param_file_.c_str());
} else {
RCLCPP_ERROR(
impl_->model_nh_->get_logger(), "No parameter file provided. Configuration might be wrong");
Expand Down Expand Up @@ -214,214 +218,70 @@ void GazeboRosControlPlugin::Load(gazebo::physics::ModelPtr parent, sdf::Element
new pluginlib::ClassLoader<gazebo_ros2_control::GazeboSystemInterface>(
"gazebo_ros2_control",
"gazebo_ros2_control::GazeboSystemInterface"));
} catch (pluginlib::LibraryLoadException & ex) {
RCLCPP_ERROR(
impl_->model_nh_->get_logger(), "Failed to create robot simulation interface loader: %s ",
ex.what());
}

for (unsigned int i = 0; i < control_hardware.size(); i++) {
std::string robot_hw_sim_type_str_ = control_hardware[i].hardware_class_type;
auto gazeboSystem = std::unique_ptr<gazebo_ros2_control::GazeboSystemInterface>(
impl_->robot_hw_sim_loader_->createUnmanagedInstance(robot_hw_sim_type_str_));

rclcpp::Node::SharedPtr node_ros2 = std::dynamic_pointer_cast<rclcpp::Node>(impl_->model_nh_);
if (!gazeboSystem->initSim(
node_ros2,
impl_->parent_model_,
control_hardware[i],
sdf))
{
RCLCPP_FATAL(
impl_->model_nh_->get_logger(), "Could not initialize robot simulation interface");
return;
}

resource_manager_->import_component(std::move(gazeboSystem));
for (unsigned int i = 0; i < control_hardware.size(); i++) {
std::string robot_hw_sim_type_str_ = control_hardware[i].hardware_class_type;
auto gazeboSystem = std::unique_ptr<gazebo_ros2_control::GazeboSystemInterface>(
impl_->robot_hw_sim_loader_->createUnmanagedInstance(robot_hw_sim_type_str_));

rclcpp::Node::SharedPtr node_ros2 = std::dynamic_pointer_cast<rclcpp::Node>(impl_->model_nh_);
if (!gazeboSystem->initSim(
node_ros2,
impl_->parent_model_,
control_hardware[i],
sdf))
{
RCLCPP_FATAL(
impl_->model_nh_->get_logger(), "Could not initialize robot simulation interface");
return;
}

impl_->executor_ = std::make_shared<rclcpp::executors::MultiThreadedExecutor>();

auto spin = [this]()
{
while (rclcpp::ok()) {
impl_->executor_->spin_once();
}
};
impl_->thread_executor_spin_ = std::thread(spin);

// Create the controller manager
RCLCPP_INFO(impl_->model_nh_->get_logger(), "Loading controller_manager");
impl_->controller_manager_.reset(
new controller_manager::ControllerManager(
std::move(resource_manager_),
impl_->executor_,
"controller_manager"));
impl_->executor_->add_node(impl_->controller_manager_);

// TODO(anyone): Coded example here. should disable when spawn functionality of
// controller manager is up
auto load_params_from_yaml_node = [](rclcpp::Node::SharedPtr node,
YAML::Node & yaml_node, const std::string & prefix)
{
std::function<void(YAML::Node, const std::string &,
std::shared_ptr<rclcpp::Node>, const std::string &)>
feed_yaml_to_node_rec =
[&](YAML::Node yaml_node, const std::string & key,
std::shared_ptr<rclcpp::Node> node, const std::string & prefix)
{
if (node->get_name() != prefix) {
return;
}
static constexpr char separator = '.';
if (yaml_node.Type() == YAML::NodeType::Scalar) {
std::string val_str = yaml_node.as<std::string>();

// TODO(ddengster): Do stricter typing for set_parameter value types.
// (ie. Numbers should be converted to int/double/etc instead of strings)
if (!node->has_parameter(key)) {
node->declare_parameter(key);
}

auto is_number = [](const std::string & str) -> bool {
return str.find_first_not_of("0123456789.-") == std::string::npos;
// @note: bugs with .05 or 15.
};

if (is_number(val_str)) {
std::stringstream ss(val_str);
double v = 0.0;
ss >> v;
node->set_parameter(rclcpp::Parameter(key, v));
} else {
node->set_parameter(rclcpp::Parameter(key, val_str));
}
return;
} else if (yaml_node.Type() == YAML::NodeType::Map) {
for (auto yaml_node_it : yaml_node) {
std::string newkey = yaml_node_it.first.as<std::string>();
if (newkey == prefix || newkey == "ros__parameters") {
newkey = "";
} else if (!key.empty()) {
newkey = key + separator + newkey;
}
feed_yaml_to_node_rec(yaml_node_it.second, newkey, node, prefix);
}
} else if (yaml_node.Type() == YAML::NodeType::Sequence) {
auto it = yaml_node.begin();
if (yaml_node.size()) {
if (it->IsScalar()) {
// submit as array of parameters
std::vector<std::string> val;
for (auto yaml_node_it : yaml_node) {
std::string name = yaml_node_it.as<std::string>();
val.push_back(name);
}
if (!node->has_parameter(key)) {
node->declare_parameter(key);
}
node->set_parameter({rclcpp::Parameter(key, val)});
} else {
size_t index = 0;
for (auto yaml_node_it : yaml_node) {
std::string newkey = std::to_string((index++));
if (!key.empty()) {
newkey = key + separator + newkey;
}
feed_yaml_to_node_rec(yaml_node_it, newkey, node, prefix);
}
}
}
}
};
if (node->get_name() != prefix) {
return;
}
feed_yaml_to_node_rec(yaml_node, prefix, node, prefix);
};
auto load_params_from_yaml = [&](rclcpp::Node::SharedPtr node,
const std::string & yaml_config_file, const std::string & prefix)
{
if (yaml_config_file.empty()) {
throw std::runtime_error("yaml config file path is empty");
}
YAML::Node root_node = YAML::LoadFile(yaml_config_file);
for (auto yaml : root_node) {
auto nodename = yaml.first.as<std::string>();
RCLCPP_DEBUG(impl_->model_nh_->get_logger(), "nodename: %s", nodename.c_str());
if (nodename == prefix) {
load_params_from_yaml_node(node, yaml.second, prefix);
}
}
};

// Start controller, will take effect at the end of the update function
std::vector<std::string> start_controllers = {};
std::vector<std::string> stop_controllers = {};

YAML::Node root_node = YAML::LoadFile(impl_->param_file_);
for (auto yaml : root_node) {
auto controller_manager_node_name = yaml.first.as<std::string>();
if (controller_manager_node_name == "controller_manager") {
for (auto yaml_node_it : yaml.second) { // ros__parameters
for (auto controller_manager_params_it : yaml_node_it.second) {
auto controller_name = controller_manager_params_it.first.as<std::string>();

if (controller_name == "update_rate") {
float udpate_rate = controller_manager_params_it.second.as<float>();
// Decide the plugin control period
impl_->control_period_ = rclcpp::Duration(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<double>(1.0 / udpate_rate)));

// Check the period against the simulation period
if (impl_->control_period_ < gazebo_period) {
RCLCPP_ERROR_STREAM(
impl_->model_nh_->get_logger(),
"Desired controller update period (" << impl_->control_period_.seconds() <<
" s) is faster than the gazebo simulation period (" <<
gazebo_period.seconds() << " s).");
} else if (impl_->control_period_ > gazebo_period) {
RCLCPP_WARN_STREAM(
impl_->model_nh_->get_logger(),
" Desired controller update period (" << impl_->control_period_.seconds() <<
" s) is slower than the gazebo simulation period (" <<
gazebo_period.seconds() << " s).");
}
} else {
for (auto controller_type_it : controller_manager_params_it.second) {
auto controller_type = controller_type_it.second.as<std::string>();
auto controller = impl_->controller_manager_->load_controller(
controller_name,
controller_type);
impl_->controllers_.push_back(controller);
load_params_from_yaml(
controller->get_node(),
impl_->param_file_,
controller_name);
controller->configure();
start_controllers.push_back(controller_name);
}
}
}
}
break;
}
}
resource_manager_->import_component(std::move(gazeboSystem));
}

auto switch_future = std::async(
std::launch::async,
&controller_manager::ControllerManager::switch_controller,
impl_->controller_manager_,
start_controllers, stop_controllers,
2, true, rclcpp::Duration(0, 0)); // STRICT_: 2
while (std::future_status::timeout == switch_future.wait_for(std::chrono::milliseconds(100))) {
impl_->controller_manager_->update();
}
controller_interface::return_type result = switch_future.get();
if (result != controller_interface::return_type::SUCCESS) {
RCLCPP_ERROR(impl_->model_nh_->get_logger(), "Error initializing the joint_state_controller");
}
} catch (pluginlib::LibraryLoadException & ex) {
impl_->executor_ = std::make_shared<rclcpp::executors::MultiThreadedExecutor>();

// Create the controller manager
RCLCPP_INFO(impl_->model_nh_->get_logger(), "Loading controller_manager");
impl_->controller_manager_.reset(
new controller_manager::ControllerManager(
std::move(resource_manager_),
impl_->executor_,
"controller_manager"));
impl_->executor_->add_node(impl_->controller_manager_);

// parse the parameters file
rcl_params_t * params_struct = rcl_yaml_node_struct_init(rcutils_get_default_allocator());
auto ret = rcl_parse_yaml_file(impl_->param_file_.c_str(), params_struct);
if (!ret) {
RCLCPP_ERROR(
impl_->model_nh_->get_logger(), "Failed to create robot simulation interface loader: %s ",
ex.what());
impl_->model_nh_->get_logger(), "Unable to parse yaml config in file %s",
impl_->param_file_.c_str());
return;
}
auto parameter_map = rclcpp::parameter_map_from(params_struct);
rcl_yaml_node_struct_fini(params_struct);
auto it = parameter_map.find("/controller_manager");
if (it != parameter_map.end()) {
impl_->controller_manager_->set_parameters(it->second);
} else {
RCLCPP_INFO(
impl_->model_nh_->get_logger(),
"No parameters for /controller_manager found, setting defaults");
}
Comment thread
Karsten1987 marked this conversation as resolved.
Outdated

Comment thread
Karsten1987 marked this conversation as resolved.
auto spin = [this]()
{
while (rclcpp::ok()) {
impl_->executor_->spin_once();
}
};
impl_->thread_executor_spin_ = std::thread(spin);

// Listen to the update event. This event is broadcast every simulation iteration.
impl_->update_connection_ =
Expand Down