注:本文根据古月居老师视频整理而成
目录
1. ROS2为何会替代ROS1?
- 要在资源有限的嵌入式系统中运行——>资源有限
- 要在有干扰的地方保证通信的可靠性——>易受到干扰
- 要做成产品走向市场——>成本昂贵,无法走向市场
2. ROS2的命令行工具——>基本与ROS1相同
1.ros2 topic
ros2 topic list
:显示所有话题ros2 topic info <话题名称>
:显示节点信息,包含消息类型、发布者数量、订阅数量ros2 topic echo <话题名称>
:打印某个话题中的消息数据——>相比于之前更加方便ros2 topic pub -rate 10 <话题> <消息> <tab补全>
:向某个话题循环发送10次消息——>跟ROS1相同
2. ros2 service call <service_name> <service_type> <service_data> # 发送服务请求
3.动作指令
ros2 action
4.录制
ros2 record <话题>
ros2 play <录制文件>
3. 工作空间
概念:实现机器人某项功能的所使用的文件夹。
- 工作空间内会存在四个子空间
src
:源码空间,源代码之类install
:安装空间,编译得到的可执行文件和脚本build
:编译的中间文件log
:日志空间
创建工作空间:自动初始化
$ mkdir -p ~/dev_ws/src #-p代表递归文件夹
$ cd ~/dev_ws/src
$ git clone https://gitee.com/guyuehome/ros2_21_tutorials.git #下载课程所用源码
自动安装各种依赖
#初次使用ROS2需要安装的依赖
$ sudo apt install -y python3-pip
$ sudo pip3 install rosdepc
$ sudo rosdepc init
$ rosdepc update
$ cd .. #返回主目录,cd ~/dev_ws
$ rosdepc install -i --from-path src --rosdistro humble -y #查看src空间中所需要的源码
编译工作空间
$ sudo apt install python3-colcon-ros #初次安装,colcon
#返回主目录进行编译
$ cd ~/dev_ws/
$ colcon build
设置环境变量
$ source install/local_setup.sh # 仅在当前终端生效
$ echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效
总结
4. 功能包
创建功能包
$ ros2 pkg create --build-type <build-type> <package_name>
$ cd ~/dev_ws/src
$ ros2 pkg create --build-type ament_cmake learning_pkg_c # C++
$ ros2 pkg create --build-type ament_python learning_pkg_python # Python
build-type
:创建类型- C/C++:ament_cmake
- python: ament_python
编译功能包:可以在功能包编写完成代码之后,再进行编译
$ cd ~/dev_ws/src
$ colcon build # 编译工作空间所有功能包
$ source install/local_setup.bash
5. 节点
职责
- 节点在机器人系统中的职责就是执行某些具体的任务
- 节点都是一个独立运行的可执行文件
- 节点可以在不同设备上,称之为分布式
- 每个节点都要有唯一的命名
节点流程:打开节点阀门->创建节点->实现节点->关闭节点阀门
注意
- 当修改功能包中代码后,需要重新编译运行——>(1)
colcon build
(2)source install/local_setup.bash
6.话题:节点间传输数据的桥梁
- 发布者和订阅者要统一数据的描述格式
- 消息是ROS中的一种接口定义方式
发布者代码解析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-发布“Hello World”话题
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.pub = self.create_publisher(String, "chatter", 10) # 创建发布者对象(消息类型、话题名、队列长度)
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("topic_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
订阅者代码解析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-订阅“Hello World”话题消息
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
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("topic_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
话题命令行常用操作
$ ros2 topic list # 查看话题列表
$ ros2 topic info <topic_name> # 查看话题信息
$ ros2 topic hz <topic_name> # 查看话题发布频率
$ ros2 topic bw <topic_name> # 查看话题传输带宽
$ ros2 topic echo <topic_name> # 查看话题内消息数据
$ ros2 topic pub <topic_name> <msg_type> <msg_data> # 发布话题消息
7.服务:你问我答的模式
- 如果服务器不存在,客户端会一直发送请求,直到服务器存在
客户端代码解析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-发送两个加数,请求加法器计算
"""
import sys
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.client = self.create_client(AddTwoInts, 'add_two_ints') # 创建服务客户端对象(服务接口类型,服务名)
while not self.client.wait_for_service(timeout_sec=1.0): # 循环等待服务器端成功启动
self.get_logger().info('service not available, waiting again...')
self.request = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self): # 创建一个发送服务请求的函数
self.request.a = int(sys.argv[1])
self.request.b = int(sys.argv[2])
self.future = self.client.call_async(self.request) # 异步方式发送服务请求
def main(args=None):
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderClient("service_adder_client") # 创建ROS2节点对象并进行初始化
node.send_request() # 发送服务请求
while rclpy.ok(): # ROS2系统正常运行
rclpy.spin_once(node) # 循环执行一次节点
if node.future.done(): # 数据是否处理完成
try:
response = node.future.result() # 接收服务器端的反馈数据
except Exception as e:
node.get_logger().info(
'Service call failed %r' % (e,))
else:
node.get_logger().info( # 将收到的反馈信息打印输出
'Result of add_two_ints: for %d + %d = %d' %
(node.request.a, node.request.b, response.sum))
break
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
服务器代码解析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-提供加法器的服务器处理功能
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_callback(self, request, response): # 创建回调函数,执行收到请求后对数据的处理
response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) # 输出日志信息,提示已经完成加法求和计算
return response # 反馈应答信息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderServer("service_adder_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
服务命令行操作
$ ros2 service list # 查看服务列表
$ ros2 service type <service_name> # 查看服务数据类型
$ ros2 service call <service_name> <service_type> <service_data> # 发送服务请求
8.总结
工作空间和功能包
-
工作空间和功能包创建过程
-
共同部分:当源码或其他地方发生变化时,需要重新编译和设置环境变量
- 编译:
clocon build
- 工作空间是在主目录下编译
- 功能包是在
src
空间下编译
- 设置环境变量:
source install/local_setup.bash
- 编译:
-
不同部分:
- 创建:
- 工作空间:
mkdir
——>创建文件夹即可 - 功能包(src文件夹下):
ros2 pkg --build-type <build-type> <build-name>
- C/C++:
ros2 pkg --build-type ament_cmake <pkg_name>
- Python:
ros2 pkg --build-type ament_python<pkg_name>
- C/C++:
- 工作空间:
- 安装依赖:工作空间特有
rosdepc install -i --from-path src --rosdistro humble -y
- 创建:
节点、话题、服务
-
总体流程:
- 实现功能部分:
- 话题:发布、订阅
- 服务:客户端、服务器
- 实现功能部分:
-
话题:发布——服务:客户端
-
共同部分:创建
-
创建发布者/客户端:一行代码,略
-
创建并填充话题消息/请求数据
-
创建话题消息/请求数据对象
-
向对象中填充数据
-
-
-
不同部分:
- 发布/发送
- 发布者:直接发送(一行代码),略
- 客户端:
- 选择发送方式,比如上文中的异步发送
- 发送
- 等待服务器响应
- 因为服务是实时的情况,所以需要不断的问答,等待服务器响应
- 发布/发送
-
-
话题:订阅——服务:服务端
- 共同部分:创建回调函数并处理数据/进行服务
- 回调函数:对接受到的数据进行处理
- 不同部分:
- 服务端需要将处理后的数据反馈回客户端
- 共同部分:创建回调函数并处理数据/进行服务