ROS2核心基础
DDS:机器人的神经网络
常用通信模型

-
第一种,点对点模型,许多客户端连接到一个服务端,每次通信时,通信双方必须建立一条连接。当通信节点增多时,连接数也会增多。而且每个客户端都需要知道服务器的具体地址和所提供的服务,一旦服务器地址发生变化,所有客户端都会受到影响。
-
第二种,Broker模型,针对点对点模型进行了优化,由Broker集中处理所有人的请求,并进一步找到真正能响应该服务的角色。这样客户端就不用关心服务器的具体地址了。不过问题也很明显,Broker作为核心,它的处理速度会影响所有节点的效率,当系统规模增长到一定程度,Broker就会成为整个系统的性能瓶颈。更麻烦是,如果Broker发生异常,可能导致整个系统都无法正常运转。之前的ROS1系统,使用的就是类似这样的架构。
-
第三种,广播模型,所有节点都可以在通道上广播消息,并且节点都可以收到消息。这个模型解决了服务器地址的问题,而且通信双方也不用单独建立连接,但是广播通道上的消息太多了,所有节点都必须关心每条消息,其实很多是和自己没有关系的。
-
第四种,就是以数据为中心的DDS模型了,这种模型与广播模型有些类似,所有节点都可以在DataBus上发布和订阅消息。但它的先进之处在于,通信中包含了很多并行的通路,每个节点可以只关心自己感兴趣的消息,忽略不感兴趣的消息,有点像是一个旋转火锅,各种好吃的都在这个DataBus传送,我们只需要拿自己想吃的就行,其他的和我们没有关系。
DDS在自动驾驶中的应用

DDS(Data Distribution Service)是一种用于实时系统数据分发的中间件标准,由对象管理组织(OMG)制定并维护。它采用发布-订阅通信模型,专门针对需要高性能、低延迟数据传输的分布式系统而设计。以下是DDS的关键特性与技术细节:
-
核心架构
- 基于DCPS(Data-Centric Publish-Subscribe)模型
- 采用去中心化的对等网络架构,无需中央服务器
- 内置发现机制实现动态节点自动发现(如SPDP和SEDP协议)
-
服务质量(QoS)策略
- 提供22种可配置QoS策略,包括:
- 可靠性(RELIABILITY):可选择"尽力而为"或"可靠传输"
- 持久性(DURABILITY):支持历史数据缓存
- 截止时间(DEADLINE):确保数据时效性
- 资源限制(RESOURCE_LIMITS):防止内存溢出
- 提供22种可配置QoS策略,包括:
-
典型应用场景
- 工业物联网(IIoT):如西门子PLC设备间实时数据交换
- 自动驾驶系统:特斯拉Autopilot中传感器数据分发
- 金融交易系统:高频交易订单传输
- 军事系统:北约采用的DIS标准就基于DDS
-
实现版本比较
实现方案 开发者 特点 OpenDDS OCI 开源实现,支持C++/Java RTI Connext RTI 商业版,提供微秒级延迟 Fast DDS eProsima ROS2默认中间件,Apache许可 -
性能指标
- 典型延迟:<100μs(千兆网络环境下)
- 吞吐量:可达Gbps级
- 支持节点数:理论上无上限(实际受网络限制)
最新DDS规范(v1.4)新增了:
- 类型安全增强(TypeObject)
- 网络绑定规范(DDSI-RTPS)
- 安全扩展(DDS-Security)
DDS在ROS2中的地位

刚才我们也提到,DDS是一种通信的标准,就像4G、5G一样,既然是标准,那大家都可以按照这个标准来实现对应的功能,所以华为、高通都有很多5G的技术专利,DDS也是一样,能够按照DDS标准实现的通信系统很多,这里每一个红色模块,就是某一企业或组织实现的一种DDS系统。
既然可选用的DDS这么多,那我们该用哪一个呢?具体而言,他们肯定都符合基本标准,但还是会有性能上的差别,ROS2的原则就是尽量兼容,让用户根据使用场景选择,比如个人开发,我们选择一个开源版本的DDS就行,如果是工业应用,那可能得选择一个商业授权的版本了。
为了实现对多个DDS的兼容,ROS设计了一个Middleware中间件,也就是一个统一的标准,不管我们用那个DDS,保证上层编程使用的函数接口都是一样的。此时兼容性的问题就转移给了DDS厂商,如果他们想让自己的DDS系统进入ROS生态,就得按照ROS的接口标准,开发一个驱动,也就是这个部分。
ROS2中DDS的核心特性

这张图讲的就是 ROS2 底层 DDS 通信的 QoS(服务质量)规则,控制话题消息怎么发、怎么存、丢包怎么办,对应机械臂、机器人通信非常关键。
左边框图:整体模型
-
Data Domain(全局数据空间) 可以理解成一个大的 “消息共享广场”,所有节点都在这个广场交换话题数据。
-
参与者(Participant) 每一个 ROS2 程序节点,就是一个参与者。一个节点里面可以同时有:
- 数据写入器 DataWriter(发布方):往话题发消息
- 数据读取器 DataReader(订阅方):接收话题消息
注意:同一个节点,既能发布,也能订阅。
-
历史数据缓冲区(黄色高亮) 发布方本地会存一堆历史消息:
data1 data2 data3 data4,不是发完就扔掉。 网络不好的时候,订阅者晚来了、断连重连,可以补发缓存里的旧消息。 消息沿着线路:data4 → data3 → data2 → data1传给订阅者。 -
DEADLINE 周期 图下面双向箭头代表Deadline(截止时间)。约定:发布方必须在这个周期内至少发一次数据;超时不发,订阅方就判定对方异常。
例子:机械臂关节状态,要求 10ms 必须来一次消息,如果超过 20ms 没来,就判定传感器 / 发布节点挂掉。
每个读写器都带自己一套 QoS 配置,发布方、订阅方 QoS 必须兼容,否则连不上话题!
右边表格:四大核心 QoS 策略()
1. DEADLINE 截止时间
A DataWriter and a DataReader must update data at least once every deadline period.:约定好最大间隔多久必须来一条消息。 比如设置deadline=0.1s:发布者必须每 0.1 秒至少输出一次数据。超过这个时间还没有新消息,订阅端就收到 “发布方失联” 事件。 用途:监控传感器、关节状态、控制指令心跳。
2. HISTORY 历史策略 + depth(深度)
控制缓存里面存多少条历史消息。
KEEP_LAST:只存最新 N 条(depth=N),旧消息直接扔掉。机器人最常用。depth=1 就是只留最新 1 条。KEEP_ALL:全部历史消息都缓存下来。
depth 就是缓冲区最多存多少条。比如 depth=10,就存最近 10 条消息。 举例:网络卡顿,一下子收到一堆滞后的传感器数据,缓冲区决定到底给订阅者补发多少旧消息。
###3. RELIABILITY 可靠性策略
- BEST_EFFORT(尽力而为)
能发就发,网络差消息直接丢,不会重传。 适合高频传感器,比如摄像头图像,旧帧没用,丢就丢,要最新的。
- RELIABLE(可靠传输)
消息丢了就自动重传,保证订阅者一定收到全部消息。 适合控制指令、关节轨迹,不能丢,丢包会导致机械臂失控;代价是网络差的时候会有延迟。
###4. DURABILITY 持久性
新的订阅者 “迟到”,晚才启动,能不能收到订阅之前就已经发布过的旧消息。
VOLATILE:晚来的订阅拿不到老消息,只能接收连接之后的新消息。TRANSIENT_LOCAL:发布方本地缓存消息,后面新启动的订阅者,上来就能拿到之前发过的数据。
典型例子:机器人参数、地图,新节点启动不需要等下一轮发布,立刻拿到地图数据。
在命令行中配置DDS参数
先来试一试在命令行中配置DDS的参数。
启动第一个终端,我们使用best_effort创建一个发布者节点,循环发布任意数据,在另外一个终端中,如果我们使用reliable模型订阅同一话题,无法实现数据通信,如果修改为同样的best_effort,才能实现数据传输。
$ ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42" --qos-reliability best_effort
$ ros2 topic echo /chatter --qos-reliability reliable
$ ros2 topic echo /chatter --qos-reliability best_effort



如何去查看ROS2系统中每一个发布者或者订阅者的QoS策略呢,在topic命令后边跟一个"--verbose"参数就行了。
ros2 topic info /chatter --verbose

DDS编程示例

接下来我们尝试在代码中配置DDS,以之前Hello World话题通信为例
启动两个终端,分别运行发布者和订阅者节点:
$ ros2 run learning_qos qos_helloworld_pub
$ ros2 run learning_qos qos_helloworld_sub
可以看到两个终端中的通信效果如下,和之前貌似并没有太大区别。


看效果确实差不多,不过底层通信机理上可是有所不同的
发布者代码解析
在代码中,如果加入QoS的配置
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy # ROS2 QoS类
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
qos_profile = QoSProfile( # 创建一个QoS原则
# reliability=QoSReliabilityPolicy.BEST_EFFORT,
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1
)
self.pub = self.create_publisher(String, "chatter", qos_profile) # 创建发布者对象(消息类型、话题名、QoS原则)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = 'Hello World' # 填充消息对象中的消息数据
self.pub.publish(msg) # 发布话题消息
self.get_logger().info('Publishing: "%s"' % msg.data)# 输出日志信息,提示已经完成话题发布
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = PublisherNode("qos_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={
'console_scripts': [
'qos_helloworld_pub = learning_qos.qos_helloworld_pub:main',
},
订阅者代码解析
订阅者中的QoS配置和发布者类似。
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy # ROS2 QoS类
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
qos_profile = QoSProfile( # 创建一个QoS原则
# reliability=QoSReliabilityPolicy.BEST_EFFORT,
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1
)
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, qos_profile) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、QoS原则)
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = SubscriberNode("qos_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={
'console_scripts': [
'qos_helloworld_pub = learning_qos.qos_helloworld_pub:main',
'qos_helloworld_sub = learning_qos.qos_helloworld_sub:main',
],
},
Launch:多节点启动与配置脚本
运行ROS节点时,每次都要打开新终端输入命令确实很繁琐,尤其是当机器人系统包含多个节点时。那么有没有办法能一次性启动所有节点呢?
其实ROS提供了Launch启动文件功能,它可以轻松实现多节点的统一启动和配置。
Launch文件

这是一个完整的Launch文件,乍看上去,好像Python代码呀,没错,ROS2中的Launch文件就是基于Python描述的。
Launch的核心目的是启动节点,我们在命令行中输入的各种参数,在Launch文件中,通过类似这样的很多代码模版,也可以进行配置,甚至还可以使用Python原有的编程功能,大大丰富了启动过程中的多样化配置。
Launch文件在ROS系统中出现的频次相当之高,它就像粘合剂一样,可以自由组装和配置各个节点,那如何编写或者阅读一个Launch文件呢,我们通过一系列例程带领大家来了解。
源码分析
ROS2 launch.py 代码通俗解析 + Java 类比
ROS2 的
launch.py本质:用 Python 写配置,组装要启动的节点 / 子 launch 文件,最后一次性交给 ROS2 启动器运行,不是普通 Python 业务程序。
整体流程梳理
def generate_launch_description():
# 1. 定义路径、参数变量
# 2. 引入另一个launch文件(mbot.launch.py) → 类比Java调用别的方法
# 3. 引入Gazebo官方的launch文件,启动仿真环境
# 4. 定义一个Node:spawn_entity.py,把机器人模型生成到Gazebo世界里
# 5. 把上面全部组件打包返回,ROS2框架拿到返回值,执行启动
return LaunchDescription([mbot, gazebo, spawn_entity])
📌关键知识点:
generate_launch_description()不是我们自己调用,是 ROS2 launch 框架自动调用。 类比 Java:你写一个类,实现框架接口,框架反射调用你的方法拿到配置对象。
逐段拆解 + Java 类比
① 路径拼接部分
package_name='learning_gazebo'
world_file_path = 'worlds/neighborhood.world'
pkg_path = os.path.join(get_package_share_directory(package_name))
world_path = os.path.join(pkg_path, world_file_path)
get_package_share_directory("包名"):拿到 ROS 包的安装路径,相当于 Java 里读取资源文件,自动找share下资源,不用写死绝对路径。os.path.join():安全拼接文件路径,兼容 Linux 斜杠。
Java 类比
// 相当于从classpath找资源文件
String pkgPath = ResourceLoader.getPackagePath("learning_gazebo");
String worldPath = Paths.get(pkgPath, "worlds", "neighborhood.world").toString();
② 设置机器人生成位姿
spawn_x_val = '0.0'
spawn_y_val = '0.0'
spawn_z_val = '0.0'
spawn_yaw_val = '0.0'
定义机器人在 Gazebo 仿真世界的初始坐标、偏航角,后面传给 spawn 工具。
③ IncludeLaunchDescription —— 导入并启动另一个 launch 文件
mbot = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory(package_name),'launch','mbot.launch.py'
)]), launch_arguments={'use_sim_time': 'true', 'world':world_path}.items()
)
IncludeLaunchDescription:包含并运行另一个 launch 脚本mbot.launch.pyPythonLaunchDescriptionSource:指定要调用的 launch 文件路径launch_arguments:给被调用的 launch 传参数,use_sim_time:=true开启仿真时间,把 world 世界路径传过去。.items()把字典转成(key,value)元组列表,这里就是你之前遇到报错dictionary update sequence element #0 has length 1;2 is required的地方!传错格式直接崩溃。
Java 类比
// 调用另一个配置,传入参数,类似调用别的Service/方法
LaunchComponent mbot = launch.include("mbot.launch.py")
.addArg("use_sim_time","true")
.addArg("world",worldPath);
相当于:Java 中,主方法不去写全部逻辑,调用另外一个模块,同时给它传参数。
⚠️注意:
IncludeLaunchDescription只是描述对象,此时不会执行,只是登记,等最后返回给框架才会真正启动。
④ 导入 Gazebo 官方 launch
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]),
)
调用系统包gazebo_ros自带 launch,启动 Gazebo 仿真器 GUI、物理引擎。 类比 Java:调用第三方库提供的初始化方法,启动仿真环境。
⑤ Node 定义要运行的可执行程序 spawn_entity.py
spawn_entity = Node(package='gazebo_ros', executable='spawn_entity.py',
arguments=['-topic', 'robot_description',
'-entity', 'mbot',
'-x', spawn_x_val,
'-y', spawn_y_val,
'-z', spawn_z_val,
'-Y', spawn_yaw_val],
output='screen')
Node:声明一个 ROS 节点。package:属于哪个功能包executable:要运行的程序脚本spawn_entity.py
arguments:命令行参数。 逻辑:订阅robot_description话题拿到机器人 URDF 模型,在 Gazebo 世界生成实体,名字叫mbot,放在 x/y/z/yaw 坐标。output='screen':把程序打印日志输出到终端。
Java 类比
// 登记要启动的进程,类似Runtime.exec(),但只是声明,还没跑
LaunchComponent spawnEntity = launch.newNode()
.packageName("gazebo_ros")
.executable("spawn_entity.py")
.args("-topic","robot_description","-entity","mbot","-x","0.0")
.outputScreen();
⑥ 返回全部待启动组件
return LaunchDescription([
mbot,
gazebo,
spawn_entity,
])
把上面 3 个组件打包,返回LaunchDescription对象交给 ROS2 框架。
⚠️这里非常关键:整个脚本从头到尾只是组装 “启动计划”,没有任何东西真正运行! 只有框架拿到这个返回对象之后,才会依次:启动 mbot 子 launch、启动 Gazebo、运行 spawn_entity 节点。
Java 类比
// 返回全部待启动组件列表,框架拿到再调度执行
return new LaunchDescription(List.of(mbot,gazebo,spawnEntity));
就像 SpringBoot:你装配 Bean,不会在装配的时候执行业务逻辑,容器拿到配置后才实例化、启动。
多节点启动
启动终端,使用ros2中的launch命令来启动第一个launch文件示例:
ros2 launch learning_launch simple.launch.py
运行成功后,就可以在终端中看到发布者和订阅者两个节点的日志信息啦

文件解析
from launch import LaunchDescription # launch文件的描述类
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
return LaunchDescription([ # 返回launch文件的描述信息
Node( # 配置一个节点的启动
package='learning_topic', # 节点所在的功能包
executable='topic_helloworld_pub', # 节点的可执行文件
),
Node( # 配置一个节点的启动
package='learning_topic', # 节点所在的功能包
executable='topic_helloworld_sub', # 节点的可执行文件名
),
])
命令行参数配置
我们使用ros2命令在终端中启动节点时,还可以在命令后配置一些传入程序的参数,使用launch文件一样可以做到
运行效果
我们使用ros2命令在终端中启动节点时,还可以在命令后配置一些传入程序的参数,使用launch文件一样可以做到
ros2 run rviz2 rviz2 -d <PACKAGE-PATH>/rviz/turtle_rviz.rviz
命令后边还得跟一长串配置文件的路径,如果放在launch文件里,启动就优雅很多了:
ros2 launch learning_launch rviz.launch.py

文件解析
import os
from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法
from launch import LaunchDescription # launch文件的描述类
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
rviz_config = os.path.join( # 找到配置文件的完整路径
get_package_share_directory('learning_launch'),
'rviz',
'turtle_rviz.rviz'
)
return LaunchDescription([ # 返回launch文件的描述信息
Node( # 配置一个节点的启动
package='rviz2', # 节点所在的功能包
executable='rviz2', # 节点的可执行文件名
name='rviz2', # 对节点重新命名
arguments=['-d', rviz_config] # 加载命令行参数
)
])
资源重映射
ROS社区中的资源非常多,当我们使用别人代码的时候,经常会发现通信的话题名称不太符合我们的要求,能否对类似的资源重新命名呢?
为了提高软件的复用性,ROS提供了资源重映射的机制,可以帮助我们解决类似的问题。
运行效果
启动一个终端,运行如下例程,很快会看到出现了两个小海龟仿真器界面;再打开一个终端,发布如下话题,让海龟1动起来,海龟2也会一起运动:
$ ros2 launch learning_launch rviz.launch.py
$ ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"

文件解析
为什么两个海龟都会动呢?这里要用到turtlesim功能包里另外一个节点,叫做mimic,它的功能是订阅某一个海龟的Pose位置,通过计算,变换成一个同样运动的速度指令,发布出去。
至于mimic节点订阅或者发布的话题名叫什么呢?我们就可以通过重映射修改成对应任意海龟的名字。
from launch import LaunchDescription # launch文件的描述类
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
return LaunchDescription([ # 返回launch文件的描述信息
Node( # 配置一个节点的启动
package='turtlesim', # 节点所在的功能包
namespace='turtlesim1', # 节点所在的命名空间
executable='turtlesim_node', # 节点的可执行文件名
name='sim' # 对节点重新命名
),
Node( # 配置一个节点的启动
package='turtlesim', # 节点所在的功能包
namespace='turtlesim2', # 节点所在的命名空间
executable='turtlesim_node', # 节点的可执行文件名
name='sim' # 对节点重新命名
),
Node( # 配置一个节点的启动
package='turtlesim', # 节点所在的功能包
executable='mimic', # 节点的可执行文件名
name='mimic', # 对节点重新命名
remappings=[ # 资源重映射列表
('/input/pose', '/turtlesim1/turtle1/pose'), # 将/input/pose话题名修改为/turtlesim1/turtle1/pose
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'), # 将/output/cmd_vel话题名修改为/turtlesim2/turtle1/cmd_vel
]
)
])
ROS参数设置
ROS系统中的参数,也可以在Launch文件中设置。
运行效果
启动一个终端,运行如下命令:
$ ros2 launch learning_launch parameters.launch.py
在启动的海龟仿真器中,我们看到背景颜色被改变了,这个颜色参数的设置就是在launch文件中完成的。

文件解析
from launch import LaunchDescription # launch文件的描述类
from launch.actions import DeclareLaunchArgument # 声明launch文件内使用的Argument类
from launch.substitutions import LaunchConfiguration, TextSubstitution
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
background_r_launch_arg = DeclareLaunchArgument(
'background_r', default_value=TextSubstitution(text='0') # 创建一个Launch文件内参数(arg)background_r
)
background_g_launch_arg = DeclareLaunchArgument(
'background_g', default_value=TextSubstitution(text='84') # 创建一个Launch文件内参数(arg)background_g
)
background_b_launch_arg = DeclareLaunchArgument(
'background_b', default_value=TextSubstitution(text='122') # 创建一个Launch文件内参数(arg)background_b
)
return LaunchDescription([ # 返回launch文件的描述信息
background_r_launch_arg, # 调用以上创建的参数(arg)
background_g_launch_arg,
background_b_launch_arg,
Node( # 配置一个节点的启动
package='turtlesim',
executable='turtlesim_node', # 节点所在的功能包
name='sim', # 对节点重新命名
parameters=[{ # ROS参数列表
'background_r': LaunchConfiguration('background_r'), # 创建参数background_r
'background_g': LaunchConfiguration('background_g'), # 创建参数background_g
'background_b': LaunchConfiguration('background_b'), # 创建参数background_b
}]
),
])
加载参数文件
以上例程我们在launch文件中一个一个的设置参数,略显麻烦,当参数比较多的时候,建议使用参数文件进行加载。
import os
from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法
from launch import LaunchDescription # launch文件的描述类
from launch_ros.actions import Node # 节点启动的描述类
def generate_launch_description(): # 自动生成launch文件的函数
config = os.path.join( # 找到参数文件的完整路径
get_package_share_directory('learning_launch'),
'config',
'turtlesim.yaml'
)
return LaunchDescription([ # 返回launch文件的描述信息
Node( # 配置一个节点的启动
package='turtlesim', # 节点所在的功能包
executable='turtlesim_node', # 节点的可执行文件名
namespace='turtlesim2', # 节点所在的命名空间
name='sim', # 对节点重新命名
parameters=[config] # 加载参数文件
)
])
Launch文件包含
在复杂的机器人系统中,launch文件也会有很多,此时我们可以使用类似编程中的include机制,让launch文件互相包含。
import os
from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法
from launch import LaunchDescription # launch文件的描述类
from launch.actions import IncludeLaunchDescription # 节点启动的描述类
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import GroupAction # launch文件中的执行动作
from launch_ros.actions import PushRosNamespace # ROS命名空间配置
def generate_launch_description(): # 自动生成launch文件的函数
parameter_yaml = IncludeLaunchDescription( # 包含指定路径下的另外一个launch文件
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_launch'), 'launch'),
'/parameters_nonamespace.launch.py'])
)
parameter_yaml_with_namespace = GroupAction( # 对指定launch文件中启动的功能加上命名空间
actions=[
PushRosNamespace('turtlesim2'),
parameter_yaml]
)
return LaunchDescription([ # 返回launch文件的描述信息
parameter_yaml_with_namespace
])
功能包编译配置
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
(os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
(os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.*'))),
],
...
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)