4 min read

ROS C++ 程式設計風格指南

ROS C++ 程式設計風格指南

前言

在 ROS2 開發中,良好的程式設計風格不僅能提高代碼的可讀性和可維護性,還能避免一些常見的錯誤和性能問題。本文將介紹 ROS2 中 C++ 程式設計的風格指南和最佳實踐,特別是關於節點創建的不同方式及其優缺點。

ROS2 節點創建的兩種方式

在 ROS2 中,創建節點有兩種主要的方式:實例化節點對象和繼承節點類。讓我們來看看這兩種方式的具體實現和比較。

1. 實例化節點對象

這是最簡單直接的方式,直接創建一個 rclcpp::Node 的實例:

#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
    rclcpp::init(argc, argv);
    
    auto node = rclcpp::Node::make_shared("helloworld_node");
    
    RCLCPP_INFO(node->get_logger(), "hello world!");
    
    rclcpp::shutdown();
    
    return 0;
}

這種方式簡單明了,適合快速開發和簡單的應用場景。

2. 繼承節點類

這種方式通過創建一個繼承自 rclcpp::Node 的自定義類來實現:

#include "rclcpp/rclcpp.hpp"

class MyNode: public rclcpp::Node{
public:
    MyNode():Node("node_name"){
        RCLCPP_INFO(this->get_logger(), "hello world!");
    }
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc,argv);
    
    auto node = std::make_shared<MyNode>();
    
    rclcpp::shutdown();
    
    return 0;
}

這種方式更加面向對象,適合複雜的應用場景和大型項目。

兩種方式的比較

實例化節點對象的優缺點

優點:

  1. 簡單直接,代碼量少
  2. 適合快速原型開發和簡單應用
  3. 學習曲線較低,適合初學者

缺點:

  1. 不適合複雜功能的實現
  2. 代碼可能變得雜亂,難以維護
  3. 不符合 ROS2 的推薦風格

繼承節點類的優缺點

優點:

  1. 更加面向對象,符合 ROS2 的設計理念
  2. 更好的代碼組織和封裝
  3. 適合複雜功能和大型項目
  4. 更容易擴展和維護
  5. 這是 ROS2 官方推薦的方式

缺點:

  1. 代碼量稍多
  2. 對初學者來說可能有一定的學習曲線

最佳實踐建議

在 ROS2 中,更推薦使用繼承 Node 的方式來創建節點對象。這種方式雖然初看起來代碼量稍多,但它提供了更好的代碼組織和封裝,使得代碼更容易維護和擴展。

以下是一些使用繼承方式的最佳實踐:

  1. 明確的類結構:將節點的功能清晰地組織在類的成員函數和變量中
  2. 適當的封裝:使用 private 和 protected 成員來控制訪問權限
  3. 初始化列表:在構造函數中使用初始化列表來初始化基類
  4. 資源管理:使用智能指針管理資源,避免內存泄漏

範例:完整的繼承式節點

下面是一個更完整的繼承式節點範例,包含了發布者、訂閱者和定時器:

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class CompleteNode : public rclcpp::Node
{
private:
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
    rclcpp::TimerBase::SharedPtr timer_;
    size_t count_;

public:
    CompleteNode() : Node("complete_node"), count_(0)
    {
        // 創建發布者
        publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
        
        // 創建訂閱者
        subscription_ = this->create_subscription<std_msgs::msg::String>(
            "topic", 10,
            [this](const std_msgs::msg::String::SharedPtr msg) {
                RCLCPP_INFO(this->get_logger(), "Received: '%s'", msg->data.c_str());
            });
            
        // 創建定時器
        timer_ = this->create_wall_timer(
            std::chrono::seconds(1),
            [this]() {
                auto message = std_msgs::msg::String();
                message.data = "Hello World: " + std::to_string(count_++);
                RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
                publisher_->publish(message);
            });
    }
};

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<CompleteNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

結論

在 ROS2 C++ 開發中,雖然實例化節點對象的方式簡單直接,但繼承節點類的方式更符合 ROS2 的設計理念和最佳實踐。對於任何超出簡單示例的實際應用,都建議使用繼承的方式來創建節點。這種方式提供了更好的代碼組織、封裝和可維護性,使得代碼更加健壯和易於擴展。

隨著項目的複雜度增加,繼承方式的優勢會變得更加明顯。因此,即使對於初學者,也建議從一開始就養成使用繼承方式創建節點的習慣,這將有助於更好地理解和應用 ROS2 的設計理念。