ROS2 入門

0x01 重要概念

ROS2本質上是一個通信框架,有兩個重要組成:

  • node : 通信對象
  • topic : 消息主題

ROS2 三種通信方式
├── Topic(發(fā)布/訂閱) — 適合持續(xù)數(shù)據(jù)流(傳感器、狀態(tài))
├── Service(服務) — 請求/響應模式,適合一次性指令(開關燈、查詢狀態(tài))
└── Action(動作) — 長時任務 + 進度反饋(導航到目標點、機械臂運動)

上面的概念對于后端開發(fā)而言,理解起來很容易。

0x02 DDS協(xié)議

DDS( Data Distribution Service) 分布式通信協(xié)議標準。

0x03 FastDDS

FastDDS: 作為上述協(xié)議的一個實現(xiàn),主要功能是為node提供發(fā)現(xiàn)機制

分兩個重要階段:

一、參與者發(fā)現(xiàn)階段(PDP)

目的:讓不同DomainParticipant相互感知存在。
機制:每個參與者周期性地發(fā)送公告消息(默認通過基于DomainId計算的多播地址和端口),消息中包含接收元數(shù)據(jù)和用戶數(shù)據(jù)流量的單播地址(IP+端口)。
匹配條件:兩個參與者必須在同一個DDS Domain中。
配置選項:可通過“Initial peers”指定單播地址列表發(fā)送公告。

可配置公告的發(fā)送周期(通過發(fā)現(xiàn)配置)。

二、端點發(fā)現(xiàn)階段(EDP)

目的:讓DataWriter和DataReader互相發(fā)現(xiàn)。
機制:參與者利用PDP建立的通信通道,交換各自擁有的DataWriter和DataReader信息,包括Topic和數(shù)據(jù)類型。
匹配條件:Topic和數(shù)據(jù)類型必須一致。
結果:匹配成功后,DataWriter和DataReader即可開始收發(fā)用戶數(shù)據(jù)。

經(jīng)過上面兩個階段后,所有參與者(node) 都會知道相互之間的 topic 和消息數(shù)據(jù)格式了。

0x04 node 發(fā)布流程

一個典型的node發(fā)布消息的調(diào)用棧關系如下:

  • RCLCPP層
    1. rclcpp::Publisher::publish
    2. rclcpp::Publisher::do_inter_process_publish
  • RCL層
    1. rcl_publish
  • RMW層
    1.rmw_publish
    1. rmw_fastrtps_shared_cpp::__rmw_publish
  • FastDDS層
    1. eprosima::fastdds::dds::DataWriter::write_w_timestamp
    2. eprosima::fastdds::dds::DataWriterImpl::write_w_timestamp
    3. eprosima::fastdds::dds::DataWriterImpl::create_new_change_with_params
      4.eprosima::fastdds::dds::DataWriterImpl::perform_create_new_change

0x05 node 訂閱

class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber()
  : Node("minimal_subscriber")
  {
    auto topic_callback =
      [this](std_msgs::msg::String::UniquePtr msg) -> void {
        RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
      };
    subscription_ =
      this->create_subscription<std_msgs::msg::String>("topic", 10, topic_callback);
  }

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

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

0x06 Message

傳遞的消息格式:

std_msgs/Header header
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data

0x07 完成代碼演示

使用 CPP 實現(xiàn)基于ROS2的消息訂閱示例

  • CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(ros2_pub_recv_poc)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(ping_publisher src/ping_publisher.cpp)
ament_target_dependencies(ping_publisher rclcpp std_msgs)
add_executable(ping_subscriber src/ping_subscriber.cpp)
ament_target_dependencies(ping_subscriber rclcpp std_msgs)

install(TARGETS
  ping_publisher
  ping_subscriber
  DESTINATION lib/${PROJECT_NAME})

ament_package()
  • 生產(chǎn)者
#include <chrono>
#include <memory>
#include <iomanip>
#include <sstream>

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

using namespace std::chrono_literals;

class PingPublisher : public rclcpp::Node {
public:
  PingPublisher() : Node("ping_publisher") {
    publisher_ = this->create_publisher<std_msgs::msg::String>("ping", 10);
    timer_ = this->create_wall_timer(
      1000ms,
      [this]() {
        auto message = std_msgs::msg::String();
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
          now.time_since_epoch()) % 1000;

        std::stringstream ss;
        ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
        ss << '.' << std::setfill('0') << std::setw(3) << ms.count();

        message.data = ss.str();
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
        publisher_->publish(message);
      }
    );
  }

private:
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
};

int main(int argc, char * argv[]) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<PingPublisher>());
  rclcpp::shutdown();
  return 0;
}
  • 訂閱者
#include <memory>

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

class PingSubscriber : public rclcpp::Node {
public:
  PingSubscriber() : Node("ping_subscriber") {
    subscription_ = this->create_subscription<std_msgs::msg::String>(
      "ping",
      10,
      [this](const std_msgs::msg::String::SharedPtr msg) {
        RCLCPP_INFO(this->get_logger(), "Received: '%s'", msg->data.c_str());
      }
    );
  }

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

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

還支持通過命令行方式來確認

# 打印 node 列表
ros2 node list
# 打印 topic 列表
ros2 topic list
# 命令行監(jiān)聽 /ping  topic
ros2 topic echo /ping
# 

0x08 后續(xù)學習規(guī)劃

第一階段:夯實 ROS2 通信機制
你已完成 Topic(話題),接下來補全另外兩種通信范式:
ROS2 三種通信方式
├── Topic(發(fā)布/訂閱) — 已掌握,適合持續(xù)數(shù)據(jù)流(傳感器、狀態(tài))
├── Service(服務) — 請求/響應模式,適合一次性指令(開關燈、查詢狀態(tài))
└── Action(動作) — 長時任務 + 進度反饋(導航到目標點、機械臂運動)
Action 是三者中最復雜、也最貼近真實機器人任務的,一定要掌握。

第二階段:坐標變換 TF2
這是 ROS2 中最核心的基礎設施之一,幾乎所有機器人功能都依賴它:

管理機器人各坐標系的空間關系(base_link → camera_link → map)
學會發(fā)布和查詢 TF 變換
理解靜態(tài) TF 和動態(tài) TF 的區(qū)別

沒有 TF2 的概念,后續(xù)做導航、感知都會寸步難行。

第三階段:仿真環(huán)境 Gazebo
在真實機器人上調(diào)試代價高、風險大,先在仿真里驗證:

搭建一個帶傳感器(激光雷達 / 攝像頭)的差速輪機器人
學會用 URDF / SDF 描述機器人模型
ROS2 + Gazebo Harmonic 的橋接(ros_gz_bridge)

第四階段:導航棧 Nav2
ROS2 導航的標準答案,也是檢驗前三階段是否掌握的綜合練習:
激光雷達數(shù)據(jù)

SLAM 建圖(slam_toolbox)

Nav2 路徑規(guī)劃 + 避障

/cmd_vel 速度指令 → 機器人底盤

第五階段:機械臂 MoveIt2(按需)
如果方向是操作臂/協(xié)作機器人:

運動學求解、碰撞檢測、軌跡規(guī)劃
與 ros2_control 對接真實硬件

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容