ROS程序设计系列 - 1.ROS介绍
1. 源由
目前,在最有路径计算和SLAM(Simultaneous Localization and Mapping)地图重建方面,ROS系统提供的生态最为完善。
因此,很多机器人方面的研发人员使用最多的就是ROS(Robot Operating System)系统。
另外,前面我们了解了无人机路径计算算法:
- Linux 35.5 + JetPack v5.1.3@FC-Planner编译安装
- Linux 35.5 + JetPack v5.1.3@ego-planner-swarm编译安装
- Linux 35.5 + JetPack v5.1.3@ ego-planner编译安装
- Linux 35.5 + JetPack v5.1.3@RACER编译安装
- Linux 35.5 + JetPack v5.1.3@FUEL编译安装
- Linux 35.5 + JetPack v5.1.3@FUEL编译安装
目前还是基于ROS,尚未对ROS2进行移植,我相信在一段时间内,估计也不可能快速的切换到ROS2.
除此之外,我们还有Ardupilot Rover/Copter的应用(今后可能还有Boat/Submarine等)
因此,我们将学习和整理下ROS系统的架构和基本编程应用。
2. ROS介绍
从概念定义上,个人觉得ROS可以定义为一个中间件(middleware)。
其底层仍然是Linux系统,但是ROS应用编程采用的是ROS API,封装了很多标准ROS包,这些包不仅仅简单功能API,还有很多动态管理服务,因此中间件的说法更为合适。
在 ROS(Robot Operating System)中,中间件起到了关键作用,用于节点之间的通信和数据交换。ROS 中的中间件通常指的是通信框架,主要包括以下几个关键组件:
-
ROS Master:ROS Master 是整个 ROS 网络的核心,它负责节点注册、查找和管理。每个 ROS 网络中都需要一个 Master,它可以记录节点的信息,并帮助节点找到彼此。Master 并不参与实际数据的传输,而只是提供节点间的联系信息。
-
节点(Nodes):节点是 ROS 中的基本执行单位,每个节点是一个独立的进程,负责特定的功能。节点可以通过发布和订阅(publish/subscribe)机制、服务(service)和动作(action)等方式进行通信。
-
话题(Topics):话题是节点之间进行异步通信的渠道。一个节点可以发布消息到某个话题,其他节点可以订阅这个话题以接收消息。话题适用于需要频繁更新的数据流,比如传感器数据。
-
消息(Messages):消息是 ROS 中节点之间传递的数据结构。ROS 提供了一套标准消息类型,开发者也可以自定义消息类型。
-
服务(Services):服务提供了一种同步通信方式,适用于需要请求-响应模式的场景。一个节点可以定义一个服务,其他节点可以通过调用该服务来请求特定的操作并等待响应。
-
动作(Actions):动作是一种增强的服务机制,适用于需要长时间运行并且需要反馈的任务。动作可以提供任务执行的进度和中途取消的能力。
ROS 中的中间件通常基于发布-订阅模式(Publish-Subscribe Pattern)和客户端-服务器模式(Client-Server Pattern),这使得 ROS 能够灵活、高效地处理机器人系统中的各种通信需求。
ROS 1 中,默认使用的是基于 TCP 的通信协议(TCPROS),也可以使用 UDP(UDPROS)来实现低延迟的通信。在 ROS 2 中,采用了数据分发服务(DDS)作为默认的中间件,这提供了更好的实时性、可扩展性和安全性。DDS 是一种工业标准的中间件协议,广泛应用于分布式实时系统。
3. 关键要点
3.1 中间件组成
- 2007年斯坦福人工智能实验室开始投入
- 2013年OSRF组织接管项目
- 当前已经成为机器人行业不成文的一个标准平台
3.2 功能特性
- 消息解耦
- 分布式部署
- 多语言支持
- 轻量化组件
- 开源代码
3.3 消息分发
3.3.1 ROS Master
ROS Master 是整个 ROS 网络的核心,它负责节点注册、查找和管理。
命令:
$ roscore
3.3.2 ROS Node
节点是 ROS 中的基本执行单位,每个节点是一个独立的进程,负责特定的功能。
命令:
$ rosrun package_name node_name
$ rosnode list
$ rosnode info node_name
3.3.3 ROS Topic
话题是节点之间进行异步通信的渠道。
命令:
$ rostopic list
$ rostopic echo /topic
$ rostopic info /topic
3.3.4 ROS Message
消息是 ROS 中节点之间传递的数据结构。
命令:
$ rostopic type /topic
$ rostopic pub /topic type data
3.4 tutorial示例
以Linux 35.5 + ros-noetic@Jetson Orin Nano为例:
- 安装示例程序
$ sudo apt-get install ros-noetic-roscpp-tutorials
- 运行roscore
$ roscore
... logging to /home/daniel/.ros/log/fe950290-2b71-11ef-b45c-200b7460c5b7/roslaunch-nvidia-8506.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.
started roslaunch server http://nvidia:45373/
ros_comm version 1.16.0
SUMMARY
========
PARAMETERS
* /rosdistro: noetic
* /rosversion: 1.16.0
NODES
WARNING: IP addresses 0.0.0.0 for local hostname 'nvidia' do not appear to match
any local IP address (127.0.0.1,192.168.1.19,172.17.0.1). Your ROS nodes may fail to communicate.
Please use ROS_IP to set the correct IP address to use.
auto-starting new master
process[master]: started with pid [8524]
ROS_MASTER_URI=http://nvidia:11311/
setting /run_id to fe950290-2b71-11ef-b45c-200b7460c5b7
process[rosout-1]: started with pid [8544]
started core service [/rosout]
- 运行talker程序
$ rosrun roscpp_tutorials talker
[ INFO] [1718495429.976510980]: hello world 0
[ INFO] [1718495430.076535932]: hello world 1
[ INFO] [1718495430.176508181]: hello world 2
[ INFO] [1718495430.276509423]: hello world 3
[ INFO] [1718495430.376511752]: hello world 4
[ INFO] [1718495430.476498946]: hello world 5
[ INFO] [1718495430.576503228]: hello world 6
[ INFO] [1718495430.676507413]: hello world 7
[ INFO] [1718495430.776500207]: hello world 8
[ INFO] [1718495430.876500553]: hello world 9
- 查看rosnode
$ rosnode list
/rosout
/talker
$ rosnode info /talker
--------------------------------------------------------------------------------
Node [/talker]
Publications:
* /chatter [std_msgs/String]
* /rosout [rosgraph_msgs/Log]
Subscriptions: None
Services:
* /talker/get_loggers
* /talker/set_logger_level
contacting node http://nvidia:35617/ ...
Pid: 8596
Connections:
* topic: /rosout
* to: /rosout
* direction: outbound (46207 - 127.0.0.1:38730) [11]
* transport: TCPROS
- 查看topic
$ rostopic info /chatter
Type: std_msgs/String
Publishers:
* /talker (http://nvidia:35617/)
Subscribers: None
$ rostopic type /chatter
std_msgs/String
$ rostopic echo /chatter
data: "hello world 3184"
---
data: "hello world 3185"
---
data: "hello world 3186"
---
data: "hello world 3187"
---
data: "hello world 3188"
---
data: "hello world 3189"
---
data: "hello world 3190"
---
data: "hello world 3191"
---
data: "hello world 3192"
---
data: "hello world 3193"
---
data: "hello world 3194"
$ rostopic hz /chatter
subscribed to [/chatter]
average rate: 10.001
min: 0.100s max: 0.100s std dev: 0.00005s window: 9
average rate: 10.001
min: 0.100s max: 0.100s std dev: 0.00004s window: 19
- 运行listener程序
$ rosrun roscpp_tutorials listener
[ INFO] [1718495980.876318035]: I heard: [hello world 4752]
[ INFO] [1718495980.976116422]: I heard: [hello world 4753]
[ INFO] [1718495981.076103806]: I heard: [hello world 4754]
[ INFO] [1718495981.176119224]: I heard: [hello world 4755]
[ INFO] [1718495981.276106769]: I heard: [hello world 4756]
[ INFO] [1718495981.376120779]: I heard: [hello world 4757]
[ INFO] [1718495981.476125572]: I heard: [hello world 4758]
[ INFO] [1718495981.576107389]: I heard: [hello world 4759]
[ INFO] [1718495981.676035765]: I heard: [hello world 4760]
[ INFO] [1718495981.776045423]: I heard: [hello world 4761]
[ INFO] [1718495981.876131498]: I heard: [hello world 4762]
[ INFO] [1718495981.976107139]: I heard: [hello world 4763]
- 检查node/topic
$ rosnode list
/listener
/rosout
/talker
$ rostopic info /chatter
Type: std_msgs/String
Publishers:
* /talker (http://nvidia:35617/)
Subscribers:
* /listener (http://nvidia:41419/)
- 手动测试
消息发送端命令:
$ rostopic pub /chatter std_msgs/String "data: 'Test Custom ROS message'"
publishing and latching message. Press ctrl-C to terminate
消息接收端命令:
$ rosrun roscpp_tutorials listener
[ INFO] [1718496338.863755270]: I heard: [Test Custom ROS message]
3.5 工程构建
3.5.1 工程环境
$ echo $ROS_PACKAGE_PATH
/opt/ros/noetic/share
$ cat /opt/ros/noetic/setup.sh
#!/usr/bin/env sh
# generated from catkin/cmake/template/setup.sh.in
# Sets various environment variables and sources additional environment hooks.
# It tries it's best to undo changes from a previously sourced setup file before.
# Supported command line options:
# --extend: skips the undoing of changes from a previously sourced setup file
# --local: only considers this workspace but not the chained ones
# In plain sh shell which doesn't support arguments for sourced scripts you can
# set the environment variable `CATKIN_SETUP_UTIL_ARGS=--extend/--local` instead.
# since this file is sourced either use the provided _CATKIN_SETUP_DIR
# or fall back to the destination set at configure time
: ${_CATKIN_SETUP_DIR:=/opt/ros/noetic}
_SETUP_UTIL="$_CATKIN_SETUP_DIR/_setup_util.py"
unset _CATKIN_SETUP_DIR
if [ ! -f "$_SETUP_UTIL" ]; then
echo "Missing Python script: $_SETUP_UTIL"
return 22
fi
# detect if running on Darwin platform
_UNAME=`uname -s`
_IS_DARWIN=0
if [ "$_UNAME" = "Darwin" ]; then
_IS_DARWIN=1
fi
unset _UNAME
# make sure to export all environment variables
export CMAKE_PREFIX_PATH
if [ $_IS_DARWIN -eq 0 ]; then
export LD_LIBRARY_PATH
else
export DYLD_LIBRARY_PATH
fi
unset _IS_DARWIN
export PATH
export PKG_CONFIG_PATH
export PYTHONPATH
# remember type of shell if not already set
if [ -z "$CATKIN_SHELL" ]; then
CATKIN_SHELL=sh
fi
# invoke Python script to generate necessary exports of environment variables
# use TMPDIR if it exists, otherwise fall back to /tmp
if [ -d "${TMPDIR:-}" ]; then
_TMPDIR="${TMPDIR}"
else
_TMPDIR=/tmp
fi
_SETUP_TMP=`mktemp "${_TMPDIR}/setup.sh.XXXXXXXXXX"`
unset _TMPDIR
if [ $? -ne 0 -o ! -f "$_SETUP_TMP" ]; then
echo "Could not create temporary file: $_SETUP_TMP"
return 1
fi
CATKIN_SHELL=$CATKIN_SHELL "$_SETUP_UTIL" $@ ${CATKIN_SETUP_UTIL_ARGS:-} >> "$_SETUP_TMP"
_RC=$?
if [ $_RC -ne 0 ]; then
if [ $_RC -eq 2 ]; then
echo "Could not write the output of '$_SETUP_UTIL' to temporary file '$_SETUP_TMP': may be the disk if full?"
else
echo "Failed to run '\"$_SETUP_UTIL\" $@': return code $_RC"
fi
unset _RC
unset _SETUP_UTIL
rm -f "$_SETUP_TMP"
unset _SETUP_TMP
return 1
fi
unset _RC
unset _SETUP_UTIL
. "$_SETUP_TMP"
rm -f "$_SETUP_TMP"
unset _SETUP_TMP
# source all environment hooks
_i=0
while [ $_i -lt $_CATKIN_ENVIRONMENT_HOOKS_COUNT ]; do
eval _envfile=\$_CATKIN_ENVIRONMENT_HOOKS_$_i
unset _CATKIN_ENVIRONMENT_HOOKS_$_i
eval _envfile_workspace=\$_CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
unset _CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
# set workspace for environment hook
CATKIN_ENV_HOOK_WORKSPACE=$_envfile_workspace
. "$_envfile"
unset CATKIN_ENV_HOOK_WORKSPACE
_i=$((_i + 1))
done
unset _i
unset _CATKIN_ENVIRONMENT_HOOKS_COUNT
3.5.2 工程编译
以:leggedrobotics/ros_best_practices为例:
$ cd ~
$ mkdir -p catkin_ws/src
$ cd catkin_ws/src
$ git clone git@github.com:leggedrobotics/ros_best_practices.git
Cloning into 'ros_best_practices'...
remote: Enumerating objects: 403, done.
remote: Counting objects: 100% (107/107), done.
remote: Compressing objects: 100% (55/55), done.
remote: Total 403 (delta 41), reused 86 (delta 32), pack-reused 296
Receiving objects: 100% (403/403), 180.10 KiB | 330.00 KiB/s, done.
Resolving deltas: 100% (201/201), done.
$ cd ..
$ catkin build ros_package_template
-----------------------------------------------------------
Profile: default
Extending: [env] /opt/ros/noetic
Workspace: /home/daniel/catkin_ws
-----------------------------------------------------------
Build Space: [exists] /home/daniel/catkin_ws/build
Devel Space: [exists] /home/daniel/catkin_ws/devel
Install Space: [unused] /home/daniel/catkin_ws/install
Log Space: [missing] /home/daniel/catkin_ws/logs
Source Space: [exists] /home/daniel/catkin_ws/src
DESTDIR: [unused] None
-----------------------------------------------------------
Devel Space Layout: linked
Install Space Layout: None
-----------------------------------------------------------
Additional CMake Args: None
Additional Make Args: None
Additional catkin Make Args: None
Internal Make Job Server: True
Cache Job Environments: False
-----------------------------------------------------------
Buildlisted Packages: None
Skiplisted Packages: None
-----------------------------------------------------------
Workspace configuration appears valid.
NOTE: Forcing CMake to run for each package.
-----------------------------------------------------------
[build] Found 1 packages in 0.0 seconds.
[build] Updating package table.
Starting >>> catkin_tools_prebuild
Finished <<< catkin_tools_prebuild [ 3.0 seconds ]
Starting >>> ros_package_template
Finished <<< ros_package_template [ 15.2 seconds ]
[build] Summary: All 2 packages succeeded!
[build] Ignored: None.
[build] Warnings: None.
[build] Abandoned: None.
[build] Failed: None.
[build] Runtime: 18.3 seconds total.
[build] Note: Workspace packages have changed, please re-source setup files to use them.
3.5.3 工程运行
$ roslaunch ros_package_template ros_package_template.launch
... logging to /home/daniel/.ros/log/fe950290-2b71-11ef-b45c-200b7460c5b7/roslaunch-nvidia-10017.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.
started roslaunch server http://nvidia:36891/
SUMMARY
========
PARAMETERS
* /ros_package_template/subscriber_topic: /temperature
* /rosdistro: noetic
* /rosversion: 1.16.0
NODES
/
ros_package_template (ros_package_template/ros_package_template)
ROS_MASTER_URI=http://localhost:11311
process[ros_package_template-1]: started with pid [10041]
[ INFO] [1718498148.996771476]: Successfully launched node.
注:这个roslaunch非常像之前做的跨板上层软件x86模拟平台搭建的一键启动。
3.6 Gazebo模拟
4. 课程视频
个人感觉英文视频总体上还是和国语的视频不太一样,大家可以品一下!!!
尤其在上面第三章节【关键要点】,我们在做了笔记的情况下,大家更应该能够克服语言上的障碍。
不要觉得英文视频就放弃了,大量的编程代码、技术文献资料、芯片规格、解决方案都是英文,因此这个视频压根就不是问题,对吧:)
Programming for Robotics, Lecture 1 Introduction to ROS