ROS1从入门到精通 2:ROS1核心概念详解(节点、话题、服务一网打尽)

【ROS1从入门到精通】第2篇:ROS1核心概念详解(节点、话题、服务一网打尽)

系列导航← 第1篇:ROS简介与环境搭建 | 系列目录 | 第3篇:创建工作空间与功能包 →

前置知识 :需要完成ROS Noetic环境搭建(第1篇

学习目标:通过本文,你将深入理解ROS的核心概念,掌握节点、话题、服务、参数的原理和使用方法,能够分析和设计ROS系统架构


一、引言

1.1 为什么需要理解ROS核心概念

想象你正在设计一个送餐机器人,需要协调多个功能模块:

  • 📷 视觉模块:识别障碍物和目标
  • 🗺️ 导航模块:规划路径
  • 🚗 控制模块:驱动电机
  • 🔊 交互模块:语音提示

如果采用传统方式,这些模块间的通信会形成复杂的网状结构,难以维护。而ROS通过节点(Node)话题(Topic)服务(Service)、**参数(Parameter)**等核心概念,优雅地解决了这个问题。

1.2 本文内容概览

  • 理论基础:ROS计算图与通信机制
  • 四大核心:节点、话题、服务、参数详解
  • 实战案例:构建温度监控系统
  • 最佳实践:如何选择合适的通信方式

二、理论基础:ROS计算图

2.1 什么是计算图(Computation Graph)

ROS将复杂的机器人系统抽象为计算图(Computation Graph),这是理解ROS的关键。

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#34495E', 'primaryTextColor':'#fff', 'primaryBorderColor':'#2C3E50', 'lineColor':'#7F8C8D' }}}%% graph LR subgraph ROS Master M[Master
名称服务] end subgraph 节点 A[相机节点] B[图像处理节点] C[显示节点] end subgraph 通信机制 T1[/camera/image
话题] T2[/processed/image
话题] S[/save_image
服务] end A -->|发布| T1 T1 -->|订阅| B B -->|发布| T2 T2 -->|订阅| C C -.->|调用| S B -.->|提供| S A -.->|注册| M B -.->|注册| M C -.->|注册| M

计算图的组成

  1. 节点(Nodes):执行运算的进程
  2. 消息(Messages):节点间传递的数据
  3. 话题(Topics):消息传递的通道
  4. 服务(Services):请求/响应通信
  5. 参数服务器(Parameter Server):共享配置
  6. ROS Master:名称注册和查找

2.2 ROS Master的作用

ROS Master是整个系统的管理者,负责:

  • 📝 名称注册:管理节点、话题、服务的名称
  • 🔍 查找服务:帮助节点找到彼此
  • 📊 参数服务器:存储和管理全局参数

重要:节点间的实际通信是**点对点(P2P)**的,不经过Master!


三、核心概念一:节点(Node)

3.1 节点的定义

节点(Node) 是ROS中的基本执行单元,每个节点都是一个独立的进程,负责特定的功能。

节点的特点

  • 🔹 模块化:每个节点完成特定任务
  • 🔹 可重用:节点可在不同项目中复用
  • 🔹 语言无关:支持Python、C++、Java等
  • 🔹 分布式:可运行在不同机器上

3.2 节点的生命周期

ros.init() 向Master注册 spin()/sleep() 处理回调 shutdown信号 清理资源 初始化 注册 运行 关闭

3.3 创建节点示例

Python节点示例

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
节点示例:简单的ROS节点
功能:演示节点的创建和生命周期
"""

import rospy

class SimpleNode:
    def __init__(self):
        """节点初始化"""
        # 1. 初始化ROS节点,节点名必须唯一
        rospy.init_node('simple_node', anonymous=True)

        # 2. 获取节点参数
        self.node_name = rospy.get_name()
        self.rate = rospy.Rate(1)  # 1Hz

        # 3. 打印节点信息
        rospy.loginfo(f"节点 {self.node_name} 已启动")

    def run(self):
        """节点主循环"""
        while not rospy.is_shutdown():
            # 执行节点任务
            rospy.loginfo(f"{self.node_name} 正在运行...")

            # 控制循环频率
            self.rate.sleep()

    def shutdown(self):
        """节点关闭时的清理"""
        rospy.loginfo(f"节点 {self.node_name} 正在关闭...")

if __name__ == '__main__':
    try:
        node = SimpleNode()
        # 注册关闭回调
        rospy.on_shutdown(node.shutdown)
        node.run()
    except rospy.ROSInterruptException:
        pass

C++节点示例

cpp 复制代码
/**
 * @file simple_node.cpp
 * @brief 简单的ROS C++节点示例
 */

#include <ros/ros.h>

class SimpleNode {
private:
    ros::NodeHandle nh_;           // 节点句柄
    ros::Timer timer_;              // 定时器
    std::string node_name_;         // 节点名称

public:
    SimpleNode() {
        // 获取节点名称
        node_name_ = ros::this_node::getName();

        // 创建定时器,每秒触发一次
        timer_ = nh_.createTimer(ros::Duration(1.0),
                                 &SimpleNode::timerCallback, this);

        ROS_INFO("节点 %s 已启动", node_name_.c_str());
    }

    void timerCallback(const ros::TimerEvent& event) {
        // 定时器回调函数
        ROS_INFO("%s 正在运行...", node_name_.c_str());
    }

    ~SimpleNode() {
        ROS_INFO("节点 %s 正在关闭...", node_name_.c_str());
    }
};

int main(int argc, char** argv) {
    // 初始化ROS节点
    ros::init(argc, argv, "simple_node");

    // 创建节点对象
    SimpleNode node;

    // 进入ROS事件循环
    ros::spin();

    return 0;
}

3.4 节点管理命令

bash 复制代码
# 查看运行中的节点
rosnode list

# 查看节点详细信息
rosnode info /node_name

# 测试节点连接
rosnode ping /node_name

# 清理无响应节点
rosnode cleanup

# 终止节点
rosnode kill /node_name

# 查看节点发布/订阅的话题
rostopic list -v

四、核心概念二:话题(Topic)

4.1 话题的定义

话题(Topic) 是节点间进行异步通信的机制,采用**发布/订阅(Publish/Subscribe)**模式。

话题通信特点

  • 📢 一对多通信:一个发布者,多个订阅者
  • 🔄 异步通信:发布者和订阅者解耦
  • 📊 数据流传输:适合连续数据流
  • ⏱️ 无需等待:发布者不等待订阅者

4.2 话题通信原理

发布者节点 ROS Master 订阅者1 订阅者2 1.注册为发布者(/topic) 2.注册为订阅者(/topic) 3.注册为订阅者(/topic) 4.通知发布者地址 5.通知发布者地址 6.建立TCP连接 7.建立TCP连接 8.发送消息(P2P) 9.发送消息(P2P) loop [数据传输] 发布者节点 ROS Master 订阅者1 订阅者2

4.3 发布者实现

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
话题发布者示例
功能:发布传感器数据到话题
"""

import rospy
from std_msgs.msg import Float32, Header
from sensor_msgs.msg import Temperature
import random

class TemperaturePublisher:
    def __init__(self):
        """初始化发布者"""
        rospy.init_node('temperature_publisher', anonymous=True)

        # 创建发布者
        # 话题名:/sensor/temperature
        # 消息类型:Temperature
        # 队列大小:10
        self.pub = rospy.Publisher('/sensor/temperature',
                                  Temperature,
                                  queue_size=10)

        # 设置发布频率
        self.rate = rospy.Rate(10)  # 10Hz

        # 初始温度
        self.base_temp = 25.0

        rospy.loginfo("温度发布者已启动")

    def publish_temperature(self):
        """发布温度数据"""
        while not rospy.is_shutdown():
            # 创建消息
            temp_msg = Temperature()

            # 填充消息头
            temp_msg.header.stamp = rospy.Time.now()
            temp_msg.header.frame_id = "temperature_sensor"

            # 模拟温度数据(25±5度随机波动)
            temp_msg.temperature = self.base_temp + random.uniform(-5, 5)

            # 温度方差
            temp_msg.variance = 0.01

            # 发布消息
            self.pub.publish(temp_msg)

            # 日志输出
            rospy.loginfo(f"发布温度: {temp_msg.temperature:.2f}°C")

            # 控制发布频率
            self.rate.sleep()

if __name__ == '__main__':
    try:
        publisher = TemperaturePublisher()
        publisher.publish_temperature()
    except rospy.ROSInterruptException:
        rospy.loginfo("温度发布者已停止")

4.4 订阅者实现

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
话题订阅者示例
功能:订阅并处理传感器数据
"""

import rospy
from sensor_msgs.msg import Temperature
import matplotlib.pyplot as plt
from collections import deque
import threading

class TemperatureSubscriber:
    def __init__(self):
        """初始化订阅者"""
        rospy.init_node('temperature_subscriber', anonymous=True)

        # 数据缓冲区(保存最近100个数据)
        self.temp_buffer = deque(maxlen=100)
        self.time_buffer = deque(maxlen=100)
        self.start_time = rospy.Time.now()

        # 温度阈值
        self.temp_threshold = 28.0
        self.alarm_triggered = False

        # 创建订阅者
        self.sub = rospy.Subscriber('/sensor/temperature',
                                    Temperature,
                                    self.temperature_callback,
                                    queue_size=10)

        rospy.loginfo("温度订阅者已启动")
        rospy.loginfo(f"温度警报阈值: {self.temp_threshold}°C")

    def temperature_callback(self, msg):
        """
        话题回调函数
        @param msg: Temperature消息
        """
        # 获取温度值
        temperature = msg.temperature
        current_time = (msg.header.stamp - self.start_time).to_sec()

        # 保存到缓冲区
        self.temp_buffer.append(temperature)
        self.time_buffer.append(current_time)

        # 温度警报检查
        if temperature > self.temp_threshold and not self.alarm_triggered:
            rospy.logwarn(f"⚠️ 温度警报!当前温度: {temperature:.2f}°C")
            self.alarm_triggered = True
        elif temperature <= self.temp_threshold and self.alarm_triggered:
            rospy.loginfo(f"✅ 温度恢复正常: {temperature:.2f}°C")
            self.alarm_triggered = False

        # 计算统计信息
        if len(self.temp_buffer) > 0:
            avg_temp = sum(self.temp_buffer) / len(self.temp_buffer)
            max_temp = max(self.temp_buffer)
            min_temp = min(self.temp_buffer)

            # 每10个数据打印一次统计
            if len(self.temp_buffer) % 10 == 0:
                rospy.loginfo(f"统计 - 平均: {avg_temp:.2f}°C, "
                            f"最高: {max_temp:.2f}°C, "
                            f"最低: {min_temp:.2f}°C")

    def spin(self):
        """保持节点运行"""
        rospy.spin()

if __name__ == '__main__':
    try:
        subscriber = TemperatureSubscriber()
        subscriber.spin()
    except rospy.ROSInterruptException:
        rospy.loginfo("温度订阅者已停止")

4.5 话题管理命令

bash 复制代码
# 列出所有话题
rostopic list

# 显示话题详细信息
rostopic info /sensor/temperature

# 打印话题消息
rostopic echo /sensor/temperature

# 显示话题发布频率
rostopic hz /sensor/temperature

# 显示话题带宽
rostopic bw /sensor/temperature

# 显示消息类型
rostopic type /sensor/temperature

# 手动发布消息
rostopic pub /test std_msgs/String "data: 'Hello ROS'"

# 查找话题
rostopic find sensor_msgs/Temperature

五、核心概念三:服务(Service)

5.1 服务的定义

服务(Service) 提供**同步的请求/响应(Request/Response)**通信机制,适合需要即时反馈的场景。

服务通信特点

  • 🤝 一对一通信:客户端请求,服务端响应
  • 同步通信:客户端等待响应
  • 📬 单次交互:适合偶发性操作
  • 确认机制:知道操作是否成功

5.2 服务vs话题的选择

场景 使用话题 使用服务
数据流 ✅ 连续传感器数据
状态更新 ✅ 机器人位置
命令执行 ✅ 启动/停止操作
参数查询 ✅ 获取配置信息
文件操作 ✅ 保存/加载文件
计算请求 ✅ 路径规划计算

5.3 服务定义文件(.srv)

首先创建服务定义文件 SetTemperature.srv

复制代码
# 请求部分
float32 target_temperature  # 目标温度
---
# 响应部分
bool success               # 是否成功
string message            # 返回消息
float32 actual_temperature # 实际设置的温度

5.4 服务端实现

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
服务端示例
功能:提供温度设置服务
"""

import rospy
from std_srvs.srv import Empty, EmptyResponse
from your_package.srv import SetTemperature, SetTemperatureResponse

class TemperatureService:
    def __init__(self):
        """初始化服务端"""
        rospy.init_node('temperature_service_server')

        # 当前温度
        self.current_temperature = 25.0
        self.min_temp = 10.0
        self.max_temp = 40.0

        # 创建服务
        # 服务1:设置温度
        self.set_temp_srv = rospy.Service('/set_temperature',
                                          SetTemperature,
                                          self.handle_set_temperature)

        # 服务2:重置温度
        self.reset_srv = rospy.Service('/reset_temperature',
                                       Empty,
                                       self.handle_reset_temperature)

        rospy.loginfo("温度服务已启动")
        rospy.loginfo(f"温度范围: {self.min_temp}°C - {self.max_temp}°C")

    def handle_set_temperature(self, req):
        """
        处理设置温度请求
        @param req: SetTemperatureRequest
        @return: SetTemperatureResponse
        """
        rospy.loginfo(f"收到温度设置请求: {req.target_temperature}°C")

        # 创建响应
        response = SetTemperatureResponse()

        # 验证温度范围
        if req.target_temperature < self.min_temp:
            response.success = False
            response.message = f"温度过低,最低温度为{self.min_temp}°C"
            response.actual_temperature = self.current_temperature

        elif req.target_temperature > self.max_temp:
            response.success = False
            response.message = f"温度过高,最高温度为{self.max_temp}°C"
            response.actual_temperature = self.current_temperature

        else:
            # 设置温度(模拟硬件操作)
            self.current_temperature = req.target_temperature
            response.success = True
            response.message = "温度设置成功"
            response.actual_temperature = self.current_temperature

            rospy.loginfo(f"温度已设置为: {self.current_temperature}°C")

        return response

    def handle_reset_temperature(self, req):
        """
        处理重置温度请求
        @param req: EmptyRequest
        @return: EmptyResponse
        """
        rospy.loginfo("收到温度重置请求")

        # 重置到默认温度
        self.current_temperature = 25.0
        rospy.loginfo(f"温度已重置为: {self.current_temperature}°C")

        return EmptyResponse()

    def spin(self):
        """保持服务运行"""
        rospy.loginfo("等待服务请求...")
        rospy.spin()

if __name__ == '__main__':
    try:
        service = TemperatureService()
        service.spin()
    except rospy.ROSInterruptException:
        rospy.loginfo("温度服务已停止")

5.5 客户端实现

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
服务客户端示例
功能:调用温度设置服务
"""

import rospy
import sys
from std_srvs.srv import Empty
from your_package.srv import SetTemperature

class TemperatureClient:
    def __init__(self):
        """初始化客户端"""
        rospy.init_node('temperature_service_client')

        # 等待服务可用
        rospy.loginfo("等待服务 /set_temperature...")
        rospy.wait_for_service('/set_temperature')
        rospy.loginfo("服务已就绪")

        # 创建服务代理
        self.set_temp_proxy = rospy.ServiceProxy('/set_temperature',
                                                 SetTemperature)
        self.reset_proxy = rospy.ServiceProxy('/reset_temperature',
                                              Empty)

    def set_temperature(self, temperature):
        """
        调用设置温度服务
        @param temperature: 目标温度
        @return: 服务响应
        """
        try:
            # 调用服务
            rospy.loginfo(f"请求设置温度: {temperature}°C")
            response = self.set_temp_proxy(temperature)

            # 处理响应
            if response.success:
                rospy.loginfo(f"✅ {response.message}")
                rospy.loginfo(f"当前温度: {response.actual_temperature}°C")
            else:
                rospy.logwarn(f"❌ {response.message}")

            return response

        except rospy.ServiceException as e:
            rospy.logerr(f"服务调用失败: {e}")
            return None

    def reset_temperature(self):
        """调用重置温度服务"""
        try:
            rospy.loginfo("请求重置温度...")
            self.reset_proxy()
            rospy.loginfo("✅ 温度已重置")

        except rospy.ServiceException as e:
            rospy.logerr(f"服务调用失败: {e}")

def main():
    """主函数"""
    client = TemperatureClient()

    # 测试设置不同温度
    test_temperatures = [20.0, 35.0, 5.0, 45.0, 25.0]

    for temp in test_temperatures:
        client.set_temperature(temp)
        rospy.sleep(1)  # 等待1秒

    # 重置温度
    client.reset_temperature()

if __name__ == '__main__':
    if len(sys.argv) == 2:
        # 从命令行参数获取温度
        try:
            temperature = float(sys.argv[1])
            client = TemperatureClient()
            client.set_temperature(temperature)
        except ValueError:
            rospy.logerr("请输入有效的温度值")
    else:
        main()

5.6 服务管理命令

bash 复制代码
# 列出所有服务
rosservice list

# 显示服务类型
rosservice type /set_temperature

# 显示服务参数
rosservice args /set_temperature

# 调用服务
rosservice call /set_temperature "target_temperature: 30.0"

# 查找服务
rosservice find your_package/SetTemperature

# 显示服务信息
rosservice info /set_temperature

六、核心概念四:参数服务器

6.1 参数服务器概述

参数服务器(Parameter Server) 是ROS Master提供的共享参数存储功能,用于存储配置信息。

参数特点

  • 🗄️ 全局共享:所有节点都可访问
  • 🔄 动态修改:运行时可以修改
  • 📁 层次结构:支持命名空间
  • 💾 持久化:可保存到文件

6.2 参数操作示例

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
参数服务器示例
功能:演示参数的设置和获取
"""

import rospy

class ParameterDemo:
    def __init__(self):
        """初始化节点并设置参数"""
        rospy.init_node('parameter_demo')

        # 1. 设置参数
        rospy.set_param('/robot/name', 'TurtleBot')
        rospy.set_param('/robot/max_speed', 1.5)
        rospy.set_param('/robot/sensors', ['lidar', 'camera', 'imu'])

        # 2. 设置私有参数(~开头)
        rospy.set_param('~debug_mode', True)

        # 3. 获取参数
        robot_name = rospy.get_param('/robot/name', 'default_robot')
        max_speed = rospy.get_param('/robot/max_speed', 1.0)

        # 4. 获取参数(带默认值)
        min_speed = rospy.get_param('/robot/min_speed', 0.1)

        # 5. 检查参数是否存在
        if rospy.has_param('/robot/sensors'):
            sensors = rospy.get_param('/robot/sensors')
            rospy.loginfo(f"传感器列表: {sensors}")

        # 6. 获取所有参数名
        param_names = rospy.get_param_names()
        rospy.loginfo(f"参数总数: {len(param_names)}")

        # 7. 删除参数
        if rospy.has_param('/robot/temp'):
            rospy.delete_param('/robot/temp')

        # 打印参数信息
        rospy.loginfo(f"机器人名称: {robot_name}")
        rospy.loginfo(f"最大速度: {max_speed} m/s")
        rospy.loginfo(f"最小速度: {min_speed} m/s")

if __name__ == '__main__':
    try:
        demo = ParameterDemo()
        rospy.spin()
    except rospy.ROSInterruptException:
        pass

6.3 参数管理命令

bash 复制代码
# 列出所有参数
rosparam list

# 获取参数值
rosparam get /robot/name
rosparam get /

# 设置参数
rosparam set /robot/max_speed 2.0

# 删除参数
rosparam delete /robot/temp

# 保存参数到文件
rosparam dump params.yaml

# 从文件加载参数
rosparam load params.yaml

# 在命名空间加载
rosparam load params.yaml /robot

七、实战案例:智能温控系统

7.1 项目需求

构建一个智能温控系统,包含:

  • 🌡️ 温度监测:实时发布温度数据
  • 📊 数据分析:分析温度趋势
  • 🎯 温度控制:提供温度设置服务
  • ⚠️ 警报系统:温度异常时发出警报

7.2 系统架构

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#34495E' }}}%% graph TB subgraph 传感器层 S1[温度传感器1] S2[温度传感器2] end subgraph 处理层 A[数据融合节点] B[分析节点] C[控制节点] end subgraph 应用层 D[显示节点] E[警报节点] end subgraph 通信机制 T1[/sensor1/temp
话题] T2[/sensor2/temp
话题] T3[/fusion/temp
话题] T4[/analysis/result
话题] SV1[/set_temp
服务] SV2[/get_status
服务] end S1 -->|发布| T1 S2 -->|发布| T2 T1 -->|订阅| A T2 -->|订阅| A A -->|发布| T3 T3 -->|订阅| B T3 -->|订阅| D B -->|发布| T4 T4 -->|订阅| E C -.->|提供| SV1 C -.->|提供| SV2

7.3 Launch文件配置

xml 复制代码
<launch>
  <!-- 参数配置 -->
  <rosparam file="$(find temp_control)/config/system_params.yaml" command="load"/>

  <!-- 温度传感器节点 -->
  <node name="temp_sensor1" pkg="temp_control" type="temperature_sensor.py">
    <param name="sensor_id" value="sensor_1"/>
    <param name="publish_rate" value="10"/>
  </node>

  <node name="temp_sensor2" pkg="temp_control" type="temperature_sensor.py">
    <param name="sensor_id" value="sensor_2"/>
    <param name="publish_rate" value="10"/>
  </node>

  <!-- 数据融合节点 -->
  <node name="data_fusion" pkg="temp_control" type="data_fusion.py"/>

  <!-- 分析节点 -->
  <node name="analyzer" pkg="temp_control" type="analyzer.py">
    <param name="window_size" value="100"/>
    <param name="threshold_high" value="30.0"/>
    <param name="threshold_low" value="15.0"/>
  </node>

  <!-- 控制服务节点 -->
  <node name="controller" pkg="temp_control" type="controller.py"/>

  <!-- 可视化节点(可选) -->
  <node name="visualizer" pkg="temp_control" type="visualizer.py"
        output="screen" if="$(arg show_viz)"/>
</launch>

八、常见问题与解决方案

Q1: 节点无法通信?

问题:节点已启动但无法收到消息

排查步骤

bash 复制代码
# 1. 检查Master是否运行
roscore

# 2. 检查节点是否注册
rosnode list

# 3. 检查话题连接
rostopic info /topic_name

# 4. 检查网络配置
echo $ROS_MASTER_URI
echo $ROS_IP

解决方案

  • 确保所有节点连接同一个Master
  • 检查防火墙设置
  • 确认话题名称一致
  • 验证消息类型匹配

Q2: 话题消息丢失?

问题:订阅者收不到所有消息

原因分析

  1. 队列溢出:发布速度>处理速度
  2. 网络延迟:分布式系统延迟
  3. 回调阻塞:回调函数执行太慢

解决方案

python 复制代码
# 增加队列大小
pub = rospy.Publisher('/topic', String, queue_size=100)
sub = rospy.Subscriber('/topic', String, callback, queue_size=100)

# 使用多线程回调
rospy.init_node('node', disable_signals=True)
spinner = rospy.AsyncSpinner(4)  # 4个线程
spinner.start()

Q3: 服务调用超时?

问题:服务调用长时间无响应

解决方案

python 复制代码
# 设置超时时间
try:
    rospy.wait_for_service('/service', timeout=5.0)
    proxy = rospy.ServiceProxy('/service', ServiceType)
    response = proxy(request)
except rospy.ROSException:
    rospy.logerr("服务调用超时")
except rospy.ServiceException as e:
    rospy.logerr(f"服务调用失败: {e}")

Q4: 参数更新不生效?

问题:修改参数后节点没有响应

解决方案

python 复制代码
# 方法1:定期检查参数
def check_params():
    rate = rospy.Rate(1)  # 1Hz
    while not rospy.is_shutdown():
        param_value = rospy.get_param('/param', default)
        # 处理参数变化
        rate.sleep()

# 方法2:使用动态参数(dynamic_reconfigure)
# 需要额外配置,参见第6篇教程

Q5: 多机通信配置?

问题:多台机器间的ROS通信

配置步骤

bash 复制代码
# 主机(运行roscore)
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.100
roscore

# 从机
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.101
rosrun package node

九、总结

9.1 本文要点回顾

  • 理解了ROS计算图:节点间的通信架构
  • 掌握了四大核心概念
    • 节点(Node):独立的执行单元
    • 话题(Topic):异步发布/订阅
    • 服务(Service):同步请求/响应
    • 参数(Parameter):全局配置管理
  • 学会了选择通信方式:根据场景选择合适的机制
  • 完成了温控系统案例:综合运用所学知识

9.2 核心概念对比

特性 话题(Topic) 服务(Service) 参数(Parameter)
通信模式 发布/订阅 请求/响应 存储/检索
通信类型 异步 同步 同步
数据流向 单向 双向 双向
连接关系 一对多 一对一 多对多
适用场景 连续数据流 偶发操作 配置管理
实时性
示例 传感器数据 启动/停止命令 机器人参数

9.3 最佳实践建议

  1. 节点设计

    • 单一职责原则
    • 合理的粒度划分
    • 避免节点过于庞大
  2. 话题使用

    • 合理设置queue_size
    • 注意消息频率
    • 避免消息过大
  3. 服务设计

    • 快速响应,避免阻塞
    • 复杂操作考虑异步
    • 提供明确的错误信息
  4. 参数管理

    • 使用配置文件
    • 合理的命名空间
    • 避免运行时频繁修改

9.4 下一步学习

9.5 实践建议

  1. ✅ 创建包含节点、话题、服务的完整项目
  2. ✅ 尝试不同的通信模式组合
  3. ✅ 使用rqt_graph可视化系统架构
  4. ✅ 测试多机分布式通信

十、参考资料


附录A:ROS通信机制速查表

节点相关

bash 复制代码
rosnode list              # 列出所有节点
rosnode info <node>       # 节点详细信息
rosnode ping <node>       # 测试节点连接
rosnode kill <node>       # 终止节点
rosnode cleanup           # 清理无响应节点

话题相关

bash 复制代码
rostopic list            # 列出所有话题
rostopic echo <topic>    # 打印话题消息
rostopic info <topic>    # 话题详细信息
rostopic type <topic>    # 显示消息类型
rostopic hz <topic>      # 显示发布频率
rostopic bw <topic>      # 显示带宽使用
rostopic pub <topic>     # 发布消息

服务相关

bash 复制代码
rosservice list          # 列出所有服务
rosservice call <srv>    # 调用服务
rosservice type <srv>    # 显示服务类型
rosservice find <type>   # 查找服务
rosservice info <srv>    # 服务详细信息
rosservice args <srv>    # 显示服务参数

参数相关

bash 复制代码
rosparam list            # 列出所有参数
rosparam get <param>     # 获取参数值
rosparam set <param>     # 设置参数值
rosparam delete <param>  # 删除参数
rosparam dump <file>     # 保存参数到文件
rosparam load <file>     # 从文件加载参数

附录B:消息类型参考

常用标准消息(std_msgs)

python 复制代码
# 基础类型
from std_msgs.msg import (
    Bool, Int8, Int16, Int32, Int64,
    Float32, Float64, String,
    Time, Duration, Header
)

# 数组类型
from std_msgs.msg import (
    Int32MultiArray, Float32MultiArray,
    ByteMultiArray
)

几何消息(geometry_msgs)

python 复制代码
from geometry_msgs.msg import (
    Point, Quaternion, Pose,
    Twist, Vector3, Transform,
    PoseStamped, TwistStamped
)

传感器消息(sensor_msgs)

python 复制代码
from sensor_msgs.msg import (
    Image, CompressedImage,
    LaserScan, PointCloud2,
    Imu, Temperature, Range,
    JointState
)

导航消息(nav_msgs)

python 复制代码
from nav_msgs.msg import (
    Odometry, Path,
    OccupancyGrid,
    MapMetaData
)

附录C:项目代码结构

复制代码
temp_control_system/
├── CMakeLists.txt
├── package.xml
├── launch/
│   └── system.launch
├── config/
│   └── system_params.yaml
├── msg/
│   └── TempStatus.msg
├── srv/
│   ├── SetTemperature.srv
│   └── GetStatus.srv
├── scripts/
│   ├── temperature_sensor.py
│   ├── data_fusion.py
│   ├── analyzer.py
│   ├── controller.py
│   └── visualizer.py
└── README.md

🎉 恭喜你掌握了ROS的核心概念!这是成为ROS专家的重要一步!

如果本文对你有帮助,请点赞👍收藏⭐支持一下!

有问题欢迎在评论区讨论,我会及时回复!

下一篇预告:《创建工作空间与功能包》,开始你的第一个ROS项目!


作者简介:机器人工程师,专注于ROS开发和机器人导航技术,曾参与多个机器人项目开发。

版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议。

相关推荐
世洋Blog16 小时前
装饰器模式实践:告别臃肿的继承链,优雅解耦初始化状态管理
unity·设计模式·c#·装饰器模式
feiduoge17 小时前
教程 41 - 增强纹理映射(采样器)
windows·游戏引擎·图形渲染
feiduoge18 小时前
教程 37 - 法线贴图
windows·游戏引擎·图形渲染
feiduoge19 小时前
教程 42 - 可写纹理
windows·游戏引擎·图形渲染
qq_4286396119 小时前
虚幻基础:mod制作流程
游戏引擎·虚幻
老朱佩琪!1 天前
Unity原型模式
开发语言·经验分享·unity·设计模式·原型模式
老朱佩琪!1 天前
Unity组合模式
unity·游戏引擎·组合模式
老朱佩琪!1 天前
Unity装饰器设计模式
unity·设计模式
向宇it1 天前
【unity游戏开发——网络】unity对接steam,并上传发布游戏版本——Steamworks.NET
游戏·unity·游戏引擎·.net·交互