配置ROS2
ros为了使一个系统中可以存在多个不同版本的ros,将ros的配置指令写成了脚本,要用ros前根据自己所需要执行对应的脚本
source /opt/ros/jazzy/setup.zsh
如果你只用一个版本的ros,可以将执行脚本的指令写到对应shell的配置文件里,每次启动shell自动执行ros配置脚本。
配置好ros环境后还需要配置ros通信使用的域id,domain_id,ros通信使用DDS中间件,为了避免相互干扰,设置里通信域,不同域中的ros节点无法通信,默认通信域id为0,可以自己设置。
export ROS_DOMAIN_ID=<your_domain_id>
ros2整体结构
ros2是一个机器人操作系统,相较于电脑操作系统,功能简化了很多,只需要实现网络&通信这么一件事情,其他事情都由操作系统实现或者压根儿没用其他功能。
- 在ros2中代码管理单位是软件包package,运行时最小参与单位是节点node,概念类似于操作系统中的进程,ros2负责实现node间的通信。
- node发布topic,其他node订阅topic,就可以实现publishers-subscribers模式的通信,这有点像网络广播,这个广播可以是本地的也可以是跨机器的,ros提供了通信域概念,不同通信域间不互通,相同通信域都可以互通,就上面配置的domain_id,这个概念有点像局域网
- ros2中的通信是分布式的,跟ros1不同,ros1有个master节点,所有节点向他注册。ros2是当新节点建立时会向所有相同通信域的节点发消息并接受他们的反馈,来得知当下通信域的情况,并且当node下线时也会向其他node发消息通知。
- 没有master,新node是如何发现同一域内的node并通知的呢?查了一下资料,有的说是域id和真实的物理网络端口号有映射关系,具体没有细究,后续再说,现在会用就行。
- node间通信有三种类型:
- msg信息:node新建一个topic发布msg,另一个接收msg
- srv请求-相应:一个node发请求,另一个收到请求后返回响应
- action:一个长期的过程,node发送请求,另一个node收到后不断反馈,直到返回执行结果。
- 通信报文样式分别定义在每个package的
msg
,srv
,action
文件夹下,msg只需要定义信息格式,srv需要定义请求格式和响应格式,action需要定义请求格式、反馈格式、结果格式。中间用---
隔开
一个action定义样例:
# request constants
int8 FOO=1
int8 BAR=2
# request fields
int8 foobar
another_pkg/AnotherMessage msg
---
# response constants
uint32 SECRET=123456
# response fields
another_pkg/YetAnotherMessage val
CustomMessageDefinedInThisPackage value
uint32 an_integer
配置乌龟机器人
安装仿真节点
sudo apt install ros-jazzy-turtlesim
使用ros2 pkg查看是否安装成功
ros2 pkg executables turtlesim
# 列举出turtlesim package下的的可执行节点
执行仿真器,这个节点会接收机器人控制指令进行相应移动,可以简单理解为一个三自由度机器人仿真器
ros2 run turtlesim turtlesim_node
#执行turtlesim package下的turtlesim_node节点
可以使用ros2 node list查看节点
ros2 node list
#查看当下执行的节点
执行控制节点
ros2 run turtlesim turtle_teleop_key
发现目前这个仿真器不能同时执行旋转和直行两个行为,当同时发出旋转和直行两个指令时,第二个指令会把第一个指令打断。
可视化工具
安装可视化工具rqt
sudo apt install '~nros-jazzy-rqt*'
# 安装所有匹配该正则表达式的包
rqt是一个图形化框架,实现各种功能的是对应的插件,例如rqt_graph是显示各个节点间的关系的,rqt_service是显示现有service的,rqt_console是查看节点log日志的等等。可以从plugins中选择运行自己需要的插件。
可以使用rqt_service给spawn发命令,生成新的乌龟。
也可以使用kill服务杀死乌龟,或者使用teleport_absolute控制乌龟绝对位置。
通过rqt_graphic插件可以看到node间的通信关系:
可以使用remapping将控制节点的输出转移到turtle2/cmd_vel这个topic上
ros2 run turtlesim turtle_teleop_key --ros-args --remap turtle1/cmd_vel:=turtle2/cmd_vel
这个时候再用ros2 node list发现会出现warning,有两个同名的node在执行
可以使用命令行修改一下名称
ros2 run turtlesim turtle_teleop_key --ros-args -r __node:=turtle_control1
# 重新启动控制turtle1的控制节点并改名为turtle_control1
也可以使用ros2 node info来查看node的信息
$ ros2 node info /turtle_control1
# output
/turtle_control1
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
/turtle1/cmd_vel: geometry_msgs/msg/Twist
Service Servers:
/turtle_control1/describe_parameters: rcl_interfaces/srv/DescribeParameters
/turtle_control1/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/turtle_control1/get_parameters: rcl_interfaces/srv/GetParameters
/turtle_control1/get_type_description: type_description_interfaces/srv/GetTypeDescription
/turtle_control1/list_parameters: rcl_interfaces/srv/ListParameters
/turtle_control1/set_parameters: rcl_interfaces/srv/SetParameters
/turtle_control1/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
Service Clients:
Action Servers:
Action Clients:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
可以用ros2 topic/service/action list/info/echo/pub/hz等一系列工具来查看topic/service/action的具体情况,这些工具约等于创建了一个临时node。
此外每个node都有自己的参数,ros2提供了工具来调整这些参数
ros2 param list #查看每个节点的参数
ros2 param get <node_name> <parameter_name> # 查看参数的类型
ros2 param set <node_name> <parameter_name> <value> # 设置参数值
ros2 param dump <node_name> # 查看节点当下所有参数的值
ros2 param load <node_name> <parameter_file> #从文件中加载节点参数
除了使用上述的ros2 run命令行单独执行一个节点,还可以使用ros2 launch批量启动节点
ros2 launch turtlesim multisim.launch.py
# 自动运行multisim.launch.py脚本
脚本内容:
# turtlesim/launch/multisim.launch.py
from launch import LaunchDescription
import launch_ros.actions
def generate_launch_description():
return LaunchDescription([
launch_ros.actions.Node(
namespace= "turtlesim1", package='turtlesim', executable='turtlesim_node', output='screen'),
launch_ros.actions.Node(
namespace= "turtlesim2", package='turtlesim', executable='turtlesim_node', output='screen'),
])
ros还提供了ros bag指令,方便将topic和service的发布信息进行记录打包,方便进行在别的地方复现,例如上面对turtle的控制指令可以打包然后在实际的机器人上进行播放,就可以进行复现
ros2 bag record -o output_bag turtle1/cmd_vel
# 录制turtle1/cmd_vel的数据并保保存到output_bag中
# ros2 bag record --service <service_names>
# 录制service的数据
ros2 info output_bag
# 查看output_bag的信息
ros2 bag play output_bag
# 播放output_bag的数据
创建workspace & package
上述都是用的demo中的代码,下面自己写一个自己的node来控制乌龟。
ROS2中每个项目构成一个工作空间workspace,每个workspace下由可以根据功能细分打包为pacakge功能包,ROS2使用colcon来进行项目工程管理,相比于ROS1中的catkin更好用
官方称colcon为编译工具,build tool,工作流程是:由colcon来确定编译那些包以及他们的先后顺序,每个功能包中使用cmake,make等编译系统build system。
安装colcon
sudo apt install python3-colcon-common-extensions
安装ros的依赖管理工具,rosdep,国内ros大佬小鱼搞了一个国内链接版的rosdepc,能避免网络问题。rosdep只负责查找依赖情况等,具体安装还是调用的apt这些工具
pip install rosdepc
rosdep init # 初始化
rosdep update # 更新远程源
rosdep install -i --from-path src #为src目录下的package检查依赖
对于Ubuntu24.04可能不适用,因为Ubuntu24.04采用来采用 PEP-668 中定义的标准,即pip不能安装到全局上下文环境中,可以使用venv建一个虚拟环境来安装,也可以把/usr/lib/python3.xx下的 EXTERNALY-MANAGED 文件删除,最简单的解决方案就是用另一个安装器pipx,当然pipx也要提前安装
cite: 解决方案
新建项目目录并下载官方教程代码
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
git clone git@github.com:ros/ros_tutorials.git -b jazzy
使用colcon进行编译
colcon build --symlink-install --allow-overriding turtlesim
# --symlink-install指install文件夹下使用链接直接链接到build文件夹下的文件而不是复制一份,可以节省空间
# --allow-overriding turtlesim指覆盖ROS自带编译的turtlesim,不加这个会warning
感觉搞个install文件夹完全没必要,直接在build里面找不就行了,或者在build里面搞一个bin文件夹
如果用vscode打开代码会看到红线报错,说没用srv
文件夹下没用xxx.hpp
文件,正常情况,这是因为ros2在编译时会自动根据srv下的srv文件生成hpp文件
更详细来说就是:
在ROS中提供来ROS客户端API(rcl API)和ROS中间件API(rmw API),
其中rcl API提供了将rosidl文件(也就是定义了通信格式那些文件,msg,srv,action)转换为具体编程语言的代码生成器rosidl_generator,它会在编译时辅助生成与rosidl文件同名的代码文件,所以这就是为什么没用xxx.hpp
的原因。
如果查看CMakeLists.txt文件,会发现在里面有这么一句:
find_package(rosidl_default_generators REQUIRED)
这就是在找rosidl_default_generators库。
编译后在install文件夹下会由local_setup.bash和setup.bash两个脚本,执行前者会将当下workspace中的package添加到环境中,后者会直接添加到全局环境中,在其他workspace中也能用。
ROS官方给出的tutorials中是这么说的,ROS2的基础被称为基础环境underlay,当新建一个workspace后,这个workspace就是一个新的环境overlay,source一下install里面的local_setup.sh脚本后就进入了overlay环境,而source一下setup.sh脚本就只是将一些环境变量等加入到underlay环境中。
在overlay中存在与underlay中同名的package时,会覆盖掉underlay中的package包。
我大致看了一下local_setup.sh和setup.sh,local_setup.sh比后者复杂一些,多出来的代码主要是在执行类似于underlay中的setup.sh的任务。
这时我们就使用我们自己的turtlesim覆盖掉之前我们安装的turtlesim了,我们可以进行一下DIY,例如改一下turtlesim的窗口名称和窗口大小。
找到这句代码
setWindowTitle("TrutleSim");
// 改为
setWindowTitle("MyTrutleSim");
turtlesim中涉及到很多Qt的内容,不过不影响主要逻辑,不需要全部理解。
接下来我们创建一个自己的package,并创建一个节点
# ros2 pkg create --build-type ament_cmake --license Apache-2.0 <package_name>
ros2 pkg create --build-type ament_cmake --license Apache-2.0 turtle_control
# 创建一个C++类型的package,选取Apache-2.0作为开源协议
这时这个package还是空的,要添加节点跟编写正常一样写一个程序并配置好CMakeLists.txt就ok了
#include <iostream>
#include <rclcpp/rclcpp.hpp>
int main(int argc, char ** argv)
{
rclcpp::Node::SharedPtr node_header;
rclcpp::init(argc, argv);
node_header = rclcpp::Node::make_shared("hello");
while(true)
{
std::cout << "Hello, world!" << std::endl;
if(std::cin.get() == 'q')
{
break;
}
}
rclcpp::shutdown();
return 0;
}
CMakeLists.txt中加上这几句
find_package(rclcpp REQUIRED)
add_executable(turtle_control src/hello.cpp)
target_link_libraries(turtle_control PRIVATE
rclcpp::rclcpp
)
......
install(
TARGETS turtle_control
DESTINATION lib/${PROJECT_NAME}
)
进行build
colcon build --packages-select my_package
# colcon build默认构建src下的全部package,可以使用--packages-select选择要构建的package
source ./install/local_setup.zsh
# 告诉ros当下overlay中的package信息
ros2 run turtle_control turtle_control
如果将这个节点再进行扩展,添加上action或者msg,就可以成为一个控制节点,可以看turtlesim的tutorials文件夹下有个minic.cpp,里面就写了控制节点的一个最小实现。
这基本上就是ROS的基础内容了,再深入就是各种工具的使用以及这些概念的具体实现的问题了,用不到的情况下纸上谈兵没用意义。
node和topic除了上述这种使用方式以外,还可以作为参数节点,不断广播参数,这有点像大模型并行训练中的参数服务器,只不过那个的通信带宽可能要求更高一些。
Reference
ROS2 Documentation