本文仅供学习使用
本文参考:
基础部分 :古月居-ROS2入门21讲
,鱼香ROS
节点需要存在于功能包当中、功能包需要存在于工作空间当中。所以我们要想创建节点,就要先创建一个工作空间,再创建功能包。
工作空间就是文件夹,所以很简单。
cd d2lros2/chapt2/
mkdir -p chapt2_ws/src/
创建example_cpp
功能包,使用ament-cmake
作为编译类型,并为其添加rclcpp
依赖。
cd chapt2_ws/src
ros2 pkg create example_cpp --build-type ament_cmake --dependencies rclcpp
pkg create
是创建包的意思
--build-type
用来指定该包的编译类型,一共有三个可选项ament_python
、ament_cmake
、cmake
--dependencies
指的是这个功能包的依赖,这里给了一个ros2的C++客户端接口rclcpp
接着我们在example_cpp/src
下创建一个node_01.cpp
文件:
#include "rclcpp/rclcpp.hpp"
int main(int argc, char **argv)
{
/* 初始化rclcpp */
rclcpp::init(argc, argv);
/*产生一个node_01的节点*/
auto node = std::make_shared<rclcpp::Node>("node_01");
// 打印一句自我介绍
RCLCPP_INFO(node->get_logger(), "node_01节点已经启动.");
/* 运行节点,并检测退出信号 Ctrl+C*/
rclcpp::spin(node);
/* 停止运行 */
rclcpp::shutdown();
return 0;
}
在node_01.cpp
中输入上面的内容后,还需要修改一下CMakeLists.txt
。将其添加为可执行文件,并使用install
指令将其安装到install
目录。
在CmakeLists.txt最后一行加入下面两行代码。
add_executable(node_01 src/node_01.cpp)
ament_target_dependencies(node_01 rclcpp)
添加这两行代码的目的是让编译器编译node_01
这个文件,接着在上面两行代码下面添加下面的代码。
install(TARGETS
node_01
DESTINATION lib/${PROJECT_NAME}
)
在chapt2_ws
下依次输入下面的命令
colcon build
source install/setup.bash
ros2 run example_cpp node_01
当节点运行起来后,可以再尝试使用ros2 node list
指令来查看现有的节点。这个时候你应该能看到:/node_01
C++版本的ros2节点,但是这仅仅是编写ROS2节点方式之一,相比之下,更推荐使用面向对象的方式编写节点
又出问题了
创建一个名字叫做example_py python
版本的功能包。
cd chapt2/chapt2_ws/
ros2 pkg create example_py --build-type ament_python --dependencies rclpy
编写ROS2节点的一般步骤
- 导入库文件
- 初始化客户端库
- 新建节点
- spin循环节点
- 关闭客户端库
在chapt2_ws/example_py/example_py
下创建node_02.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("node_02") # 新建一个节点
node.get_logger().info("大家好,我是node_02.")
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
代码编写完成用Crtl+S进行保存。接着修改setup.py
entry_points={
'console_scripts': [
"node_02 = example_py.node_02:main"
],
},
)
setup.py
这段配置是声明一个ROS2的节点,声明后使用colcon build
才能检测到,从而将其添加到install
目录下。
完成上面的工作后,就可以编译运行了。
打开vscode终端,进入chapt2/chapt2_ws/
colcon build
source install/setup.bash
ros2 run example_py node_02
当节点运行起来后,可以再尝试使用ros2 node list在这里插入代码片
指令来查看现有的节点。/node_02
除了使用上节中的只定义一个函数就完成编写一个Python节点外,还有另外两种方式。
要做机器人离不开计算机编程,而计算机编程经过多年的发展,演变出了三种不同且常用的编程思想,分别是:
在阅读机器人相关开源程序代码时,比如导航框架Nav2、机械臂运动控制框架Moveit时发现,别人的代码,每一行好像都看得懂,但放一起就看不懂了,看别人函数调来调去,很快人就给整蒙了。不知道如何下手。
这其实就是对别人的编程思想不了解造成的,所以提一提常见的三种编程思想,让大家脑子里有个概念,以后遇到了看不明白的程序,知道该往哪个方向去学习。
编程是为了什么?——编程思想就是解决问题的思路
想把一只大象装进冰箱,分别用三种思想,我们看看有什么不一样。
1. 打开冰箱门; 2. 把大象塞进去; 3. 关上冰箱门;
1. 调用:冰箱->打开门(行为) ; 2. 调用:冰箱->装东西(行为); 3. 调用:冰箱->关闭门(行为)
面向对象中有五个重要的概念,理解这五个概念相当于对OOP编程有了了解
我们通过调用你家美的冰箱的开门、装东西和关门三个行为来把大象装进冰箱。这时我们可以把你家的美的冰箱(具体的)称之为一个对象,而冰箱(抽象的)就称为一个类。
在ROS2设计时这种抽象和具体的思想发挥着非常重要的作用,比如说DDS是有很多厂家的,ROS2为了匹配不同厂家的DDS,就设计了DDS抽象层,而每一个具体的DDS厂家,我们可以称之为一个DDS的对象,是具体的。
所谓封装就是将属性和行为封装在一起。上面已经介绍了对象 = 属性+行为,比如冰箱将冰箱的温度值(属性)和对温度值的操作(行为)等封装在一起。
继承,继承可以帮我们减少很多的工作量,比如ROS2中的执行器类,通过继承执行器类实现了单线程执行器和多线程执行器,更多具体的例子我们在后续的学习中遇到再说。
多态,其实很简单,我们可以说鲤鱼是鱼类,草鱼是鱼类,鲤鱼是鱼类。同一个鱼类可以有多种不同的类型,即多态。 更多的用法,等到写代码的时候再和小鱼一起解锁
根据你的功能需求来,如果只需要实现一个很简单的功能,比如只是做一个键盘控制器,实现控制小车前进后退,直接采用面向过程的设计思想即可。
但如果是做一个稍大的工程,且后续要考虑功能的拓展性,这个时候就需要采用面向对象的思路来了。
在d2lros2/chapt2/chapt2_ws/src/example_cpp/src
下新建node_03.cpp
,接着输入下面的代码。
#include "rclcpp/rclcpp.hpp"
/*
创建一个类节点,名字叫做Node03,继承自Node.
*/
class Node03 : public rclcpp::Node
{
public:
// 构造函数,有一个参数为节点名称
Node03(std::string name) : Node(name)
{
// 打印一句
RCLCPP_INFO(this->get_logger(), "大家好,我是%s.",name.c_str());
}
private:
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
/*产生一个node_03的节点*/
auto node = std::make_shared<Node03>("node_03");
/* 运行节点,并检测退出信号*/
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
接着修改CMakeLists.txt
,添加下方代码。
add_executable(node_03 src/node_03.cpp)
ament_target_dependencies(node_03 rclcpp)
install(TARGETS
node_03
DESTINATION lib/${PROJECT_NAME}
)
接着即可自行编译测试
colcon build --packages-select example_cpp
source install/setup.bash
ros2 run example_cpp node_03
在d2lros2/d2lros2/chapt2/chapt2_ws/example_py/example_py
下新建node_04.py
,输入下面的代码
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
class Node04(Node):
"""
创建一个Node04节点,并在初始化时输出一个话
"""
def __init__(self,name):
super().__init__(name)
self.get_logger().info("大家好,我是%s!" % name)
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
node = Node04("node_04") # 新建一个节点
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
接着修改setup.py
entry_points={
'console_scripts': [
"node_02 = example_py.node_02:main",
"node_04 = example_py.node_04:main"
],
},
把节点写成一个类的形式对我们组织代码和使用ROS2的新特性有很多的好处
使用CMake(GCC或Makefile)和 Python Setup打包工具依然可以完成ROS2代码的编译,那为什么还需要Colcon呢?
构建系统与构建工具 的区分点在于针对的对象不同,构建系统只针对一个单独的包进行构建,而构建工具重点在于按照依赖关系依次调用构建系统完成一系列功能包的构建。
ROS中用到的构建系统:
CMake
、ament_cmake
、catkin
、Python setuptools
。
ROS中用到的构建工具:colcon
、catkin_make
、catkin_make_isolated
、catkin_tools
。
很明显colcon
作为构建工具,通过调用(构建系统)CMake
、Python setuptools
完成构建。
cmake
、make
、make intsall
setuptools
是Python包的打包常用工具。Python 包使用文件来描述依赖项,以及如何构建和安装内容。在ROS2中,功能包可以是“普通”Python包,而在ROS1中,任何Python功能都是从CMake文件触发setup.py
进行打包。catkin
基于CMake,并提供了一组方便的函数,使编写CMake包更容易。它自动生成 CMake 配置文件以及 pkg
配置文件。它还提供了注册不同类型测试的函数。colcon
是一个命令行工具,用于改进构建,测试和使用多个软件包的工作流程。它自动化了流程,处理了订购并设置了使用软件包的环境。ament_tools
由用于构建 ROS 2 包的独立 Python 3 包提供。它是为引导ROS 2项目而开发的,因此仅针对Python 3,并且可以在Linux,MacOS和Windows上运行。package.xml
文件的 ROS 2 包。package.xml
普通的 CMake 包。package.xml
文件的 Python 包。setup.py
文件中提取包名称和依赖项)。我们平时用的最多的场景是编译功能包,所以这里重点介绍build
时候的一些参数。
--packages-select
,仅生成单个包(或选定的包)。--packages-up-to
,构建选定的包,包括其依赖项。--packages-above
,整个工作区,然后对其中一个包进行了更改。此指令将重构此包以及(递归地)依赖于此包的所有包。--build-base
参数和--install-base
,指定构建目录和安装目录。--merge-install
,使用 作为所有软件包的安装前缀,而不是安装基中的软件包特定子目录--install-base
--symlink-install
后将不会把文拷贝到install
目录,而是通过创建符号链接的方式。--continue-on-error
,当发生错误的时候继续进行编译。--cmake-args
,将任意参数传递给CMake。与其他选项匹配的参数必须以空格为前缀。--executor EXECUTOR
,用于处理所有作业的执行程序。默认值是根据所有可用执行程序扩展的优先级选择的。要查看完整列表,请调用 colcon extensions colcon_core.executor --verbose
sequential [colcon-core]
一次处理一个包。parallel [colcon-parallel-executor]
处理多个作业平行--parallel-workers NUMBER
要并行处理的最大作业数。默认值为 os.cpu_count()
给出的逻辑 CPU 内核数。--log-level
可以设置日志级别,比如--log-level info
。