[足式机器人]Part5 ROS2学习笔记-CH00-1-基础入门部分

本文仅供学习使用
本文参考:
基础部分 : 古月居-ROS2入门21讲 , 鱼香ROS

ROS2学习笔记-Ch00 - 基础入门部分part1


1. 前言

ROS2使用:Humble版本 + Linux系统(22.04)

1.1 Linux 使用

1.1.1 更改分辨率

在桌面空白处右击,选择Display Settings(显示设置)——修改Resolution(分辨率)——右上角点Apply(应用)

1.1.2 常用快捷键

Ctrl + Alt + T:打开终端窗口。
复制文本:Ctrl+Shift+C
粘贴文本:Ctrl+Shift+V

Ctrl + C:终止当前运行的命令。

Ctrl + D:退出当前的终端会话。
Ctrl + A:将光标移动到行首。
Ctrl + E:将光标移动到行尾。

1.1.3 小鱼做的一键安装ROS等常用指令合集


 wget http://fishros.com/install -O fishros && . fishros

1.1.4 安装软件

更换掉系统源为国内镜像源 —— 5

安装指令 :sudo apt install git , 指令详解:
apt指令是Ubuntu中的包管理工具,可以用于安装、卸载、升级软件包。
apt前加上sudo,表示使用以管理员(root)权限执行apt指令。
apt后的install代表安装
install后的git是软件名字

1.1.5 常用指令

  • ls命令: list 的缩写,通过 ls 命令不仅可以查看 linux 文件夹包含的文件,而且可以查看文件权限(包括目录、文件夹、文件权限)查看目录信息等
    ls -a 列出目录所有文件,包含以.开始的隐藏文件
    ls -l 除了文件名之外,还将文件的权限、所有者、文件大小等信息详细列出来

  • cd 命令: cd(changeDirectory) 命令语法
    cd / 进入要目录
    cd ~ 进入 “home” 目录

  • pwd 命令: 用于查看当前工作目录路径。
    pwd查看当前路径
    pwd -P查看软链接的实际路径

  • mkdir 命令: 用于创建文件夹
    mkdir t当前工作目录下创建名为 t 的文件夹
    mkdir -p /tmp/test/t1/t在 tmp 目录下创建路径为 test/t1/t 的目录,若不存在,则创建(-p: 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不在的目录,即一次可以建立多个目录。-m: 对新建目录设置存取权限,也可以用 chmod 命令设置)

  • rm 命令: 删除一个目录中的一个或多个文件或目录,如果没有使用 -r 选项,则 rm 不会删除目录。如果使用 rm 来删除文件,通常仍可以将该文件恢复原状。rm [选项] 文件…
    rm -i *.log删除任何 .log 文件,删除前逐一询问确认
    rm -rf test删除 test 子目录及子目录中所有档案删除,并且不用一一确认
    rm -- -f*删除以 -f 开头的文件

  • mv命令: 移动文件或修改文件名,根据第二参数类型(如目录,则移动文件;如为文件则重命令该文件)。当第二个参数为目录时,第一个参数可以是多个以空格分隔的文件或目录,然后移动第一个参数指定的多个文件到第二个参数指定的目录中。
    mv test.log test1.txt将文件 test.log 重命名为 test1.txt
    mv log1.txt log2.txt log3.txt /test3将文件 log1.txt,log2.txt,log3.txt 移动到根的 test3 目录中
    mv -i log1.txt log2.txt将文件 file1 改名为 file2,如果 file2 已经存在,则询问是否覆盖
    mv * ../移动当前文件夹下的所有文件到上一级目录

  • cp 命令: 将源文件复制至目标文件,或将多个源文件复制至目标目录。
    cp a.txt b.txt复制 a.txt 到 test 目录下,保持原文件时间,如果原文件存在提示是否覆盖。

1.2 编程调用

1.2.1 Python

  1. 打开终端,创建一个d2lros2文件夹接着在文件夹下创建chapt1文件夹。
mkdir d2lros2
cd d2lros2
mkdir chapt1
  1. 接着输入下面的指令打开gedit并创建文件
gedit hello_fishros.py
  1. 接着你应该看到一个类似于记事本的东西,在里面输入下面一段代码,打印一句话
print("Hello FishRos!")
  1. 使用Ctrl+S保存代码
  2. 关掉gedit,接着在终端输入下面的指令运行这段脚本
python3 hello_fishros.py

1.2.2 C++

  1. 进入终端,用gedit再次编辑一个叫做hello_fishros.cpp的文件。(如果还是在当前目录d2lros2/chapt1/,则无效-'bash: cd: d2lros2/chapt1/: 没有那个文件或目录)
cd d2lros2/chapt1/
gedit hello_fishros.cpp
  1. gedit中输入下面的内容
#include"iostream"

int main(){
    std::cout<<"Hello FishRos!"<<std::endl;
    return 0;
}
  1. 保存代码,关闭gedit。
  2. 终端输入下面的指令对刚刚的代码进行编译
g++ hello_fishros.cpp
  1. 使用ls指令,应该可以看到一个叫做a.out的文件
  2. 输入下面的指令即可运行
./a.out

1.2.3 手动安装g++

C++代码必须经过编译构建之后才能运行。C++的编译工具使用的是g++,默认是不安装的

sudo apt install g++

1.3 常用软件

1.3.1 VsCode

左边是文件目录,Ctrl+B即可打开隐藏侧边栏。
中间是编辑器
下面可以显示终端,Ctrl+Shift+~即可打开终端

常用的是Python和C++插件

1.3.2 搜狗拼音

根据上述命令,查询官网学会安装软件吧!

1.3.3 QQ与微信

根据上述命令,查询官网学会安装软件吧!

1.4 ROS介绍

ROS(Robot Operating System)机器人操作系统,但ROS本身并不是一个操作系统,而是可以安装在现在已有的操作系统上(Linux、Windows、Mac)上的软件库工具集

机器人,我们对其进行拆解。可以分为感知(激光雷达、深度相机、IMU、里程计、碰撞感知、建图)、决策(路径规划(navigation)算法、定位算法)、控制(轮子驱动)三个部分。

ROS为此设计了一整套通信机制(话题、服务、参数、动作)。

1.4.1 核心概念

工作空间(Woekspace):开发过程的大本营
功能包(Package):功能源码的聚集地
节点(Node):机器人的工作细胞
话题(Topic): 节点间传递数据的桥梁
服务(Service):节点间的你问我答
通信接口(Interface):数据传递的标准结构
参数(Parameter):机器人系统的全局字典
动作(Action):完整行为的流程管理
分布式通信(Distributed Communication):多计算平台的任务分配
DDS(Date Distribution Service):机器人的神经网络(Data Distribution Service 数据分发服务)

1.4.2 安装ROS2

wget http://fishros.com/install -O fishros && . fishros

1.4.3 ROS2卸载

sudo apt remove ros-humble-*
sudo apt autoremove

1.4.4 ROS2初体验

  • 你听我说:启动两个节点,一个节点负责发消息(说),一个节点负责收消息(听)
  1. 启动一个终端Ctrl+Alt+T
  2. 启动倾听者
ros2 run demo_nodes_py listener
  1. 启动一个新终端Ctrl+Alt+T
  2. 启动说话者
ros2 run demo_nodes_cpp talker

观察一下现象,talker节点每数一个输,倾听节点每一次都能听到一个

  • 涂鸦乌龟:启动海龟模拟器,启动海龟遥控器,控制海龟在地图上画出任意轨迹
  1. 打开终端Ctrl+Alt+T
  2. 输入下面的指令
ros2 run turtlesim turtlesim_node
  1. 点一下原来的终端输入Ctrl+Shift+T
  2. 打开一个新的标签页输入
ros2 run turtlesim turtle_teleop_key

这个时候你就可以使用上下左右(方向键)去遥控海龟了

  • RQT可视化
  1. 保持前面两个游戏在运行状态,打新的终端,输入rqt
  2. Plugins——Introspection——Node Graph

2. 编程基础

2.1 使用gcc编译ROS2节点

2.1.1 动态链接库

程序编译一般需要经预处理、编译、汇编和链接几个步骤。在实际应用中,有些公共代码需要反复使用,就把这些代码编译成为 “库”文件

在链接步骤中,链接器将从库文件取得所需的代码,复制到生成的可执行文件中,这种库称为静态(链接)库,其特点是可执行文件中包含了库代码的一份完整拷贝,缺点是被多次使用就会多份冗余拷贝。——(编写子函数)

还有一种库,就是程序在开始运行后调用库函数时才被载入,这种库独立于现有的程序,其本身不可执行,但包含着程序需要调用的一些函数,这种库称为动态(链接)库(Dynamic Link Library)——(调用函数包)

在widows平台下,静态链接库是.lib文件,动态库文件是.dll文件。在linux平台下,静态链接库是.a文件,动态链接库是.so文件。

2.1.2 用g++编译ROS2的C++节点

编写节点:打开终端,创建chapt2/basic目录,用VSCODE打开d2lros2目录

mkdir -p d2lros2/chapt2/basic/
code d2lros2

接着在左侧chapt2上新建first_ros2_node.cpp,然后在first_ros2_node.cpp中输入下面的代码。

// 包含rclcpp头文件,如果Vscode显示红色的波浪线也没关系
// 我们只是把VsCode当记事本而已,谁会在意记事本对代码的看法呢,不是吗?
#include "rclcpp/rclcpp.hpp"

int main(int argc, char **argv)
{
    // 调用rclcpp的初始化函数
    rclcpp::init(argc, argv);
    // 调用rclcpp的循环运行我们创建的first_node节点
    rclcpp::spin(std::make_shared<rclcpp::Node>("first_node"));
    return 0;
}

接着我们使用g++来编译first_ros2_node节点。正常的话一定会报错

g++ first_ros2_node.cpp 

一定要记住这个错误 No such file or directory,这将是你接下来机器人学习工作生涯中最常见的错误之一。

原因我们在代码里包含了"rclcpp/rclcpp.hpp"头文件,但是g++找不到这个头文件,解决方法就是告诉g++这个头文件的目录。

首先我们要找到这个头文件在哪里,这个头文件是ROS2的客户端库,其地址肯定在ROS2的安装目录下,即/opt/ros/humble/include/rclcppls指令列出命令 | grep rclcpp.h 是对列出的结果进行过滤,只显示包含rclcpp.h的行

cd /opt/ros/humble/include/rclcpp
ls rclcpp/* | grep rclcpp.h

使用上面的指令,可以看到这个文件确实在这里

接着我们可以用-I(大写i)来为g++指定这个目录,然后再次运行,你会发现依然报错 (需在代码路径cd /home/wjh/d2lros2/chapt2/basic下运行)

g++ first_ros2_node.cpp -I /opt/ros/humble/include/rclcpp/ 

/opt/ros/humble/include/rclcpp/rclcpp/executors/multi_threaded_executor.hpp:25这个位置,包含了rcl/guard_condition.h发现找不到这个头文件。

既然错误一样,那么解决方案也是相同的,rcl/guard_condition.h所在的路径是/opt/ros/humble/include/rcl/我们再次指定后运行。

g++ first_ros2_node.cpp -I /opt/ros/humble/include/rclcpp/ -I /opt/ros/humble/include/rcl/

会发现还是相同错误,因为头文件的包含是类似于套娃形式的,一层层加下去,总有终点,…发现报的错误变了

请记住上面错误中的undefined reference to xxxxx,这将是你接下来机器人学习工作生涯中另一个最常见的错误。

原因在于g++找不到库文件,解决方法就是我们帮助它定位到库文件的位置,并通过-L参数指定库目录,-l(小写L)指定库的名字。

ROS2相关的库的地址都在/opt/ros/humble/lib下,你可以使用下面的指定看到rclcpp的动态链接库。

ls /opt/ros/humble/lib | grep rclcpp
-L /opt/ros/humble/lib/ \
-lrclcpp -lrcutils

2.2 使用make编译ROS2节点

没搞明白

g++编译节点无比的麻烦,的确是这样子,为此先行者们发明了一个叫做make的批处理工具,我们可以将g++的指令写成脚本,就可以通过make自动的调用脚本完成操作。

2.2.1 安装make

sudo apt install make

2.2.2 编写Makefile

d2lros2/d2lros2/chapt2/basic下新建Makefile,然后将g++编译指令用下面的形式写到Makefile里。

build:
    g++ first_ros2_node.cpp \
    -I/opt/ros/humble/include/rclcpp/ \
    -I /opt/ros/humble/include/rcl/ \
    -I /opt/ros/humble/include/rcutils/ \
    -I /opt/ros/humble/include/rmw \
    -I /opt/ros/humble/include/rcl_yaml_param_parser/ \
    -I /opt/ros/humble/include/rosidl_runtime_c \
    -I /opt/ros/humble/include/rosidl_typesupport_interface \
    -I /opt/ros/humble/include/rcpputils \
    -I /opt/ros/humble/include/builtin_interfaces \
    -I /opt/ros/humble/include/rosidl_runtime_cpp \
    -I /opt/ros/humble/include/tracetools \
    -I /opt/ros/humble/include/rcl_interfaces \
    -I /opt/ros/humble/include/libstatistics_collector \
    -I /opt/ros/humble/include/statistics_msgs \
    -L /opt/ros/humble/lib/ \
    -lrclcpp -lrcutils \
    -o first_node
    
# 顺便小鱼加个clean指令,用来删掉first_node
clean:
    rm first_node

Makefile同级目录输入

make build
 g++ first_ros2_node.cpp \
    -I/opt/ros/humble/include/rclcpp/ \
    -I /opt/ros/humble/include/rcl/ \
    -I /opt/ros/humble/include/rcutils/ \
    -I /opt/ros/humble/include/rmw \
    -I /opt/ros/humble/include/rcl_yaml_param_parser/ \
    -I /opt/ros/humble/include/rosidl_runtime_c \
    -I /opt/ros/humble/include/rosidl_typesupport_interface \
    -I /opt/ros/humble/include/rcpputils \
    -I /opt/ros/humble/include/builtin_interfaces \
    -I /opt/ros/humble/include/rosidl_runtime_cpp \
    -I /opt/ros/humble/include/tracetools \
    -I /opt/ros/humble/include/rcl_interfaces \
    -I /opt/ros/humble/include/libstatistics_collector \
    -I /opt/ros/humble/include/statistics_msgs \
    -L /opt/ros/humble/lib/ \
    -lrclcpp -lrcutils \
    -o first_node

可以看到make指令调用了脚本里的build下的指令,对代码进行了编译。同级目录下也产生了first_node可执行文件(绿色代表可执行)。使用make clean指令即可删掉first_node节点。

make clean
rm first_node

2.3 使用CMakeLists.txt编译ROS2节点

虽然通过make调用Makefile编译代码非常的方便,但是还是需要我们手写gcc指令来编译,那有没有什么办法可以自动生成Makefile呢?

答案是有的,那就是cmake工具。cmake通过调用CMakeLists.txt直接生成Makefile

2.3.1 安装Cmake

sudo apt install cmake

2.3.2 编译CMakeLists.txt

d2lros2/d2lros2/chapt2/basic新建CMakeLists.txt,输入下面内容。

cmake_minimum_required(VERSION 3.22)

project(first_node)

#include_directories 添加特定的头文件搜索路径 ,相当于指定g++编译器的-I参数
include_directories(/opt/ros/humble/include/rclcpp/)
include_directories(/opt/ros/humble/include/rcl/)
include_directories(/opt/ros/humble/include/rcutils/)
include_directories(/opt/ros/humble/include/rcl_yaml_param_parser/)
include_directories(/opt/ros/humble/include/rosidl_runtime_c/)
include_directories(/opt/ros/humble/include/rosidl_typesupport_interface/)
include_directories(/opt/ros/humble/include/rcpputils/)
include_directories(/opt/ros/humble/include/builtin_interfaces/)
include_directories(/opt/ros/humble/include/rmw/)
include_directories(/opt/ros/humble/include/rosidl_runtime_cpp/)
include_directories(/opt/ros/humble/include/tracetools/)
include_directories(/opt/ros/humble/include/rcl_interfaces/)
include_directories(/opt/ros/humble/include/libstatistics_collector/)
include_directories(/opt/ros/humble/include/statistics_msgs/)

# link_directories - 向工程添加多个特定的库文件搜索路径,相当于指定g++编译器的-L参数
link_directories(/opt/ros/humble/lib/)

# add_executable - 生成first_node可执行文件
add_executable(first_node first_ros2_node.cpp)

# target_link_libraries - 为first_node(目标) 添加需要动态链接库,相同于指定g++编译器-l参数
# 下面的语句代替 -lrclcpp -lrcutils
target_link_libraries(first_node rclcpp rcutils)

一般会创建一个新的目录,运行cmake并进行编译,这样的好处是不会显得那么乱。

mkdir build
cd build

创建好文件夹,接着运行cmake指令,..代表到上级目录找CMakeLists.txt。

cmake ..

运行完cmake你应该可以在build目录下看到cmake自动生成的Makefile了,接着就可以运行make指令进行编译

make

运行完上面的指令,就可以在build目录下发现first_node节点了。

2.4 CMake依赖查找流程

上面我们用g++makecmake三种方式来编译ros2的C++节点。用cmake虽然成功了,但是CMakeLists.txt的内容依然非常的臃肿,我们需要将其进一步的简化。

2.4.1 优化CMakeList.txt

cmake_minimum_required(VERSION 3.22)
project(first_node)

find_package(rclcpp REQUIRED)
add_executable(first_node first_ros2_node.cpp)
target_link_libraries(first_node rclcpp::rclcpp)

接着继续生成和编译(在build文件夹路径下)

cmake ..
make

为什么可以浓缩成那么短的几句指令呢?

2.4.2 find_package查找路径

<package>_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH

2.5 Python依赖查找流程

python的打包和引入依赖的方式相比C++要容易太多

没搞明白

2.5.1 编写ROS2的Python节点

d2lros2/d2lros2/chapt2/basic新建second_ros2_node.py,输入下面的内容

# 导入rclpy库,如果Vscode显示红色的波浪线也没关系
# 我们只是把VsCode当记事本而已,谁会在意记事本对代码的看法呢,不是吗?
import rclpy
from rclpy.node import Node
# 调用rclcpp的初始化函数
rclpy.init() 
# 调用rclcpp的循环运行我们创建的second_node节点
rclpy.spin(Node("second_node"))

2.6 Python打包工具之Setup

本部分只做了解即可,我们平时用的并不多,因为python的依赖并不是靠setup来查找的,但是C++却靠着CmakeLists.txt进行查找。

2.6.1 为什么需要对项目分发打包?

平常我们习惯了使用 pip 来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 打包

打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。

不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI 的项目,你都要学会如何打包你的项目。

Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢?

你可能听过 distutilsdistutils2setuptools等等,好像很熟悉,却又很陌生,他们都是什么关系呢?

2.6.2 包分发的始祖:distutils

distutils 是 Python 的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 Python 官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。

distutils 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。经常用它来进行模块的安装

python setup.py install

2.6.3 分发工具升级:setuptools

setuptoolsdistutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。

distributesetuptools 一个分支版本,分支的原因可能是有一部分开发者认为 setuptools 开发太慢了。但现在,distribute 又合并回了 setuptools 中。因此,我们可以认为它们是同一个东西。

还有一个大包分发工具是 distutils2,其试图尝试充分利用distutils,detuptools 和 distribute 并成为 Python 标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。

因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。

2.6.4 超详细讲解 setup.py 的编写

打包分发最关键的一步是编写 setup.py 文件。

from setuptools import setup, find_packages

setup(
    # 指定项目名称,我们在后期打包时,这就是打包的包名称,当然打包时的名称可能还会包含下面的版本号哟~
    name="mytest",
    # 指定版本号
    version="1.0",
    author="flp",
    author_email="flepeng@163.com",
    # 这是对当前项目的一个描述
    description="这只是一次测试",

    # 项目主页
    url="http://iswbm.com/", 

    # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包
    packages=find_packages()
    
    # 指定包名,即你需要打包的包名称,要实际在你本地存在哟,它会将指定包名下的所有"*.py"文件进行打包哟,但不会递归去拷贝所有的子包内容。
    # 综上所述,我们如果想要把一个包的所有"*.py"文件进行打包,应该在packages列表写下所有包的层级关系哟~这样就开源将指定包路径的所有".py"文件进行打包!
    packages=['devops', "devops.dev", "devops.ops"],
)

3. 节点

3.1 ROS2节点是什么

机器人是各种功能的综合体,每一项功能就像机器人的一个工作细胞,众多细胞通过一些机制连接到一起,成为了一个机器人整体。

在ROS中,我们给这些 “细胞”取了一个名字,那就是节点
在这里插入图片描述
在机器人身体里搭载了一台计算机A,它可以通过机器人的眼睛——摄像头,获取外界环境的信息,也可以控制机器人的腿——轮子,让机器人移动到想要去的地方。除此之外,可能还会有另外一台计算机B,放在你的桌子上,它可以远程监控机器人看到的信息,也可以远程配置机器人的速度和某些参数,还可以连接一个摇杆,人为控制机器人前后左右运动。

这些功能虽然位于不同的计算机中,但都是这款机器人的工作细胞,也就是节点,他们共同组成了一个完整的机器人系统。

ROS2中每一个节点也是只负责一个单独的模块化的功能(比如一个节点负责控制车轮转动,一个节点负责从激光雷达获取数据、一个节点负责处理激光雷达的数据、一个节点负责定位等等)
在这里插入图片描述

  • 节点在机器人系统中的职责就是执行某些具体的任务,从计算机操作系统的角度来看,也叫做进程
  • 每个节点都是一个可以独立运行的可执行文件,比如执行某一个python程序,或者执行C++编译生成的结果,都算是运行了一个节点;
  • 既然每个节点都是独立的执行文件,那自然就可以想到,得到这个执行文件的编程语言可以是不同的,比如C++、Python,乃至Java、Ruby等更多语言。
  • 这些节点是功能各不相同的细胞,根据系统设计的不同,可能位于计算机A,也可能位于计算机B,还有可能运行在云端,这叫做分布式,也就是可以分布在不同的硬件载体上;
  • 每一个节点都需要有唯一的命名,当我们想要去找到某一个节点的时候,或者想要查询某一个节点的状态时,可以通过节点的名称来做查询。

节点也可以比喻是一个一个的工人,分别完成不同的任务,他们有的在一线厂房工作,有的在后勤部门提供保障,他们互相可能并不认识,但却一起推动机器人这座“工厂”,完成更为复杂的任务。

3.2 节点之间如何交互?

上面举了一个激光雷达的例子,一个节点负责获取激光雷达的扫描数据,一个节点负责处理激光雷达数据,比如去除噪点。

那节点与节点之间就必须要通信了,那他们之间该如何通信呢?ROS2早已为你准备好了一共四种通信方式:

  • 话题-topics : 单向传递,多端接受
  • 服务-services : 双向信息传递
  • 动作-action
  • 参数-parameters
    在这里插入图片描述

3.3 如何启动一个节点?

节点对应一个功能模块,及相当于启动一个工作空间和包:

ros2 run <package_name> <executable_name>
启动<package_name>包下的<executable_name>中的节点

这里运行一个节点,感受一下

ros2 run turtlesim turtlesim_node

运行之后可以看到一只小乌龟

3.4 通过命令行界面查看节点信息

3.4.1 ROS2命令行

ROS2的CLI,就是和ROS2相关的命令行操作。

CLI(Command-Line Interface)命令行
GUI(Graphical User Interface)可视化

GUI(Graphical User Interface)就是平常我们说的图形用户界面,大家用的Windows是就是可视化的,我们可以通过鼠标点击按钮等图形化交互完成任务。
CLI(Command-Line Interface)就是命令行界面了,我们所用的终端,黑框框就是命令行界面,没有图形化。

很久之前电脑还是没有图形化界面的,所有的交互都是通过命令行实现,就学习机器人而言,命令行操作相对于图形化优势更加明显。

ROS2为我们提供了一系列指令,通过这些指令,可以实现对ROS2相关模块信息的获取设置等操作。

如果你学过ansys apdl的话这里的概念就很明显了

3.4.2 节点相关的CLI

ros2 run <package_name> <executable_name>  # 启动<package_name>包下的<executable_name>中的节点
ros2 node list  # 查看节点列表(常用)
ros2 node info <node_name>  # 查看节点信息(常用)

ros2 run turtlesim turtlesim_node --ros-args --remap __node:=my_turtle  # 重映射节点名称为/my_turtle _ 用于启用相同节点

ros2 run example_parameters_rclcpp parameters_basic --ros-args -p rcl_log_level:=10  # 运行节点时设置参数  ??

3.4.3 编写ROS2节点的一般步骤

  1. 导入库文件
  2. 初始化客户端库
  3. 新建节点
  4. spin循环节点
  5. 关闭客户端库

3.5 案例一:Hello World节点(面向过程)

路径:~/dev_ws ——需要删除第四节中所设置的其他文件(CMake+Python)哦!

colcon build

3.5.1 运行效果

ros2 run learning_node node_helloworld

在这里插入图片描述

3.5.2 代码解析

Vscode 中:文件 — 打开文件 — ~/dev_ws/src/ros2_21_tutorialslearning_node/learning_node/node_helloworld.py

#!/usr/bin/env python3 
# -*- coding: utf-8 -*-

"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向过程的实现方式
"""

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
import time

def main(args=None):                             # ROS2节点主入口main函数
    rclpy.init(args=args)                        # ROS2 Python接口初始化 - 固定范式
    node = Node("node_helloworld")               # 创建ROS2节点对象并进行初始化
    
    while rclpy.ok():                            # ROS2系统是否正常运行
        node.get_logger().info("Hello World")    # ROS2日志输出 - print
        time.sleep(0.5)                          # 休眠控制循环时间 - ctrl + c 跳出循环,进入下一行
    
    node.destroy_node()                          # 销毁节点对象    
    rclpy.shutdown()                             # 关闭ROS2 Python接口

learning_node/setup.py : 完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
在这里插入图片描述

from setuptools import setup

package_name = 'learning_node'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='Hu Chunxu',
    maintainer_email='huchunxu@guyuehome.com',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={                                     # 入口节点
        'console_scripts': [
         'node_helloworld       = learning_node.node_helloworld:main',  # 文件夹名称 + 程序名称 - 入口点
         'node_helloworld_class = learning_node.node_helloworld_class:main',
         'node_object            = learning_node.node_object:main',
         'node_object_webcam     = learning_node.node_object_webcam:main',
        ],
    },
)

3.5.3 创建节点流程

代码中出现的函数未来会经常用到,先不用纠结函数的具体使用方法,更重要的是理解节点的编码流程。

总结一下,想要实现一个节点,代码的实现流程是这样做:

  • 编程接口初始化
  • 创建节点并初始化
  • 实现节点功能
  • 销毁节点并关闭接口

如果有学习过C++或者Pyhton的话,应该可以发现这里使用的是面向过程的编程方法,这种方式虽然实现简单,但是对于稍微复杂一点的机器人系统,就很难做到模块化编码

3.6 案例二:Hello World节点(面向对象)

在ROS2的开发中,更推荐大家使用面向对象的编程方式,比如刚才的代码就可以改成这样,虽然看上去复杂了一些,但是代码会具备更好的可读性和可移植性,调试起来也会更加方便。

3.6.1 运行效果

Vscode 中:文件 — 打开文件 — ~/dev_ws/src/ros2_21_tutorialslearning_node/learning_node/node_helloworld_class.py

ros2 run learning_node node_helloworld_class

在这里插入图片描述

3.6.2 代码解析

learning_node/node_helloworld_class.py:功能虽然一样,但是程序的结构发生了变化。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向对象的实现方式
"""

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
import time

"""
创建一个HelloWorld节点, 初始化时输出“hello world”日志
"""
class HelloWorldNode(Node):     # 实例化——放到类中
    def __init__(self, name):
        super().__init__(name)                       # ROS2节点父类初始化
        while rclpy.ok():                            # ROS2系统是否正常运行
            self.get_logger().info("Hello World")    # ROS2日志输出
            time.sleep(0.5)                          # 休眠控制循环时间

def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = HelloWorldNode("node_helloworld_class")   # 创建ROS2节点对象并进行初始化
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

learning_node/setup.py :完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
在这里插入图片描述
编译成功后,文件放在~/dev_ws/install/learning_node/lib/learning_node
在这里插入图片描述
~/dev_ws/install/learning_node/lib/python3.10/site-packages/learning_node : 代码位置 ——不编译无法更新install文件夹下的程序

3.6.3 创建节点流程

所以总体而言,节点的实现方式依然是这四个步骤,只不过编码方式做了一些改变而已。

  • 编程接口初始化
  • 创建节点并初始化
  • 实现节点功能
  • 销毁节点并关闭接口
    到这里为止,机器人中的节点不能只是打印Hello World吧,是不是得完成一些具体的任务。

3.7 案例三:物体识别节点

~/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/node_object.py

3.7.1 运行效果

在这个例程中,我们将用到一个图像处理的库——OpenCV,运行前请使用如下指令安装:

sudo apt install python3-opencv

然后就可以运行例程啦:(运行前需要将learning_node/node_object.py代码中的图片路径,修改为实际路径,修改后重新编译-工作空间下colcon build运行即可:image = cv2.imread('/home/hcx/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/apple.jpg')

ros2 run learning_node node_object    #注意修改图片路径后重新编译

如果colcon build过程中出现如下错误
colcon build
是由于anaconda和ros冲突了,参考文章
https://blog.csdn.net/weixin_43735353/article/details/108200337
https://zhuanlan.zhihu.com/p/137908448
https://blog.csdn.net/weixin_44733606/article/details/132576418

3.7.2 代码解析

在这个例程中,加入了图像识别的处理过程,模拟一个节点的功能,关于图像处理的具体实现,并不是此处的重点,更多要关注是如何通过节点的概念来实现一个具体的机器人功能。

learning_node/node_object.py

#!/usr/bin/env python3 
# -*- coding: utf-8 -*-

"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-通过颜色识别检测图片中出现的苹果
"""

import rclpy                            # ROS2 Python接口库
from rclpy.node import Node             # ROS2 节点类

import cv2                              # OpenCV图像处理库
import numpy as np                      # Python数值计算库

lower_red = np.array([0, 90, 128])     # 红色的HSV阈值下限
upper_red = np.array([180, 255, 255])  # 红色的HSV阈值上限

def object_detect(image):
    hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)      # 图像从BGR颜色模型转换为HSV模型
    mask_red = cv2.inRange(hsv_img, lower_red, upper_red) # 图像二值化

    contours, hierarchy = cv2.findContours(mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # 图像中轮廓检测

    for cnt in contours:                                  # 去除一些轮廓面积太小的噪声
        if cnt.shape[0] < 150:
            continue

        (x, y, w, h) = cv2.boundingRect(cnt)              # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高
        cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)# 将苹果的轮廓勾勒出来
        cv2.circle(image, (int(x+w/2), int(y+h/2)), 5, (0, 255, 0), -1)# 将苹果的图像中心点画出来

    cv2.imshow("object", image)                           # 使用OpenCV显示处理后的图像效果
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def main(args=None):                                      # ROS2节点主入口main函数
    rclpy.init(args=args)                                 # ROS2 Python接口初始化
    node = Node("node_object")                            # 创建ROS2节点对象并进行初始化
    node.get_logger().info("ROS2节点示例:检测图片中的苹果")

    image = cv2.imread('/home/wjh/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/apple.jpg')  # 读取图像
    object_detect(image)                                   # 苹果检测
    rclpy.spin(node)                                       # 循环等待ROS2退出
    node.destroy_node()                                    # 销毁节点对象
    rclpy.shutdown()                                       # 关闭ROS2 Python接口

完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置: —— 否则无法通过 ros2 run 调用
在这里插入图片描述

3.8 案例四:机器视觉识别节点

用图片进行识别好像还不太合理,机器人应该有眼睛呀,接下来就让节点读取摄像头的图像,动态识别其中的苹果,或者类似颜色的物体。

3.8.1 运行效果

启动一个终端,运行如下节点:

ros2 run learning_node node_object_webcam #注意设置摄像头

3.8.2 代码解析

learning_node/node_object_webcam.py:相比之前的程序,这里最大的变化是修改了图片的来源,使用OpenCV中的VideoCapture()来驱动相机,并且周期read摄像头的信息,并进行识别。

#!/usr/bin/env python3 
# -*- coding: utf-8 -*-

"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-通过颜色识别检测图片中出现的苹果
"""

import rclpy                            # ROS2 Python接口库
from rclpy.node import Node             # ROS2 节点类

import cv2                              # OpenCV图像处理库
import numpy as np                      # Python数值计算库

lower_red = np.array([0, 90, 128])     # 红色的HSV阈值下限
upper_red = np.array([180, 255, 255])  # 红色的HSV阈值上限

def object_detect(image):
    hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)                               # 图像从BGR颜色模型转换为HSV模型
    mask_red = cv2.inRange(hsv_img, lower_red, upper_red)                          # 图像二值化

    contours, hierarchy = cv2.findContours(mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # 图像中轮廓检测

    for cnt in contours:                                                          # 去除一些轮廓面积太小的噪声
        if cnt.shape[0] < 150:
            continue
            
        (x, y, w, h) = cv2.boundingRect(cnt)                                      # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高
        cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)                        # 将苹果的轮廓勾勒出来
        cv2.circle(image, (int(x+w/2), int(y+h/2)), 5, (0, 255, 0), -1)           # 将苹果的图像中心点画出来
	    
    cv2.imshow("object", image)                                                    # 使用OpenCV显示处理后的图像效果
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def main(args=None):                                                              # ROS2节点主入口main函数
    rclpy.init(args=args)                                                         # ROS2 Python接口初始化
    node = Node("node_object")                                                     # 创建ROS2节点对象并进行初始化
    node.get_logger().info("ROS2节点示例:检测图片中的苹果")

    image = cv2.imread('/home/wjh/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/apple.jpg')  # 读取图像
    object_detect(image)                                                            # 苹果检测
    rclpy.spin(node)                                                               # 循环等待ROS2退出
    node.destroy_node()                                                            # 销毁节点对象
    rclpy.shutdown()                                                               # 关闭ROS2 Python接口

3.8.3 节点命令行操作

ros2 node list # 查看节点列表

ros2 node info <node_name> # 查看节点信息

在这里插入图片描述

3.9 思考题

现在,大家应该熟悉节点这个工作细胞的概念和实现方法了,回到这个机器人系统的框架图,我们还会发现另外一个问题。
在这里插入图片描述
电脑B中的摇杆,要控制机器人运动,这两个节点岂不是应该有某种连接,比如摇杆节点发送一个速度指令给运动节点,收到后机器人开始运动。

同理,如果我们想要改变机器人的速度,负责配置参数的节点就得发送一个指令给运动节点,如果电脑B想要显示机器人看到的图像,电脑A中的摄像头节点就得把图像发送过来。

没错,在一个ROS机器人的系统中,节点并不是孤立的,他们之间会有很多种机制保持联系,下一节,我们将给大家介绍这些机制中最为常用的一种。

4. ROS2工作空间

运行一个节点的时候使用的是:

ros2 run <package_name> <executable_name> # 启动<package_name>包下的<executable_name>中的节点 # 包名字 可执行文件名字

我们想找到一个节点(可执行文件),就必须要先知道它在哪个包,那问题就来了,想要找到某个包,该去哪里找?——工作空间

注意:一个工作空间下可以有多个功能包,一个功能包可以有多个节点存在

在文件夹构成的空间下执行命令,专门用于执行命令的文件夹——工作空间

4.1 工作空间

大家在之前的学习和开发中,应该有接触过某些集成开发环境,比如Visual Studio、Eclipse、Qt Creator等,当我们想要编写程序之前,都会在这些开发环境的工具栏中,点击一个“创建新工程”的选项,此时就产生一个文件夹,后续所有工作产生的文件,都会放置在这个文件夹中,这个文件夹以及里边的内容,就叫做是工程。

类似的,在ROS机器人开发中,我们针对机器人某些功能进行代码开始时,各种编写的代码、参数、脚本等文件,也需要放置在某一个文件夹里进行管理,这个文件夹在ROS系统中就叫做工作空间

工作空间是包含若干个功能包的目录,一开始大家把工作空间理解成一个文件夹就行了。这个文件夹包含下有src。所以一般新建一个工作空间的操作就像下面一样

cd d2lros2/chapt2/
mkdir -p chapt2_ws/src

其实就是创建一个目录,将所需的功能包放在workspace(chapt2_ws)的src下

ROS系统中一个典型的工作空间结构包括四个子目录,或者叫做四个子空间:build , install , log , src

  • src代码空间,未来编写的代码、脚本,都需要人为的放置到这里;
  • build编译空间,保存编译过程中产生的中间文件;
  • install安装空间,放置编译得到的可执行文件和脚本(结果);
  • log日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。

总体来讲,这四个空间的文件夹,我们绝大部分操作都是在src中进行的,编译成功后,就会执行install里边的结果,buildlog两个文件夹用的很少。

工作空间的名称我们可以自己定义,数量也并不是唯一的,比如:

  • 工作空间1:dev_w_a,用于A机器人的开发;
  • 工作空间2:dev_ws_b,用于B机器人的一部分功能;
  • 工作空间3:dev_ws_b2,用于开发B机器人另外一些功能。

4.1.1 创建工作空间

mkdir -p ~/dev_ws/src
cd ~/dev_ws/src  # 代码空间
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git

4.1.2 自动安装依赖

sudo apt install -y python3-pip  # 安装phython包pip3的工具
sudo pip3 install rosdepc  # 国内用于解决网络访问的问题
sudo rosdepc init
rosdepc update
cd ..
rosdepc install -i --from-path src --rosdistro humble -y  # 安装基本依赖

4.1.3 编译工作空间

sudo apt install python3-colcon-ros
cd ~/dev_ws/
colcon build

编译成功后,就可以在工作空间中看到自动生产的build、log、install文件夹了。
在这里插入图片描述

4.1.4 设置环境变量

编译成功后,为了让系统能够找到我们的功能包和可执行文件,还需要设置环境变量:

source install/local_setup.sh # 仅在当前终端生效
echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效

显示 no found?

Ctrl + H 显示隐藏文件

4.2 功能包是什么

大家可以看到有很多不同名称的文件夹,这些在ROS2并不是普通的文件夹,而是叫做功能包

功能包可以理解为存放节点的地方,ROS2中功能包根据编译方式的不同分为三种类型。

  • ament_python,适用于python程序
  • cmake,适用于C++
  • ament_cmake,适用于C++程序,是cmake的增强版

我们把不同功能的代码划分到不同的功能包中,尽量降低他们之间的耦合关系,当需要在ROS社区中分享给别人的时候,只需要说明这个功能包该如何使用,别人很快就可以用起来了。

所以功能包的机制,是提高ROS中软件复用率的重要方法之一。

4.3 功能包获取的两种方式

4.3.1 安装获取

安装一般使用:

sudo apt install ros-<version>-package_name

安装获取会自动放置到系统目录,不用再次手动source

/POT/ROS/HUMBLE # 需要记住的路径-ros包安装位置,版本为humble

4.3.2 手动编译获取

手动编译相对麻烦一些,需要下载源码然后进行编译生成相关文件。

什么时候需要手动编译呢?
一般我们能安装的功能包都是作者编译好程序将可执行文件上传到仓库中,然后我们才能够通过apt进行安装,如果作者还没来得及测试上传,或者忘记了测试上传,就会找不到对应的包,这时候就需要手动编译安装了。
另外一种就是我们需要对包的源码进行修改,这个时候也需要自己编译修改。

手动编译之后,需要手动source工作空间的install目录,才可运行。

4.4 与功能包相关的指令 ros2 pkg(package)

  1. 创建功能包 create Create a new ROS2 package

ros2 pkg create <package-name> --build-type {cmake,ament_cmake,ament_python} --dependencies <依赖名字>

  1. 列出可执行文件 executables Output a list of package specific executables
ros2 pkg executables  # 列出所有
ros2 pkg executables turtlesim  # 列出`turtlesim`功能包的所有可执行文件 
# 可以进行运行 ros2 run turtlesim turtlesim_nod
  1. 列出所有的包 list Output a list of available packages
ros2 pkg list
  1. 输出某个包所在路径的前缀 prefix Output the prefix path of a package

ros2 pkg prefix <package-name>

ros2 pkg prefix turtlesim  # 比如小乌龟
  1. 列出包的清单描述文件 xml Output the XML of the package manifest or a specific tag
    每一个功能包都有一个标配的manifest.xml文件,用于记录这个包的名字,构建工具,编译信息,拥有者,干啥用的等信息。通过这个信息,就可以自动为该功能包安装依赖,构建时确定编译顺序等
ros2 pkg xml turtlesim   # 查看小乌龟模拟器功能包的信息

4.5 创建功能包

如何在ROS2中创建一个功能包呢?

ros2 pkg create --build-type <build-type> <package_name> <dependencies>

  • pkg:表示功能包相关的功能;
  • create:表示创建功能包;
  • build-type:表示新创建的功能包是C++还是Python的,如果使用C++或者C,那这里就跟ament_cmake,如果使用Python,就跟ament_python
  • package_name:新建功能包的名字。
  • dependencies : 指的是这个功能包的依赖,这里小鱼给了一个ros2的python客户端接口rclpy
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

4.6 编译功能包

在创建好的功能包中,我们可以继续完成代码的编写,之后需要编译和配置环境变量,才能正常运行:

cd ~/dev_ws
colcon build   # 编译工作空间所有功能包
source install/local_setup.bash # source install/setup.bash

ros2 run <package_name> <executable_name> # 运行节点  
# 新终端
ros2 node list  # 查看现有的节点

4.7 功能包的结构

功能包并不是普通的文件夹,我们来分析下刚才新创建两个功能包的结构。

  1. ament_cmake: C++功能包,其中必然存在两个文件:package.xmlCMakerLists.txt
    在这里插入图片描述 package.xml :名字+许可号+许可证… + 依赖(需要注意安装)——包含功能包的版权描述,和各种依赖的声明。 在这里插入图片描述
    CMakeLists.txt:设置编译规则,C++代码需要编译才能运行,所以必须要在该文件中设置如何编译,使用CMake语法。
    在这里插入图片描述

  2. ament_python: Python功能包,C++功能包需要将源码编译成可执行文件,但是Python语言是解析型的,不需要编译,所以会有一些不同,但也会有这两个文件:package.xmlsetup.py
    在这里插入图片描述
    package.xml文件的主要内容和C++版本功能包一样,包含功能包的版权描述,和各种依赖的声明。
    在这里插入图片描述
    setup.py文件里边也包含一些版权信息,除此之外,还有 “entry_points”配置的程序入口 ,在后续编程讲解中,我们会给大家介绍如何使用。
    在这里插入图片描述
    setup.cfd文件:工作空间的信息配置
    在这里插入图片描述

4.8 编写一个python功能包

  • learning_pkg_python文件夹下放置需要的python程序,接下来我们开始编写一个python节点,首先在__init__.py同级别目录下创建<name>=li4.py文件(在vscode中右击新建就行)。
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

  • 代码编写完成用Crtl+S进行保存。接着修改setup.py

  • 增加一句话,告诉ros2村庄来了一位新村民李四,要找这位村民去learning_pkg_python.<name>:main路径下寻找。

    entry_points={
        'console_scripts': [
            "li4_node = village_li.li4:main"
        ],
    },
)
  • 工作空间下colcon build
  • source环境 source install/setup.bash
  • 过滤节点 ros2 pkg list | grep vill
  • 运行节点 ros2 run village_li li4_node
  • 查看节点运行:ros2 node list
  • 节点信息:ros2 node info <node_name>

5. ROS2构建工具—Colcon

ROS2编译工具colcon

5.1 Colcon是个啥

colcon其实是一个功能包构建工具用来编译代码,ROS2默认是没有安装colcon的(colcon想当于ros1中的catkin工具)

5.2 安装colcon

sudo apt-get install python3-colcon-common-extensions

安装完成后,打开终端输入colcon即可看到其使用方法。

5.3 编个东西测试一下

  1. 创建一个工作区文件夹colcon_test_ws
cd d2lros2/chapt2/
mkdir colcon_test_ws && cd colcon_test_ws
  1. 下载个ROS2示例源码测试一下(需要科学上网)-b 用于指定github中的分支为humble
git clone https://github.com/ros2/examples src/examples -b humble

来个VPN

https://portal.shadowsocks.au/knowledgebase/160/
sudo ./trojan # 需在安装路径下的终端运行
pkill -f trojan
  1. 编译工程
colcon build
  1. 编完之后的目录结构
    ROS系统中一个典型的工作空间结构如图所示,这个d2lros2/chapt2/colcon_test_ws 就是工作空间的根目录,里边会有四个子目录,或者叫做四个子空间: 在src放置源码)同级目录我们应该会看到 buildinstalllog 目录:
  • build 目录存储的是中间文件。对于每个包,将创建一个子文件夹,在其中调用例如CMake(编译产生的文件)
  • install 目录是每个软件包将安装到的位置。默认情况下,每个包都将安装到单独的子目录中(重要的安装目录)。
  • log 目录包含有关每个colcon调用的各种日志信息。
    在这里插入图片描述

5.4 运行一个自己编的节点

  1. 打开一个终端(d2lros2/chapt2/colcon_test_ws )进入我们刚刚创建的工作空间,先source 一下资源(以找到功能包)
source install/setup.bash
  1. 运行一个订者节点,你将看不到任何打印,因为没有发布者
ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function
  1. 打开一个新的终端(d2lros2/chapt2/colcon_test_ws ),先source,再运行一个发行者节点
source install/setup.bash
ros2 run examples_rclcpp_minimal_publisher publisher_member_function

5.5 本节学习指令

5.5.1 只编译一个包

d2lros2/chapt2/colcon_test_ws

colcon build --packages-select YOUR_PKG_NAME 

5.5.2 不编译测试单元

d2lros2/chapt2/colcon_test_ws

colcon build --packages-select YOUR_PKG_NAME  --cmake-args -DBUILD_TESTING=0

5.5.3 运行编译的包的测试

d2lros2/chapt2/colcon_test_ws

colcon test

5.5.4 允许通过更改src下的部分文件来改变install(重要)

d2lros2/chapt2/colcon_test_ws
每次调整 python 脚本时都不必重新build了——colcon build 只是进行了拷贝,src的文件更改不会影响build文件夹中的内容

colcon build --symlink-install

5.6 练习-小乌龟求偶大作战

项目开源地址:fishros/turtle_battle: 追逐你喜欢的那个小乌龟,满100分奖励教学视频哦 (github.com)

5.6.1 游戏介绍

游戏内容很简单,画面中会出现两只小乌龟,一个乌龟是可以被控制的,玩家控制这只小乌龟去找自己的配偶乌龟,找到后配偶乌龟会随机出现在其他位置。每找到一次,好感度加10分,如果满100分就会跳出一个神秘网页

5.6.2 创建工作空间编译功能包

  • 创建工作空间
mkdir -p turtle_ws/src
cd turtle_ws/src
  • 下载源码功能包到工作空间的src文件夹下
git clone https://github.com/fishros/turtle_battle.git
  • 进入工作空间——目录上一级进行编译
cd ../
colcon build

warning解决办法:将工作空间下src文件中的setup文件中的- 改为_

5.6.3 运行游戏

完成了上一步的编译之后,你会看到工作空间里的文件夹从一个src变成了四个。

  • source空间(选中该文件变为可执行文件)
source install/setup.bash
  • 启动游戏节点:打开一个新终端,进入turtle_ws工作空间,启动以下节点:
ros2 run turtlesim turtlesim_node

只看到了一个小乌龟。。未解决

  • 打开遥控器节点:打开一个新终端,进入turtle_ws工作空间,启动以下节点:
ros2 run turtlesim turtle_teleop_key
  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiongLoure

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值