ROS2学习笔记1-安装、编译和基本概念

| |

注:原笔记博客写于2023年秋天,文中一些参考了CSDN的教程链接几乎都失效了,无奈只好删除了很多内容。

2023秋
这是笔记本,不是教程。
几位耶jiweiyecau@163.com

安装

安装虚拟机或双系统

安装虚拟机

虚拟机使用教程

虚拟机VMware Workstation Pro v16.0.0使用教程 - 苏木川的文章 - 知乎

ubuntu安装

Ubuntu官网

使用虚拟机的安装

CSDN【VMware安装Ubuntu20(图文教程,超详细)】
安装语言最好先设置为英语en(us),后期再改回中文cn(zh),以避免潜在的问题。
此处在设置磁盘大小时,应该设置大一些,50GB以上

使用双系统的安装

警告:前方巨坑!可能存在问题
B站机器人工匠阿杰【Windows 和 Ubuntu 双系统的安装和卸载】
不建议按此教程设置磁盘分区
安装语言最好先设置为英语en(us),后期再改回中文cn(zh),以避免潜在的问题。

Linux/ubuntu系统使用入门

鱼香ROS【Linux与Ubuntu系统介绍】
B站机器人工匠阿杰【从没接触过Ubuntu,如何上手ROS开发】-ubuntu使用入门
菜鸟教程【Linux 常用命令学习】
40个最常用的Linux命令行大全 - Haiyuan Kwong的文章 - 知乎
Linux 之 Vim 命令使用(详细总结) - hepingfly的文章 - 知乎
精通 VIM ,此文就够了 - zempty的文章 - 知乎

shell

# 常用指令
sudo apt update # 更新软件包列表
sudo apt upgrade # 将软件包升级到最新版本

ros2安装

ROS2版本选择

ros2有不同的发行版本,与ubuntu各版本有对应关系,不能装错。现在一般选择ubuntu22.04和Humble。

ros与Ubuntu对应关系ros与Ubuntu对应关系

鱼香ROS一键安装法(推荐)

强烈推荐鱼香ROS提供的一键安装法,可以避免大量不必要的麻烦。

shell

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

参阅:鱼香ROS【一键安装ros2】

官方安装法(不推荐)

参阅:
docs.ros.org/en/humble/Installation
另可参阅:
刘思培个人博客-1.1 安装ros2
鱼香ROS-3.动手安装ROS2

其他必要的配置

安装VScode

方法一:系统自带software应用安装 打开Ubuntu Software

ubuntu_softwareubuntu_software
点击左上角放大镜搜索按钮,输入code,找到VScode图标,注意这里软件名称为“code”,点击进入详情页面。看到安装按钮,点击安装,即可。
vscode2vscode2
vscode3vscode3
方法二:命令行安装

shell

sudo apt install snap # 安装snap工具,系统可能已经自带
sudo snap install --classic code # 安装VS Code

方法三:鱼香ros一键安装

shell

wget http://fishros.com/install -O fishros && . fishros
# 输入密码后,输入7选择一键安装VsCode

fishrosfishros

安装其他插件和编译工具等

shell

sudo apt install ros-humble-turtlesim # 安装海龟功能包
# 安装make编译工具
sudo apt install make
# 安装cmake
sudo apt install cmake
# 安装构建工具colcon
sudo apt-get install python3-colcon-common-extensions
# 安装图形界面可视化工具RQT
sudo apt install ros-humble-rqt* 

# 安装依赖工具,使用鱼香ros提供的国内版,选择一键配置rosdep即可
wget http://fishros.com/install -O fishros && . fishros

# 设置始终执行该脚本文件,使ros2的软件包可在所有打开的新终端中使用
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc 

备注:colcon是ROS编译工具catkin_make、catkin_make_isolated、catkin_tools和ament_tools的迭代品。
参考:https://design.ros2.org/articles/build_tool.html
colcon的使用:鱼香ros【colcon构建工具命令参数】另请参阅本文档第3章。

shell

# 机器人运动控制
sudo apt-get install ros-humble-teleop-twist-keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard

# 安装tf树插件
sudo apt install ros-humble-rqt-tf-tree

双系统下的VPN问题

由于历史和现实的原因,机器人和计算机科学领域有大量高价值资料仅在外网可以获取到。 安装双系统时,需要在ubuntu下实现外网访问。 参考方法:
方法一:浏览器插件
AdGuard VPN,这款插件注册登录后每月有少量的免费流量,足够访问github使用了。

vpnvpn
方法二:ClashForWindows
clash for windows可以在Linux下使用。
CFW原始安装包位置:
ClashForWindows Releases
对于 Ubuntu, 一般使用 clash-linux-amd64 版本。
参考:在linux下使用clash for windows

语言和输入法的调整

很遗憾,搜狗输入法暂时并不支持ubuntu22.04
建议使用iBus框架下智能拼音输入法,详情请参考下面的链接,请注意ubuntu22.04目前还无法安装搜狗输入法。参照下面的方法设置中文输入法:
在Ubuntu20.04中安装中文输入法 - dandelion的文章 - 知乎

安装和配置Git

安装和配置git

shell

sudo apt update
sudo apt upgrade # 将软件包升级到最新版本
# 安装git
sudo apt install git
# 查看git版本,检查安装结果
git --version
# git配置
git config --global user.name "Your Name"
git config --global user.email "youremail@yourdomain.com"
# 验证git配置结果
git config --list

# 配置SSH
ssh-keygen -t rsa -C "这里换上你的邮箱"
# 回车3-4次
# 命令执行结束之后,home目录下面会生成.ssh目录

配置SSH时,执行命令后需要进行3次或4次确认:

shell

ssh -T git@github.com #测试是否配置成功
shell

# 使用Git,请参照其他教程
git add xxx
git commit -m "xxxxxx"
git push origin master

参考:
Git官方文档
ubuntu git 环境搭建以及通过 SSH 连接 Github - 上海老金的文章 - 知乎

机器人相关软件安装

仿真软件gazebo

shell

# 安装gazebo仿真软件
sudo apt install gazebo
# 安装gazebo-ros功能包(所有)
sudo apt install ros-humble-gazebo-*
# 安装gazebo_ros插件
sudo apt install ros-humble-gazebo-ros
# 测试是否安装成功
gazebo --verbose -s libgazebo_ros_init.so -s libgazebo_ros_factory.so 
# 安装gazebo模型库(可选)
cd ~/.gazebo && wget https://gitee.com/ohhuo/scripts/raw/master/gazebo_model.py && python3 gazebo_model.py

建图SLAM工具cartographer

shell

sudo apt install ros-humble-cartographer
sudo apt install ros-humble-cartographer-ros
shell

# 查看cartographer源码
git clone https://ghproxy.com/https://github.com/ros2/cartographer.git -b ros2
git clone https://ghproxy.com/https://github.com/ros2/cartographer_ros.git -b ros2

导航NAV2框架

shell

# 安装nav2
sudo apt install ros-humble-nav2-*
# 检查安装结果
ros2 pkg list | grep navigation2

# 查看nav2源码,结果为navigation2
git clone https://ghproxy.com/https://github.com/ros-planning/navigation2.git -b humble

可能出现的报错问题

报错:找不到“rclcpp"/找不到"rclpy"

点击“快速修复”点击“快速修复”

将鼠标靠近报错的波形曲线,点击“快速修复”,选择编辑IncludePath设置,编辑c_pp_properties.json文件,将ros相关的文件路径添加进去即可。

点击编辑includePath设置点击编辑includePath设置

需要的代码如下,注意ros安装的版本,这里是humble

json

,
"/opt/ros/humble/include/**"

修改为:

添加代码添加代码
即可。

shell

source /opt/ros/humble/setup.bash

报错:setup.py install is deprecated. Use build and pip……

shell

pip install setuptools==58.2.0

ros官方论坛对此有讨论:
SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools

gazebo 无响应

gazebo在上一次关闭时未能被正确地关闭,导致下一次再启动时报错 [Err] [Master.cc:96] EXCEPTION: Unable to start server[bind: Address already in use]. There is probably another Gazebo process running.

解决办法:重启系统。使用终端打开gazebo时仅在终端ctrl+c关闭,以避免关闭异常。
或者:使用 psgrep 命令查找残留的 Gazebo 进程,然后使用 kill 命令终止这些进程。
首先使用 ps auxgrep 查找 Gazebo 相关的进程:

sh

ps aux | grep gazebo

输出可能类似于:

plaintext

user     1234  0.0  0.1 123456 12345 ?        S    12:34   0:00 /usr/bin/gzserver ...
user     5678  0.0  0.1 234567 23456 ?        S    12:34   0:00 /usr/bin/gzclient ...
user     9101  0.0  0.0  1234  1234 pts/1    S+   12:35   0:00 grep --color=auto gazebo

终止 Gazebo 进程:

sh

kill -9 1234 5678

即可解决。

ROS2常用指令和部分基本概念

命令行CLI(Command-Line Interface)

节点node

节点概念: - 每个节点负责一个模块化的任务。 - 节点之间通过四种方式通信:话题topic,服务service,动作action,参数parameters 。 - 节点是可以独立运行的可执行文件。即使节点没有收到或发出数据,它也是可以独立运行的。但正常工作时需要获取到有效数据。 - 节点可以是分布式的,这对大型项目很有用。 - 设计时,节点间除了必要的通信外,应该尽可能减少彼此的耦合,便于管理。

A node is a participant in the ROS 2 graph, which uses a client library to communicate with other nodes. Nodes can communicate with other nodes within the same process, in a different process, or on a different machine. Nodes are typically the unit of computation in a ROS graph; each node should do one logical thing.
Nodes can publish to named topics to deliver data to other nodes, or subscribe to named topics to get data from other nodes. They can also act as a service client to have another node perform a computation on their behalf, or as a service server to provide functionality to other nodes. For long-running computations, a node can act as an action client to perform it, or as an action server to have another node perform it. Nodes can provide configurable parameters to change behavior during run-time.
Connections between nodes are established through a distributed discovery process.
官方文档:Basic Concepts - Nodes
官方文档:Understanding nodes

shell

# 运行节点
ros2 run <package_name> <executable_name>
# 示例
ros2 run turtlesim turtlesim_node # 运行海龟
# 查看节点列表
ros2 node list
# 查看节点信息
ros2 node info <node_name>
# 示例
ros2 node info /turtlesim # 查看/turtlesim节点的详细信息

# 重映射节点名称
ros2 run turtlesim turtlesim_node --ros-args --remap __node:=my_turtle
# 运行节点时设置参数
ros2 run example_parameters_rclcpp parameters_basic --ros-args -p rcl_log_level:=10

话题topic

shell

ros2 topic -h

ros2 topic list
ros2 topic list -t
ros2 topic info /chatter
ros2 topic echo /chatter # 查看话题数据
ros2 topic hz /turtle1/pose # 查看话题发布频率

# 查看消息类型
ros2 interface show std_msgs/msg/String

# ros2 topic pub topic_name msg_type msg_data args
# 手动发布话题消息,--once是一个可选参数,表示发布一条消息然后退出,若要以频率n HZ发布则使用--rate n
ros2 topic pub --once /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"  
# 示例2
ros2 topic pub /chatter std_msgs/msg/String 'data: "123"'

话题概念:

shell

# 关系可视化
rqt_graph

服务service

服务Service:

shell

ros2 service list # 查看服务列表
ros2 service list -t # 查看服务列表

#ros2 service type service_name
ros2 service type /spawn # 查看服务接口类型
ros2 service find turtlesim/srv/Spawn # 查找特定接口类型的所有服务

# 手动调用服务
#ros2 service call service_name service_type service_data args
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: ''}"

动作action

plaintext

ros2 action list
ros2 action list -t

# ros2 action info action_name
ros2 action info /turtle1/rotate_absolute
#ros2 action send_goal action_name action_type action_data
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.6}"
# 示例2,增加feedback
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.5}" --feedback

另请参阅:
ROS 2设计文章系列之二十一——动作(Actions) - 深圳季连AIGRAPHX的文章 - 知乎

通信接口interface

shell

ros2 interface list # 查看通信接口列表
ros2 interface show action_mags/msg/GoalInfo # 查看接口消息类型
#  ros2 interface package package_name
ros2 interface package turtlesim # 查看功能包中的通信接口

除了参数之外,话题、服务和动作都支持自定义接口,每一种通信方式所适用的场景各不相同,所定义的接口也被分为话题接口、服务接口、动作接口三种。

话题接口 xxx.msg

shell

# xxx.msg
int64 num

服务接口 xxx.srv

shell

# xxx.srv

#请求
int64 a
int64 b
---
#响应
int64 sum

动作接口 xxx.action

shell

# xxx.action

int32 order
---
int32[] sequence
---
int32[] partial_sequence

基础数据类型

shell

bool
byte
char
float32,float64
int8,uint8
int16,uint16
int32,uint32
int64,uint64
string

参数parameter
参数的组成由名字和值(键值组成)

shell

ros2 param list # 查看参数列表
# ros2 param get node_name parameter_name
ros2 param get /turtlesim background_g # 获取参数值
# ros2 param describe <node_name> <param_name>
ros2 param describe /turtlesim background_b
# ros2 param set <node_name> <parameter_name> <value>
ros2 param set /turtlesim background_g 150 # 设置参数值

# ros2 param dump <node_name>
ros2 param dump /turtlesim
#示例2 增加文件名
ros2 param dump /turtlesim > turtlesim.yaml # 保存参数到文件turtlesim.yaml
# 查看文件
cat ./turtlesim.yaml
# ros2 param load node_name parameter_file
ros2 param load /turtlesim ./turtlesim.yaml # 加载文件参数值
#ros2 param load /turtlesim turtlesim.yaml

Each parameter consists of a key, a value, and a descriptor. The key is a string and the value is one of the following types: bool, int64, float64, string, byte[], bool[], int64[], float64[] or string[]. By default all descriptors are empty, but can contain parameter descriptions, value ranges, type information, and additional constraints.

参阅官方文档:
https://docs.ros.org/en/humble/Concepts/Basic/About-Parameters.html

创建功能包

前置指令

shell

# Replace ".bash" with your shell if you're not using bash
# Possible values are: setup.bash, setup.sh, setup.zsh
source /opt/ros/humble/setup.bash

参考官方文档:创建功能包

Developing a ROS 2 package
ROS 2入门教程——2.2 创建您的第一个ROS 2软件包 - 深圳季连AIGRAPHX的文章 - 知乎
ROS2工作空间
Source the setup files
ROS 2入门教程——2.3 编写一个简单的发布者和订阅者(C++) - 深圳季连AIGRAPHX的文章 - 知乎
Creating a package

文件目录层级

文件目录层级图示

--name_ws

   --src

      --功能包1

         --节点1.1

         --节点1.2

      --功能包2

         --节点2.1

         --节点2.2

在工作空间中有一个src文件夹,并在其中创建软件包。一个工作空间下可以有多个功能包,一个功能包可以有多个节点(可执行文件)存在。每个功能包都位于各自的文件夹中,这些文件夹放置在工作空间的src文件夹中。功能包不能嵌套。

编译与构建知识基础
参阅附录一和附录二,了解编译过程、g++编译器、make和CMake基本概念,学习CMakeList.txt基本语法,了解构建的概念。
参阅官方文档:Developing a ROS 2 package
package有3类:Python,C++,Combined C++ and Python。
编译方式:ament_python,cmake,ament_cmake。

官方【ros index功能包列表】
安装已发布的功能包:

shell

sudo apt install ros-<version>-package_name

安装依赖(linux)

plaintext

rosdep install -i --from-path src --rosdistro humble -y
ros2 pkg --指令列表
create Create a new ROS2 package
executable Output a list of package specific executables
list Output a list of available packages
prefix Output the prefix path of a package输出某个包所在的路径前缀
xml Output the XML of the package manifest or a specific tag列出包的清单描述文件
shell

# 部分示例
ros2 pkg create <package-name>  --build-type  {cmake,ament_cmake,ament_python}  --dependencies <依赖名字>
ros2 pkg executables
ros2 pkg executables turtlesim
ros2 pkg list
ros2 pkg prefix  <package-name>

python

创建功能包指令(ament_python)

重要:在scr目录下执行指令

shell

# 在src目录下
# ros2 pkg create --build-type ament_python <package_name>
ros2 pkg create --build-type ament_python --node-name my_node my_package
# 官方指令
ros2 pkg create <pkg-name> --dependencies [deps] --build-type ament_python

# 更多的参数
cd chapt3/chapt3_ws/src
ros2 pkg create example_service_rclpy --build-type ament_python --dependencies rclpy example_interfaces  --node-name service_server_02
# --node-name 只支持一个节点文件

目录空间示例

--proj_ws

   --src

      --package_name

         --package_name

            --_ _init_ _.py

            --node.py

         --package.xml

         --resource

            --package_name

         --setup.cfg

         --setup.py

         --test

            --test_copyright.py

            --test_flake8.py

            --test_pep257.py

值得关注的文件:

定制package.xml文件

修改package.xml文件。

xml

<!--这申明了当该软件包的代码被执行时需要rclcpp和std_msgs库。-->
<depend>rclpy</depend>
<depend>std_msgs</depend>

添加入口点setup.py

python

# setup.py
entry_points={
        'console_scripts': [
            'my_script = my_package.my_script:main'
        ],
    },

# 示例
    entry_points={
        'console_scripts': [
            "node_02 = example_py.node_02:main"
        ],
    },
#c console_scripts是个数组

C++

创建功能包指令(ament_cmake)

shell

# 在src 目录下
# ros2 pkg create --build-type ament_cmake <package_name>
ros2 pkg create --build-type ament_cmake --node-name my_node my_package
# 官方指令
ros2 pkg create <pkg-name> --dependencies [deps] --build-type ament_cmake

目录空间示例

--proj_ws

   --src

      --package_name

         --CMakeList.txt

         --include

         --package_name

         --src

            --node.cpp

         --package.xml

定制CMakeList.txt

CMakeList.txt:

CMake

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
CMake

ament_target_dependencies(<executable-name> [dependencies])

# 示例,参考附录二
add_executable(node_01 src/node_01.cpp)
ament_target_dependencies(node_01 rclcpp)
CMake

# Install launch files
install(
  DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)

# Install nodes
install(
  TARGETS [node-names]
  DESTINATION lib/${PROJECT_NAME}
)

构建运行

在构建之前检查缺少的依赖项

shell

# 工作空间下
rosdep install -i --from-path src --rosdistro <distro> -y
shell

colcon build
source install/setup.bash
ros2 run my_package my_node
# 仅构建指定包
colcon build --packages-select YOUR_PKG_NAME 

节点和话题

重要:编写节点时,导入消息接口的流程

  1. 在CMakeLists.txt中导入,先find_packages再ament_target_dependencies。
  2. 在packages.xml中导入,具体是添加depend标签并将消息接口写入。
  3. 在代码中导入,C++中是#include"消息功能包/xxx/xxx.hpp"。

另请参阅:
Writing a simple publisher and subscriber (C++)
https://github.com/ros2/examples/tree/humble/rclcpp/topics

CMake

cmake_minimum_required(VERSION 3.5)
project(examples_rclcpp_minimal_publisher)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(publisher_lambda lambda.cpp)
ament_target_dependencies(publisher_lambda rclcpp std_msgs)

add_executable(publisher_member_function member_function.cpp)
ament_target_dependencies(publisher_member_function rclcpp std_msgs)

add_executable(publisher_member_function_with_type_adapter member_function_with_type_adapter.cpp)
ament_target_dependencies(publisher_member_function_with_type_adapter rclcpp std_msgs)

add_executable(publisher_member_function_with_unique_network_flow_endpoints member_function_with_unique_network_flow_endpoints.cpp)
ament_target_dependencies(publisher_member_function_with_unique_network_flow_endpoints rclcpp std_msgs)

add_executable(publisher_wait_for_all_acked member_function_with_wait_for_all_acked.cpp)
ament_target_dependencies(publisher_wait_for_all_acked rclcpp std_msgs)

add_executable(publisher_not_composable not_composable.cpp)
ament_target_dependencies(publisher_not_composable rclcpp std_msgs)

install(TARGETS
  publisher_lambda
  publisher_member_function
  publisher_member_function_with_type_adapter
  publisher_member_function_with_unique_network_flow_endpoints
  publisher_wait_for_all_acked
  publisher_not_composable
  DESTINATION lib/${PROJECT_NAME}
)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

参考:
Understanding topics

Launch文件

参阅:
Tutorials【Launching nodes】
Tutorials【Creating a launch file】
古月居【Launch:多节点启动与配置脚本】
范子琦博客【ROS2——教你写新版Launch文件】
CSDN博客【ros2 launch 用法以及一些基础功能函数的示例】

前置操作:创建Launch文件

文件目录

shell

cd proj_ws/src/my_package
mkdir launch
cd launch
touch xxxx.launch.py

参阅:https://docs.ros.org/en/dashing/Tutorials/Launch-system.html#ros-2-launch-system

C++ package

ament_package() 前面加上以下内容,才能在share文件夹中找到launch文件和config文件。

If you are creating a C++ package, we will only be adjusting the CMakeLists.txt file by adding:

CMake

# Install launch files.
install(DIRECTORY
  launch
  DESTINATION share/${PROJECT_NAME}/
)

install(DIRECTORY
  config
  DESTINATION share/${PROJECT_NAME}/
)

to the end of the file (but before ament_package() ).

Python Packages

plaintext

# For Python packages, your directory should look like this:
src/
  py_launch_example/
    launch/
    package.xml
    py_launch_example/
    resource/
    setup.cfg
    setup.py
    test/

In order for colcon to find the launch files, we need to inform Python’s setup tools of our launch files using the data_files parameter of setup.

python

# setup.py
import os
from glob import glob
from setuptools import setup

package_name = 'py_launch_example'

setup(
    # Other parameters ...
    data_files=[
        # ... Other data files
        # Include all launch files.
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*')))
    ]
)

# 其他参考
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
        (os.path.join('share', package_name, 'urdf'), glob('urdf/**')),
        (os.path.join('share', package_name, 'world'), glob('world/**')),
    ],

Writing the launch file

加载节点(node)示例

python

# simple.launch.py
from launch import LaunchDescription           # launch文件的描述类
from launch_ros.actions import Node            # 节点启动的描述类

def generate_launch_description():             # 自动生成launch文件的函数
    return LaunchDescription([                 # 返回launch文件的描述信息
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_pub', # 节点的可执行文件
        ),
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_sub', # 节点的可执行文件名
        ),
    ])

Your launch file should define the generate_launch_description() which returns a launch.LaunchDescription() to be used by the ros2 launch verb. 模板:

python

from launch import LaunchDescription
from launch_ros.actions import Node
python

def generate_launch_description():
   return LaunchDescription([

   ])

重映射示例

python

# remapping.launch.py
from launch import LaunchDescription      # launch文件的描述类
from launch_ros.actions import Node       # 节点启动的描述类

def generate_launch_description():        # 自动生成launch文件的函数
    return LaunchDescription([            # 返回launch文件的描述信息
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim1',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim2',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            executable='mimic',           # 节点的可执行文件名
            name='mimic',                 # 对节点重新命名
            remappings=[                  # 资源重映射列表
                ('/input/pose', '/turtlesim1/turtle1/pose'),         
                ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'), 
            ]
        )
    ])
# 最后一个节点也来自turtlesim软件包,但其可执行文件不同,其可执行文件为mimic。
# 将/input/pose话题名修改为/turtlesim1/turtle1/pose
# 将/output/cmd_vel话题名修改为/turtlesim2/turtle1/cmd_vel

节点mimic的/input/pose话题被重映射到/turtlesim1/turtle1/pose,而其/output/cmd_vel话题被重映射到/turtlesim2/turtle1/cmd_vel。这意味着mimic节点将会订阅/turtlesim1/sim的位姿话题并为/turtlesim2/sim的速度指令话题的订阅而重新发布该话题。换句话说,turtlesim2将会模仿turtlesim1的动作。

命令行示例

原命令行:

shell

ros2 run rviz2 rviz2 -d <PACKAGE-PATH>/rviz/turtle_rviz.rviz

对应Launch语句:

python

# rviz.launch.py
import os

from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法

from launch import LaunchDescription    # launch文件的描述类
from launch_ros.actions import Node     # 节点启动的描述类

def generate_launch_description():      # 自动生成launch文件的函数
   rviz_config = os.path.join(          # 找到配置文件的完整路径
      get_package_share_directory('learning_launch'),
      'rviz',
      'turtle_rviz.rviz'
      )

   return LaunchDescription([           # 返回launch文件的描述信息
      Node(                             # 配置一个节点的启动
         package='rviz2',               # 节点所在的功能包
         executable='rviz2',            # 节点的可执行文件名
         name='rviz2',                  # 对节点重新命名
         arguments=['-d', rviz_config]  # 加载命令行参数
      )
   ])

调用shell
使用ExecuteProcess调用shell命令

python

example_cmd = ExecuteProcess(
    cmd=['some-cmd', 'some-cmd'], #命令,用逗号隔开
    additional_env={'EXAMPLE_PATH': path}, #可以添加临时的环境变量
    output='screen'
)

ld.add_action(example_cmd)

参数(parameter)配置示例

python

# parameters.launch.py
from launch import LaunchDescription                   # launch文件的描述类
from launch.actions import DeclareLaunchArgument       # 声明launch文件内使用的Argument类
from launch.substitutions import LaunchConfiguration, TextSubstitution

from launch_ros.actions import Node                    # 节点启动的描述类

def generate_launch_description():                     # 自动生成launch文件的函数
   background_r_launch_arg = DeclareLaunchArgument(
      'background_r', default_value=TextSubstitution(text='0')     # 创建一个Launch文件内参数(arg)background_r
   )
   background_g_launch_arg = DeclareLaunchArgument(
      'background_g', default_value=TextSubstitution(text='84')    # 创建一个Launch文件内参数(arg)background_g
   )
   background_b_launch_arg = DeclareLaunchArgument(
      'background_b', default_value=TextSubstitution(text='122')   # 创建一个Launch文件内参数(arg)background_b
   )

   return LaunchDescription([                                      # 返回launch文件的描述信息
      background_r_launch_arg,                                     # 调用以上创建的参数(arg)
      background_g_launch_arg,
      background_b_launch_arg,
      Node(                                                        # 配置一个节点的启动
         package='turtlesim',
         executable='turtlesim_node',                              # 节点所在的功能包
         name='sim',                                               # 对节点重新命名
         parameters=[{                                             # ROS参数列表
            'background_r': LaunchConfiguration('background_r'),   # 创建参数background_r
            'background_g': LaunchConfiguration('background_g'),   # 创建参数background_g
            'background_b': LaunchConfiguration('background_b'),   # 创建参数background_b
         }]
      ),
   ])

调用参数文件

python

# learning_launch/parameters_yaml.launch.py
import os
from ament_index_python.packages import get_package_share_directory  # 查询功能包路径的方法
from launch import LaunchDescription   # launch文件的描述类
from launch_ros.actions import Node    # 节点启动的描述类


def generate_launch_description():     # 自动生成launch文件的函数
   config = os.path.join(              # 找到参数文件的完整路径
      get_package_share_directory('learning_launch'),
      'config',
      'turtlesim.yaml'
      )

   return LaunchDescription([          # 返回launch文件的描述信息
      Node(                            # 配置一个节点的启动
         package='turtlesim',          # 节点所在的功能包
         executable='turtlesim_node',  # 节点的可执行文件名
         namespace='turtlesim2',       # 节点所在的命名空间
         name='sim',                   # 对节点重新命名
         parameters=[config]           # 加载参数文件
      )
   ])

launch文件嵌套示例

python

# namespaces.launch.py
import os
from ament_index_python.packages import get_package_share_directory  # 查询功能包路径的方法
from launch import LaunchDescription                 # launch文件的描述类
from launch.actions import IncludeLaunchDescription  # 节点启动的描述类
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import GroupAction               # launch文件中的执行动作
from launch_ros.actions import PushRosNamespace      # ROS命名空间配置

def generate_launch_description():                   # 自动生成launch文件的函数
   parameter_yaml = IncludeLaunchDescription(        # 包含指定路径下的另外一个launch文件
      PythonLaunchDescriptionSource([os.path.join(
         get_package_share_directory('learning_launch'), 'launch'),
         '/parameters_nonamespace.launch.py'])
      )

   parameter_yaml_with_namespace = GroupAction(      # 对指定launch文件中启动的功能加上命名空间
      actions=[
         PushRosNamespace('turtlesim2'),
         parameter_yaml]
      )

   return LaunchDescription([                        # 返回launch文件的描述信息
      parameter_yaml_with_namespace
   ])

官方完整示例:
http://docs.ros.org/en/humble/How-To-Guides/Launch-file-different-formats.html

运行launch文件

shell

ros2 launch <package_name> <launch_file_name>

For packages with launch files, it is a good idea to add an exec_depend dependency on the ros2launch package in your package’s package.xml

xml

<exec_depend>ros2launch</exec_depend>

设置参数
To set the arguments that are passed to the launch file, you should use key:=value syntax. For example, you can set the value of background_r in the following way:

shell

ros2 launch <package_name> <launch_file_name> background_r:=255

加载Gazebo

gazebo提供的功能节点spwan_entity:

python

# load_urdf_into_gazebo.launch.py

world_file_path = 'worlds/neighborhood.world'
pkg_path = os.path.join(get_package_share_directory(package_name))
world_path = os.path.join(pkg_path, world_file_path) 
# Include the Gazebo launch file,
# provided by the gazebo_ros package
mbot = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([os.path.join(
            get_package_share_directory(package_name),'launch','mbot.launch.py'
        )]), launch_arguments={'use_sim_time': 'true', 'world':world_path}.items())
gazebo = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([os.path.join(
            get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]),
     ) 
# Run the spawner node from the gazebo_ros package. 
# The entity name doesn't really matter if you only have a single robot.
spawn_entity = Node(package='gazebo_ros', executable='spawn_entity.py',
                    arguments=['-topic', 'robot_description',
                               '-entity', 'mbot',
                               '-x', spawn_x_val,
                               '-y', spawn_y_val,
                               '-z', spawn_z_val,
                               '-Y', spawn_yaw_val],
                    output='screen')
# Launch them all!
return LaunchDescription([
    mbot,
    gazebo,
    spawn_entity,
])
python

# 加载世界模型
gazebo_world_path = os.path.join(pkg_share, 'world/fishbot.world')

# Start Gazebo server
start_gazebo_cmd =  ExecuteProcess(
    cmd=['gazebo', '--verbose','-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so', gazebo_world_path],
    output='screen')

配置Launch文件的完整说明
ROS2中的URDF系列教程(七):ROS 2中如何将URDF加载到Gazebo - 深圳季连AIGRAPHX的文章 - 知乎

加载rviz2

rviz是一个三维可视化平台,用于数据可视化。

python

rviz_config_file_path = 'rviz/urdf_gazebo_config.rviz'
default_rviz_config_path = os.path.join(pkg_share, rviz_config_file_path)
declare_rviz_config_file_cmd = DeclareLaunchArgument(
    name='rviz_config_file',
    default_value=default_rviz_config_path,
    description='Full path to the RVIZ config file to use')
declare_use_rviz_cmd = DeclareLaunchArgument(
    name='use_rviz',
    default_value='True',
    description='Whether to start RVIZ')
# Launch RViz
start_rviz_cmd = Node(
    package='rviz2',
    executable='rviz2',
    name='rviz2',
    output='screen',
    arguments=['-d', rviz_config_file])
ld.add_action(declare_rviz_config_file_cmd)
ld.add_action(declare_use_rviz_cmd) 
ld.add_action(start_rviz_cmd)

附录一:前置知识--程序编译过程

GCC编译器驱动程序读取源程序文件hello.c,并把它翻译成一个可执行目标文件hello。这个翻译过程分为四个阶段:预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)、链接(Linking)。执行这四个阶段的程序(预处理器、编译器、汇编器、和链接器)一起构成了编译系统。

编译过程编译过程

shell

# 预处理Preprocessing
gcc -E test.c -o test.i
# 编译Compilation
gcc -S test.i -o test.s
# 汇编Assemble
gcc -c test.s -o test.o
# 链接Linking
gcc test.o -o test

预处理(Preprocessing)

目标文件常常按照特定格式来组织,在linux下,它是ELF格式(Executable Linkable Format,可执行可链接格式),而在windows下是PE(Portable Executable,可移植可执行)。

  1. 预处理器(cpp)将所有的#define删除,并且展开所有的宏定义。
  2. 处理所有的条件预编译指令,比如#if、#ifdef、#elif、#else、#endif等。
  3. 处理#include预编译指令,将被包含的文件直接插入到预编译指令的位置。
  4. 删除所有的注释。
  5. 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们。
  7. 使用gcc -E hello.c -o hello.i命令来进行预处理, 预处理得到的另一个程序通常是以.i作为文件扩展名。
    经过预处理之后代码体积会大很多,相当于可执行文件一倍大小。预处理之后的程序还是文本,可以用文本编辑器打开。

编译阶段(Compilation)

编译器(ccl)将预处理完的(.i) 临时文件hello.i进行一系列的词法分析、语法分析、语义分析和优化,转换为具有汇编级指令(低级代码)的汇编文件 (.s)hello.s。
汇编语言为不同高级语言的不同编译器提供了通用的输出语言。
编译过程可分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成、目标代码优化。

  1. 词法分析:扫描器(Scanner)将源代的字符序列分割成一系列的记号(Token)。lex工具可实现词法扫描。
  2. 语法分析:语法分析器将记号(Token)产生语法树(Syntax Tree)。yacc工具可实现语法分析(yacc: Yet Another Compiler Compiler)。
  3. 语义分析:静态语义(在编译器可以确定的语义)、动态语义(只能在运行期才能确定的语义)。
  4. 源代码优化:源代码优化器(Source Code Optimizer),将整个语法书转化为中间代码(Intermediate Code)(中间代码是与目标机器和运行环境无关的)。中间代码使得编译器被分为前端和后端。编译器前端负责产生机器无关的中间代码;编译器后端将中间代码转化为目标机器代码。
  5. 目标代码生成:代码生成器(Code Generator).
  6. 目标代码优化:目标代码优化器(Target Code Optimizer)。
    整个程序代码由编译器软件一次性解析(语法分析),并通过终端窗口告诉我们源代码中存在的任何语法错误或警告。编译成汇编文件任然是文本文件,大小已经非常小了,没有像预处理的时候文件大小这么臃肿。

汇编阶段(Assemble)

汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o是一个二进制文件

链接阶段(Linking)

链接过程使用链接器将该目标文件与其他目标文件、库文件、启动文件等链接起来生成可执行文件。附加的目标文件包括静态连接库和动态连接库。 链接是由叫链接器(linker)的程序自动执行的。
链接使得分离编译(separate compilation)成为可能,我们不用将一个大型的应用程序组织成一个巨大的源文件,而是可以把它分解成为更小、更好管理的模块,可以独立的修改和编译这些模块。当我们改变这些模块中的一个时,只需要简单的重新编译它,并重新链接应用,而不必重新编译其他文件。

编译示例

C

// Simple Hello World program in C
#include<stdio.h>
int main()
{
    printf("Hello World!");
    return 0;
}

执行编译

shell

gcc -save-temps hello.c -o compilation

链接库

链接库链接库
在widows平台下,静态链接库是.lib文件,动态库文件是.dll文件。在linux平台下,静态链接库是.a文件,动态链接库是.so文件。
在实际应用中,有些公共代码需要反复使用,就把这些代码编译成为“库”文件。
库文件中包含函数的实现。 静态库:在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
共享库与静态库不同,共享库在链接间段只在可执行文件中设置使用的库,运行时由操作系统动态加载到内存上执行。
共享库是在运行程序时才动态加载库方法,若删除共享库,则程序运行时进行动态加载库方法时找不见共享库,程序将无法执行。共享库由于运行要动态加载库文件,则运行速度慢,但程序体积小,方便版本升级,程序不用重新编译。
不过,如果仅有.a或.so库文件,那么我们并不知道里面的函数到底是什么,调用的形式又是什么样。为了使用这个库,我们需要提供一个头文件,说明这些库里都有些什么。因此,对于库的使用者,只要拿到了头文件和库文件,就可以调用这个库了。

参考引用

程序详细编译过程(预处理、编译、汇编、链接) - YaoJ1aHao的文章 - 知乎
C语言的编译过程详解 - Lion-Zwart的文章 - 知乎
GCC编译过程(预处理->编译->汇编->链接) - 程序猿编码的文章 - 知乎
引自CSDN【gcc/g++ 链接库的编译与链接】
浅谈静态库和动态库 - 守望的文章 - 知乎
C++静态库与动态库深入研究——静态库篇! - C语言编程俱乐部的文章 - 知乎
Linux—静态库与共享库——详解

附录二:前置知识--编译器

在windows下,一般使用Microsoft提供的VS系列IDE即可,功能强大,使用方便。Linux系统编译C++时多用g++,g++的功能是将包含了代码的文本文件编译(预处理、编译、汇编、链接)成可执行的文件。

集成开发环境(Integrated Development Environment,IDE)

实际开发中,除了编译器是必须的工具,我们往往还需要很多其他辅助软件,例如:

这些工具通常被打包在一起,统一发布和安装,例如 Visual StudioDev C++、Xcode、Visual C++ 6.0、C-Free、Code::Blocks 等,它们统称为集成开发环境(IDE,Integrated Development Environment)。在实际开发中,我一般也是使用集成开发环境,而不是单独地使用编译器。
通常 IDE 仅限于一种编码语言或框架。主流的IDE有:
Visual Studio,Sublime Text 3,Vim(命令行软件),IntelliJ IDEA,Xcode IDE(面向apple),PyCharm(python),Microsoft Visual C++等

GCC/G++是什么

GNU编译器集合(GNU Compiler Collection,GCC)是一个能够编译多种语言的编译器,如c++、C、Objective-C、Java和Fortran。GCC开发是由Richard Stallman作为GNU项目的一部分进行的。
GCC指令:

shell

# 现在我们有源文件hello.c,下面是一些gcc的使用示例:
gcc -E hello.c -o hello.i   对hello.c文件进行预处理,生成了hello.i 文件
gcc -S hello.i -o hello.s    对预处理文件进行编译,生成了汇编文件
gcc -c hello.s -o hello.o  对汇编文件进行编译,生成了目标文件
gcc hello.o -o hello 对目标文件进行链接,生成可执行文件
gcc hello.c -o hello 直接编译链接成可执行目标文件
gcc -c hello.c 或 gcc -c hello.c -o hello.o 编译生成可重定位目标文件

gccgcc

G++是什么 GNU提供了C++的优化编译器,也就是众所周知的G++。在编译阶段,G++调用GCC,因此G++是完整的编译器。

关于gcc与g++

MinGW 就是 GCC 的 Windows 移植版本。

自由软件基金会(FSF)和GUN

FSF -- The Free Software Foundation

The Free Software Foundation (FSF) is a nonprofit with a worldwide mission to promote computer user freedom.

https://www.fsf.org/

FSF(自由软件基金会)是一个非盈利组织。使命是在全球范围内促进计算机用户的自由,捍卫所有软件用户的权利。FSF开发了GNU自由软件操作系统。 “自由软件基金会(FSF)是一个非盈利组织。我们的使命是在全球范围内促进计算机用户的自由。我们捍卫所有软件用户的权利。”

GUN项目
https://www.gnu.org/
GNU是一个类Unix操作系统。

make

代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),叫做构建(build)。
我们的程序只有一个源文件时,直接就可以用gcc命令编译它。但是,程序包含很多个源文件时,用gcc命令逐个去编译时,就很容易混乱而且工作量大,所以出现了下面make工具。
斯图亚特·费尔德曼(Stuart Feldman)在1977年在贝尔实验室(Bell Labs)里制作了这个软件。2003年,斯图亚特·费尔德曼因发明了这样一个重要的工具而接受了美国计算机协会(ACM)颁发的软件系统奖。

make只是一个根据指定的Shell命令进行构建的工具。它的规则很简单,你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。

make通过读取“makefile”的文件以自动化建构软件。make是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接的。make工具就根据makefile中的命令进行编译和链接的。makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。
许多现代软件的开发中(如Microsoft Visual Studio),集成开发环境已经取代make,但是在Unix环境中,仍然有许多工程师采用make来协助软件开发。

cmake

cmake通过调用CMakeLists.txt直接生成Makefile,更加便捷。
CMake是一个跨平台工具,CMakelists.txt是linux和windows通用的,利用cmake,可以根据CMakelist.txt在不同的操作系统上产生不同的自动编译脚本文件。
CMake 的所有操作都是通过编辑 CMakeLists.txt 来完成的。
参阅cmake官网:https://cmake.org/
cmake详细语法和介绍请参阅附录二第6节参考引用部分的链接。

必须掌握的内容如下:

CMake

# cmake_minimum_required 指定使用 CMake 的最低版本号
cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable 需要指定生成可执行文件的名称和相关源文件
add_executable(Tutorial tutorial.cxx)
CMake

# 使用 find_package() 命令找到包
find_package(Qt4 4.7.0 REQUIRED) # CMake provides a Qt4 find-module

参阅:
官方文档【camke包 find_package】

CMake

# 优化
add_executable(${PROJECT_NAME} tutorial.cpp)
# 生成可执行文件需要指定相关的源文件,如果有多个,那么就用空格隔开
add_executable(${PROJECT_NAME} a.cpp b.cpp c.cpp)

# 也可以用一个变量来表示这多个源文件
# set 命令指定 SRC_LIST 变量来表示多个源文件
# 用${var_name} 获取变量的值。
set(SRC_LIST a.cpp b.cpp c.cpp)
add_executable(${PROJECT&#95;NAME}${SRC_LIST})
CMake

# target_link_libraries - 为target(目标) 添加需要动态链接库
target_link_libraries(<target> ... <item>... ...)
#设置要链接的库文件的名称
target_link_libraries(first_node rclcpp rcutils)

# 链接
TARGET_LINK_LIBRARIES(${PROJECT&#95;NAME}${PROJECT_SOURCE_DIR}/lib/hello.lib)

注意:
link_libraries用在add_executable之前,target_link_libraries用在add_executable之后 参阅:
官方文档中文【目标链接库target_link_libraries】

随着现代的集成开发环境(IDE)的诞生,特别是非Unix的平台上,很多程序员不再手动管理依靠关系检查,甚至不用去管哪些文件是这个项目的一部分,而是把这些任务交给了他们的开发环境去做。类似的,很多现代的编程语言有自己特别的高效的依赖关系的设置方法。

cmake使用流程
先创建cpp文件,text01.cpp

c++

#include <iostream>
using namespace std;
int main()
{
    cout<<"welcome to VS code world!"<<endl;
    return 0;
}

再编写CMakeLists.txt文件

CMake

cmake_minimum_required(VERSION 3.15)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial text01.cpp)

打开终端

plaintext

mkdir build
cd build
cmake ..
make

如果是windows平台下,默认编译器是vs,如需使用gcc/g++编译器则需要安装mingw,指定gcc/g++编译器,指定编译器的指令为

plaintext

cmake -G"MinGW Makefiles" ..

注:创建build文件夹的目的是隔离源代码和cmake产生的中间文件。

参考引用

cmake教程

参考资料库