ROS2高效学习第三章 -- 梳理 ros 编译工具,开始 ros2 编程,第一个 hello ros2 样例

梳理 ros 编译工具,开始 ros2 编程,第一个 hello ros2 样例

  • [1 背景和资料](#1 背景和资料)
  • [2 正文](#2 正文)
    • [2.1 ros 构建工具和构建系统梳理](#2.1 ros 构建工具和构建系统梳理)
    • [2.2 CI/CD 粗讲](#2.2 CI/CD 粗讲)
    • [2.3 创建工作空间以及配置 colcon](#2.3 创建工作空间以及配置 colcon)
    • [2.4 hello_ros2_cpp](#2.4 hello_ros2_cpp)
    • [2.5 hello_ros2_py](#2.5 hello_ros2_py)
  • 总结

1 背景和资料

从本文开始,我们学习 ros2 编程,每一个样例我们都会用 c++ 和 python 实现两次,感受两套语言实现的区别。本文我们重点介绍 ros 构建工具的发展历史,从而引入 colcon 和 ament 。然后分别实现 hello_ros2_cpp 和 hello_ros2_py,体验 ros2 的编译方式。至于如何搭建 ros2 环境,请参考本人上一篇文章 ROS2高效学习第二章 -- ros2常用命令和相关概念学习,熟练玩起来小乌龟样例 .

本文参考资料如下:

(1)A universal build tool

(2)Colcon-Tutorial

(3)Creating-A-Workspace

(4)Creating-Your-First-ROS2-Package

(5)古月 ros2_21_tutorials

(6)git 简单教程

(7)Conan tutorial

2 正文

2.1 ros 构建工具和构建系统梳理

(1)构建系统引入:学 Linux C/C++ 编程的人都知道,程序写出来,需要编程成二进制文件才能运行。C 语言的编译器就是 gcc ,C++ 的编译器就是 g++。如果你的程序只有一个 hello_world.cpp 文件,最简单的编译指令如下:

bash 复制代码
g++ -std=c++11 hello_world.cpp -o hello_world

但一个合格的程序员,不能只写 hello world 。稍微大一些的项目,就会有很多个 .cpp 和 .h 文件,他们彼此之间相互依赖。此时,把整个项目编译出一个可执行程序,这个过程就是构建。目前针对 C/C++ 程序,最常用的构建系统就是 cmake,其底层依赖 makefile,而最下层就是 gcc/g++。

(2)构建工具引入:对于 hello world 级别的程序,只需要一行编译指令就能解决构建问题。稍微大一点的项目,就需要构建系统了。如果项目再大一点,比如实现一个自动驾驶系统,就需要把整个系统分为很多个模块或软件包,分别进行开发。这些模块之间会彼此依赖,比如多个终端模块都依赖 log 日志模块。此时就需要引入构建工具,解决复杂系统内多模块的构建问题。

(3)构建工具和构建系统比较:

构建系统:构建系统的作用范围在单个模块或单个 ros 软件包,负责程序构建。

构建工具:构建工具的作用范围是整个大型系统或一组 ros 软件包,其根据依赖关系图,按照拓扑顺序调用每个包的特定构建系统,依次完成整个系统的构建任务。比如必须先编译 log 日志软件包,然后才能编译终端模块软件包。除了构建任务之外,构建工具还需要为程序运行设置环境变量,比如那个经典的 LD_LIBRARY_PATH 。

(4)ros 历代构建系统:ros1 时我们主要使用 cmake ,以及基于 cmake 的 catkin 构建系统。

c 复制代码
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs)

到 ros2 时,我们将使用 ament_cmake,其源于 catkin ,也基于 cmake 。针对 python 软件包 ,ros2 还引入了 setuptools ,这是 python 常用的打包工具。按理说 python 属于解释型语言,不需要编译,setuptools 只是打包工具,但我们仍把他作为一种构建系统。

(5)ros 历代构建工具:ros1 时我们主要使用 catkin_make 构建工具,其继承自 rosbuild 。

bash 复制代码
catkin_make --source src/hello_ros/

在 catkin_make 之后,ros1 还发布了 catkin_make_isolated ,catkin_tools 。到 ros2 ,ROS 官方先发布了 ament_tools ,后来又重新开发了 colcon (collective construction,集中构建)构建工具。

(6)为啥重新开发 colcon :ros 经过 ros1 ,ros2 两个大版本的迭代,积累了多款构建工具和构建系统,急需一款统一的构建工具,能同时解决多种版本(ros1,ros2),多种语言(c++ ,python),多种平台(linux,mac,windows)的构建问题。因此重新开发了 colcon。命名方式上也不再坚持构建工具与构建系统的统一,如 catkin和 catkin_make,ament 和 ament_tools,以此强调 colcon 能支持多种构建系统。colcon 也将是 ros 未来唯一的构建工具,更详细的信息可以参考 ros build_tool

(7)构建工具不支持的功能:ros 的构建工具不支持代码拉取功能;不支持安装包依赖项功能,依赖项需要自己在构建之前手动安装好;不支持创建二进制包功能,例如 Debian 包。

2.2 CI/CD 粗讲

(1)CI/CD 概念:Continuous Integration (持续集成),Continuous Deployment (持续部署),这是软件工程中非常重要的概念,也是一种软件开发实践。旨在帮助软件团队快速、可靠地交付他们的产品。

(2)CI/CD 粗讲:在 CI 这步,首先需要一个版本控制系统,当前最好用的就是 git( git 简单教程) 。围绕着 git ,一般会在主线分支上设置静态代码检查,用来检测每一笔提交的质量,比如命名规范等。还会设置自动化单元测试,看守代码功能,并进行代码覆盖率分析。

然后就需要一个工具,很类似 ros 的构建工具,能依次实现代码拉取,代码构建,二进制包管理(如C++ Conan,Conan tutorial)和二进制产物生成。

最后就是 CD,其自动化的把二进制产物部署到测试或生成环境,运行集成测试和冒烟测试。

(3)整个 CI 和 CD 过程几乎涉及软件工程的方方面面,这里推荐一本书:持续交付 发布可靠软件的系统方法,Jez Humble / David Farley

2.3 创建工作空间以及配置 colcon

(1)创建工作空间

bash 复制代码
mkdir -p ~/colcon_ws/src

(2)配置 colcon_cd :ros1 中有 roscd 命令,可以方便的进行软件包目录切换。到 ros2 ,对应的是 colcon_cd,但需要手动配置一下

bash 复制代码
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/humble/" >> ~/.bashrc

测试命令:

bash 复制代码
source ~/.bashrc
colcon_cd turtlesim

(3)colcon 有很多命令选项,用户最好设置下他的命令联想功能,方便使用:

bash 复制代码
echo "source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash" >> ~/.bashrc

2.4 hello_ros2_cpp

(1)创建 hello_ros2_cpp 软件包和相关文件

bash 复制代码
cd ~/colcon_ws/src
ros2 pkg create --build-type ament_cmake  --license Apache-2.0 hello_ros2_cpp
cd hello_ros2_cpp/src
touch hello_ros2.cpp

(2)编写 hello_ros2.cpp

c 复制代码
// rcl 是指 ROS Client Library 的缩写,它是 ROS 2 中的一个底层库。
// rcl 抽象了底层的中间件通信功能,为上层的客户端库提供统一的 API 接口。
// 在ros c++ 编程中,我们使用 rclcpp;ros python 编程中,我们使用 rclpy
#include "rclcpp/rclcpp.hpp"
// ros2 更推荐用户使用面向对象编程 OOP,
// 它提供了一种清晰且结构化的方式来组织和管理复杂的软件系统,
// 而机器人软件需要 OOP 的这些优点。
class HelloRos2 : public rclcpp::Node {
public:
    HelloRos2() : Node("hello_ros2") {
        RCLCPP_INFO(this->get_logger(), "start hello ros2 in cpp !!");
    }
    void run() {
        while(rclcpp::ok()) {
            RCLCPP_INFO(this->get_logger(), "hello ros2 in cpp !!");
            sleep(1);
        }
    }
};

int main(int argc, char* argv[]) {
    rclcpp::init(argc, argv);
    auto node = std::make_shared<HelloRos2>();
    node->run();
    rclcpp::shutdown();
    return 0;
}

(3)编写CmakeLists.txt

c 复制代码
cmake_minimum_required(VERSION 3.8)
project(hello_ros2_cpp)
find_package(ament_cmake REQUIRED)
// 下面四句是手动添加的,不可少
find_package(rclcpp REQUIRED)
add_executable(${PROJECT_NAME} src/hello_ros2.cpp)
ament_target_dependencies(${PROJECT_NAME} rclcpp)
install(TARGETS
  ${PROJECT_NAME}
  DESTINATION lib/${PROJECT_NAME})
ament_package()

(4)编译并运行

bash 复制代码
~/colcon_ws
colcon build --packages-select hello_ros2_cpp
source install/local_setup.bash
ros2 run hello_ros2_cpp hello_ros2_cpp

2.5 hello_ros2_py

(1)创建 hello_ros2_py 软件包和相关文件

bash 复制代码
cd ~/colcon_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 hello_ros2_py
cd hello_ros2_py/hello_ros2_py
touch hello_ros2.py

(2)编写 hello_ros2.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# ros python 编程使用rclpy,也推荐面向对象
import rclpy
from rclpy.node import Node
import time
class HelloRos2(Node):
    def __init__(self, name):
        super().__init__(name)
        self.get_logger().info("start hello_ros2_py node !!")
        
    def run(self):
        try:
            while rclpy.ok():
                self.get_logger().info("hello ros2 in python !!")
                time.sleep(1)
        except KeyboardInterrupt:
            self.get_logger().info("Node was interrupted, shutting down...")

def main(args=None):
    rclpy.init(args=args)
    node = HelloRos2("hello_ros2_py")
    try:
        node.run()
    except KeyboardInterrupt:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

(3)编写 setup.py

bash 复制代码
from setuptools import find_packages, setup
package_name = 'hello_ros2_py'
setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='ycao',
    maintainer_email='1641395022@qq.com',
    description='TODO: Package description',
    license='Apache-2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        	# 其他都是自动生成的,只有这里需要修改
        	# ros2 引入了 setuptools ,这里的setup.py 用来定义程序的入口
        	# 这里的格式请遵循:'my_node = my_py_pkg.my_node:main'
            'hello_ros2 = hello_ros2_py.hello_ros2:main'
        ],
    },
)

(4)编译并运行

bash 复制代码
~/colcon_ws
colcon build --packages-select hello_ros2_py
source install/local_setup.bash
ros2 run hello_ros2_py hello_ros2

(5)踩坑记录

第一,编译 hello_ros2_py 时,总是遇到:

txt 复制代码
--- stderr: hello_ros2_py
/usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
  warnings.warn(
---

解决:setuptools 必须使用 58 版本,参考:setuptools执行问题

bash 复制代码
pip install setuptools==58

第二,运行 hello_ros2_py 时,总是遇到:

txt 复制代码
ModuleNotFoundError: No module named 'hello_ros2_py'
[ros2run]: Process exited with failure 1

解决:下面这个文件不能删除,尽管他是空白的。

txt 复制代码
hello_ros2_py/hello_ros2_py/__init__.py

总结

这篇文章从想写到写完,将近一个月,进度实在令人抱歉。得益于媳妇的支持,最终在春节假期结束前赶出来了,完成后确实有一吐为快的感觉。本文样例托管在本人的 github 上:ros2_code

相关推荐
dengqingrui12341 分钟前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
我的心永远是冰冰哒1 小时前
ad.concat()学习
学习
ZZZ_O^O1 小时前
二分查找算法——寻找旋转排序数组中的最小值&点名
数据结构·c++·学习·算法·二叉树
slomay3 小时前
关于对比学习(简单整理
经验分享·深度学习·学习·机器学习
hengzhepa3 小时前
ElasticSearch备考 -- Async search
大数据·学习·elasticsearch·搜索引擎·es
小小洋洋5 小时前
BLE MESH学习1-基于沁恒CH582学习
学习
Ace'6 小时前
每日一题&&学习笔记
笔记·学习
IM_DALLA6 小时前
【Verilog学习日常】—牛客网刷题—Verilog进阶挑战—VL25
学习·fpga开发·verilog学习
丶Darling.6 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
z樾8 小时前
Github界面学习
学习