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;
}
這種方式更加面向對象,適合複雜的應用場景和大型項目。
兩種方式的比較
實例化節點對象的優缺點
優點:
- 簡單直接,代碼量少
- 適合快速原型開發和簡單應用
- 學習曲線較低,適合初學者
缺點:
- 不適合複雜功能的實現
- 代碼可能變得雜亂,難以維護
- 不符合 ROS2 的推薦風格
繼承節點類的優缺點
優點:
- 更加面向對象,符合 ROS2 的設計理念
- 更好的代碼組織和封裝
- 適合複雜功能和大型項目
- 更容易擴展和維護
- 這是 ROS2 官方推薦的方式
缺點:
- 代碼量稍多
- 對初學者來說可能有一定的學習曲線
最佳實踐建議
在 ROS2 中,更推薦使用繼承 Node 的方式來創建節點對象。這種方式雖然初看起來代碼量稍多,但它提供了更好的代碼組織和封裝,使得代碼更容易維護和擴展。
以下是一些使用繼承方式的最佳實踐:
- 明確的類結構:將節點的功能清晰地組織在類的成員函數和變量中
- 適當的封裝:使用 private 和 protected 成員來控制訪問權限
- 初始化列表:在構造函數中使用初始化列表來初始化基類
- 資源管理:使用智能指針管理資源,避免內存泄漏
範例:完整的繼承式節點
下面是一個更完整的繼承式節點範例,包含了發布者、訂閱者和定時器:
#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 的設計理念。