注:已从RoboMaster退队,懒得更新
第一章 ros2介绍
1.1 ros为什么会出现
做一个机器人是非常复杂的一件事情,因为一个机器人需要涉及到多个部分,而且这些部分之间还要进行通信。
例如设计一个像下图一样的轮式移动机器人,我们对其进行拆解。可以分为感知、决策、控制三个部分
- 感知部分有:激光雷达、深度相机、IMU、里程计、碰撞感知、建图
- 决策部分有:路径规划(navigation)算法、定位算法
- 控制部分有:轮子驱动
1.2 ros与ros2对比
上节课说到ROS的设计目标是简化机器人的开发,如何简化呢?ROS为此设计了一整套通信机制(话题、服务、参数、动作)。
通过这些通信机制,ROS实现了将机器人的各个组件给的连接起来,在设计这套通信机制的时候就设计了一个叫做Ros Master
的东西,所有节点(可以理解为某一个组件,比如:激光雷达)的通信建立必须经过这个主节点。
一旦Ros Master
主节点挂掉后,就会造成整个系统通信的异常,此时避障策略将会失效,如果机器人正在运行,碰到障碍物会径直装上去,机毁人亡!
除了不稳定这个问题,ROS还有很多其他地方存在着问题:
- 通信基于TCP实现,实时性差、系统开销大
- 对Python3支持不友好,需要重新编译
- 消息机制不兼容
- 没有加密机制、安全性不高
中间层:去中心化Ros Master,各个节点采用DDS通信,使得ROS2的实时性、可靠性和连续性上都有了增强。
第二章 准备环境与安装ROS2
2.1 安装虚拟机和Ubuntu
略
2.2安装ros
- Ubuntu20.04->foxy
- Ubuntu22.04->humble
- 鱼香肉丝一键安装ros2:
wget http://fishros.com/install -O fishros && bash fishros
2.3 VsCode编辑器安装与配置
- 下载:https://code.visualstudio.com/docs/?dv=linux64_deb
- 安装
sudo dpkg -i code_1.58.0-1625728071_amd64.deb
第三章 ROS2基础
3.1 节点介绍
节点与节点之间必须要通信,那他们之间该如何通信呢?ROS2早已为你准备好了一共四种通信方式:
- 话题-topics
- 服务-services
- 动作-Action
- 参数-parameters
启动节点ros2 run <package_name> <executable_name>
(指令意义:启动 包下的 中的节点。)
查看节点列表ros2 node list
查看节点信息ros2 node info <node_name>
3.2 ROS2工作空间与功能包
一个工作空间下可以有多个功能包,一个功能包可以有多个节点存在。
- 工作空间:工作空间是包含若干个功能包的目录,一开始大家把工作空间理解成一个文件夹就行了。
- 功能包:功能包可以理解为存放节点的地方,ROS2中功能包根据编译方式的不同分为三种类型。
- ament_python,适用于python程序
- cmake,适用于C++
- ament_cmake,适用于C++程序,是cmake的增强版
功能包的获取
- 安装获取:
sudo apt install ros-<version>-package_name
(安装获取会自动放置到系统目录,不用再次手动source。) - 手动编译获取:手动编译相对麻烦一些,需要下载源码然后进行编译生成相关文件。(手动编译之后,需要手动source工作空间的install目录。)
功能包的相关指令
- 创建功能包
ros2 pkg create <package-name> --build-type {cmake,ament_cmake,ament_python} --dependencies <依赖名字>Copy to clipboard
- 列出可执行文件
ros2 pkg executables
- 列出某个功能包
ros2 pkg executables turtlesim
- 列出所有包
ros2 pkg list
- 列出某个包所在路径的前缀
ros2 pkg prefix <package-name>
- 列出包的清单描述文件(每一个功能包都有一个标配的manifest.xml文件,用于记录这个包的名字,构建工具,编译信息,拥有者,干啥用的等信息。)
ros2 pkg xml turtlesim
3.3 ROS2的编译器Colcon
##小练习
#创建工作空间
mkdir -p turtle_ws/src
cd turtle_ws/src
#下载源码功能包到工作空间的src文件下
git clone https://github.com/fishros/turtle_battle.git
#进入工作空间上一级编译
cd ..
colcon build
#source空间
source install/setup.bash
#启动游戏节点
##打开一个新终端,进入turtle_ws工作空间,启动以下节点:
ros2 run turtle_battle turtle_spawner
#启动乌龟模拟器
##打开一个新终端,进入turtle_ws工作空间,启动以下节点:
ros2 run turtlesim turtlesim_node
#打开遥控器节点
##打开一个新终端,进入turtle_ws工作空间,启动以下节点:
ros2 run turtlesim turtle_teleop_key
创建工作空间和功能包
#创建工作空间
mkdir -p town_ws/src
cd town_ws/src
#创建一个功能包
ros2 pkg create village_li --build-type ament_python --dependencies rclpy
#创建节点文件
##在__init__.py同级别目录下创建一个叫做li4.py的文件
使用POP(面向过程编程)方法编写一个节点
#打开lib4.py
import rclpy
from rclpy.node import Node
def main(args=None):
"""
ros2运行该节点的入口函数
编写ROS2节点的一般步骤
1. 导入库文件
2. 初始化客户端库
3. 新建节点对象
4. spin循环节点
5. 关闭客户端库
"""
rclpy.init(args=args) # 初始化rclpy
node = Node("li4") # 新建一个节点
node.get_logger().info("大家好,我是作家li4.")
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
#修改setup.py
##增加一句话,告诉ros2村庄来了一位新村民李四,要找这位村民去village_li.li4:main路径下寻找。
entry_points={
'console_scripts': [
"li4_node = village_li.li4:main"
],
},
)
#编译,source
#运行节点
ros2 run village_li li4_node
对象 = 属性+行为
如果只需要实现一个很简单的功能,比如只是做一个键盘控制器,实现控制小车前进后退,直接采用面向过程的设计思想即可。但如果是做一个稍大的工程,且后续要考虑功能的拓展性,这个时候就需要采用面向对象的思路来了。
使用OOP(面向对象编程)方法编写一个节点
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
class WriterNode(Node):
"""
创建一个作家节点,并在初始化时输出一个话
"""
def __init__(self,name):
super().__init__(name)
self.get_logger().info("大家好,我是%s,我是一名作家!" % name)
def main(args=None):
"""
ros2运行该节点的入口函数
1. 导入库文件
2. 初始化客户端库
3. 新建节点
4. spin循环节点
5. 关闭客户端库
"""
rclpy.init(args=args) # 初始化rclpy
node = WriterNode("li4") # 新建一个节点
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
#修改setup.py
##增加一句话,告诉ros2村庄来了一位新村民李四,要找这位村民去village_li.li4:main路径下寻找。
entry_points={
'console_scripts': [
"li4_node = village_li.li4:main"
],
},
)
#编译,source
#运行节点
ros2 run village_li li4_node
POP使用C++编写节点并测试
#创建一个C++功能包
ros2 pkg create village_wang --build-type ament_cmake --dependencies rclcpp
#创建一个节点
##接着我们在village_wang/src下创建一个wang2.cpp文件
//POP
#include "rclcpp/rclcpp.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
/*产生一个Wang2的节点*/
auto node = std::make_shared<rclcpp::Node>("wang2");
// 打印一句自我介绍
RCLCPP_INFO(node->get_logger(), "大家好,我是单身狗wang2.");
/* 运行节点,并检测退出信号*/
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
//OOP
#include "rclcpp/rclcpp.hpp"
/*创建一个类节点,名字叫做SingleDogNode,继承自Node.*/
class SingleDogNode : public rclcpp::Node
{
public:
// 构造函数,有一个参数为节点名称
SingleDogNode(std::string name) : Node(name)
{
// 打印一句自我介绍
RCLCPP_INFO(this->get_logger(), "大家好,我是单身狗%s.",name.c_str());
}
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
/*产生一个Wang2的节点*/
auto node = std::make_shared<SingleDogNode>("wang2");
/* 运行节点,并检测退出信号*/
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
#添加到CmakeLists
##在CmakeLists.txt最后一行加入下面两行代码。(添加这两行代码的目的是让编译器编译wang2.cpp这个文件,不然不会主动编译。)
add_executable(wang2_node src/wang2.cpp)
ament_target_dependencies(wang2_node rclcpp)
#接着在上面两行代码下面添加下面的代码。(这个是C++比Python要麻烦的地方,需要手动将编译好的文件安装到install/village_wang/lib/village_wang下)
install(TARGETS
wang2_node
DESTINATION lib/${PROJECT_NAME}
)
第四章 通信机制-话题与服务
我们让王二(Node)通过订阅(Subscribe)李四(Node)发布(Publish)的《艳娘传奇》(Topic)来欣赏小说的。
4.1 话题
需要满足以下规则:
- 话题名字是关键,发布订阅接口类型要相同,发布的是字符串,接受也要用字符串来接收;
- 同一个人(节点)可以订阅多个话题,同时也可以发布多个话题,就像一本书的作者也可以是另外一本书的读者;
- 同一个小说不能有多个作者(版权问题),但跟小说不一样,同一个话题可以有多个发布者。
工具RQT之rqt_graph
ros2 run demo_nodes_py listener
ros2 run demo_nodes_cpp talker
rqt_graph
- ROS2作为一个强大的工具,在运行过程中,我们是可以通过命令来看到节点和节点之间的数据关系的。
ROS2话题相关命令行界面(CLI)
ros2 topic -h
返回系统中当前活动的所有主题的列表:ros2 topic list
接着上一条,增加消息类型:ros2 topic list -t
打印实时话题内容:ros2 topic echo /chatter
查看主题内容:ros2 topic info /chatter