自学内容网 自学内容网

【ROS2】ROS2 与 ROS1 编码方式对比(C++实现)

ROS1 主要使用roscpprospy作为客户端库,分别用于C++和Python语言。

ROS2 引入了新的客户端库rclcpp(C++)和rclpy(Python),它们提供了与ROS1类似的API,但更加现代化且功能更加强大。此外,ROS2还支持更多的编程语言,如Java、JavaScript等。

不仅客户端库有不同,ROS2 在代码编写方式上相对于ROS1也有了一些重要的变化,包括一些核心概念和实践方法的调整。ROS1代码编写方式更倾向于过程式思想,ROS2则使用了更多的面向对象的方式,也添加了更多的现代编程思想的使用。

ROS2 在 C++ 编程中引入了一些新的概念和 API,这些变化使得代码更加模块化和易于维护。特别是 rclcpp 库提供了更丰富的功能和更好的错误处理机制,同时支持异步编程模型。如果你已经熟悉 ROS1 的 C++ 编程,这些变化应该不会太难适应。主要的区别在于初始化、节点管理、服务和客户端的创建和调用方式。

一、初始化和关闭节点

ROS1:

#include "ros/ros.h"

int main(int argc, char **argv) 
{
    ros::init(argc, argv, "my_node");
    ros::NodeHandle nh;
    // 节点逻辑
    ros::spin();
    return 0;
}

ROS2:

#include "rclcpp/rclcpp.hpp"

int main(int argc, char **argv) 
{
    rclcpp::init(argc, argv);
    auto node = rclcpp::Node::make_shared("my_node");
    // 节点逻辑
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

二、发布者和订阅者

ROS1:

// 发布者
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);

// 订阅者
ros::Subscriber sub = nh.subscribe("chatter", 10, chatterCallback);
void chatterCallback(const std_msgs::String::ConstPtr& msg) 
{
    ROS_INFO("I heard: [%s]", msg->data.c_str());
}

ROS2:

// 发布者
auto pub = node->create_publisher<std_msgs::msg::String>("chatter", 10);

// lambda函数版订阅者
auto sub = node->create_subscription<std_msgs::msg::String>(
    "chatter", 10, [](const std_msgs::msg::String::SharedPtr msg){
        RCLCPP_INFO(rclcpp::get_logger("my_node"), "I heard: '%s'", msg->data.c_str());
    }
);

// 成员函数版订阅者
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
subscription_ = this->create_subscription<std_msgs::msg::String>(
            "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
void topic_callback(const std_msgs::msg::String &msg) const
{
    RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
}

三、服务和客户端

ROS1:

// 服务
ros::ServiceServer service = nh.advertiseService("add_two_ints", add_two_ints_callback);
bool add_two_ints_callback(srv::AddTwoInts::Request &req, 
                           srv::AddTwoInts::Response &res) 
{
    res.sum = req.a + req.b;
    ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
    ROS_INFO("sending back response: [%ld]", (long int)res.sum);
    return true;
}

// 客户端
client = nh.serviceClient<srv::AddTwoInts>("add_two_ints");
srv::AddTwoInts srv;
srv.request.a = 1;
srv.request.b = 2;
if (client.call(srv)) 
{
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
} 
else 
{
    ROS_ERROR("Failed to call service add_two_ints");
}

ROS2:

// 服务
auto service = node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", add);
void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request, std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) 
{
    response->sum = request->a + request->b;
    RCLCPP_INFO(node->get_logger(), "request: x=%ld, y=%ld", request->a, request->b);
    RCLCPP_INFO(node->get_logger(), "sending back response: [%ld]", response->sum);
}


// 客户端
auto client = node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
request->a = 1;
request->b = 2;

auto future = client->async_send_request(request);
if (rclcpp::spin_until_future_complete(node, future) == rclcpp::FutureReturnCode::SUCCESS) 
{
    RCLCPP_INFO(node->get_logger(), "Sum: %ld", future.get()->sum);
} 
else 
{
    RCLCPP_ERROR(node->get_logger(), "Failed to call service add_two_ints");
}

四、参数管理

ROS1:

std::string param_value;
nh.getParam("param_name", param_value);

ROS2:

auto param_value = node->get_parameter("param_name").as_string();

五、日志记录

ROS1:

ROS_INFO("This is an info message.");
ROS_DEBUG("This is a DEBUG message");
ROS_INFO("This is an INFO message");
ROS_WARN("This is a WARN message");
ROS_ERROR("This is an ERROR message");
ROS_FATAL("This is a FATAL message");

ROS2:

RCLCPP_INFO(this->get_logger(), "This is an info message.");
RCLCPP_DEBUG(this->get_logger(), "This is a DEBUG message");
RCLCPP_INFO(this->get_logger(), "This is an INFO message");
RCLCPP_WARN(this->get_logger(), "This is a WARN message");
RCLCPP_ERROR(this->get_logger(), "This is an ERROR message");
RCLCPP_FATAL(this->get_logger(), "This is a FATAL message");

六、生命周期管理

ROS1:

ROS1 没有内置的生命周期管理功能,通常需要开发者自己实现节点的生命周期管理。

ROS2:

ROS2 引入了生命周期节点的概念,允许更精细地控制节点的启动和停止过程。

class LifecycleNode : public rclcpp_lifecycle::LifecycleNode 
{
public:
    LifecycleNode() : LifecycleNode("lifecycle_node") {}
    LifecycleNode(std::string name) : rclcpp_lifecycle::LifecycleNode(name) {}

    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override 
    {
        RCLCPP_INFO(get_logger(), "on_configure() is called.");
        return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
    }

    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_activate(
        const rclcpp_lifecycle::State &state) override 
    {
        RCLCPP_INFO(get_logger(), "on_activate() is called.");
        return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
    }

    // 其他生命周期回调函数...
};

rclcpp_lifecycle是一个提供生命周期管理功能的库,它允许节点(Node)在不同的状态之间转换,如激活(active)、非激活(inactive)、配置(configuring)等。这种功能对于需要复杂启动和关闭序列的机器人或应用程序特别有用。

rclcpp_lifecycle::LifecycleNoderclcpp_lifecycle库中的一个核心类,它扩展了ROS 2的rclcpp::Node类,添加了生命周期管理的功能。通过使用LifecycleNode,开发者可以定义节点在生命周期状态转换时应执行的操作。



欢迎大家加QQ群,一起讨论学习:894013891


原文地址:https://blog.csdn.net/maizousidemao/article/details/144093635

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!