写在前面:

本文章是根据 ROS 官方文档以及其他相关资料翻译、整理而来。碍于本人学识有限,且非本专业人士,部分叙述难免存在纰漏,请读者注意甄别。

使用版本:

  • Linux 20.04 LTS
  • ROS Noetic (ROS1)

参考资料:

0. 安装

ROS Noetic / linux 20.04

以下提供一个基于 Ubuntu20.04.6 系统版本的 ROS Noetic 安装脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
CURRENT_SOURCE=$(grep -E "http(s?)://(.*\.)?mirrors.tuna.tsinghua.edu.cn/ubuntu/" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null)
BASHRC_FILE="$HOME/.bashrc"
USER_NAME=$(whoami)

name_ros_distro=noetic

sudo apt install -y lsb-release curl

echo "#######################################################################################################################"
echo ""
echo ">>> { 开始安装 ROS $name_ros_distro }"
echo ">>> { 检查当前 Ubuntu 环境 } "

#Getting version and release number of Ubuntu
UBUNTU_VERSION_NAME=`lsb_release -sc`
KERNEL_VERSION=$(uname -r)
RELEASE_NUM=`grep DISTRIB_DESCRIPTION /etc/*-release | awk -F 'Ubuntu ' '{print $2}' | awk -F ' LTS' '{print $1}'`

echo ">>> { 当前 Ubuntu 版本为: [Ubuntu $UBUNTU_VERSION_NAME $RELEASE_NUM] in kernel $KERNEL_VERSION}"
#Checking version is focal, if yes proceed othervice quit
case $UBUNTU_VERSION_NAME in
"focal" )
;;
*)
echo ">>> { ERROR: 当前脚本只支持 Ubuntu Focal (20.04).}"
exit 0
esac

echo ""
echo "#######################################################################################################################"
echo ""
echo ">>> { Step 1: 设置当前 Ubuntu 的 Repo 环境 }"
#Configure your Ubuntu repositories to allow "restricted," "universe," and "multiverse." You can follow the Ubuntu guide for instructions on doing this.
#https://help.ubuntu.com/community/Repositories/Ubuntu

if [ -z "$CURRENT_SOURCE" ]; then
echo ">>> { 当前 apt 源不是国内镜像, 是否将其更改为[清华大学镜像源]? } (是y / 否n)"
read should_change_apt_image_src
echo ">>> { 您的输入为: [$should_change_apt_image_src] }"
if [[ "$should_change_apt_image_src" == "y" || "$should_change_apt_image_src" == "Y" ]]; then
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo tee /etc/apt/sources.list <<EOF
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs) main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs) main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-backports main restricted universe multiverse

# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换
deb http://security.ubuntu.com/ubuntu/ $(lsb_release -cs)-security main restricted universe multiverse
# deb-src http://security.ubuntu.com/ubuntu/ $(lsb_release -cs)-security main restricted universe multiverse

# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-proposed main restricted universe multiverse
# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ $(lsb_release -cs)-proposed main restricted universe multiverse
EOF
fi
fi

echo ">>> { 更新 apt 缓存... }"
sudo apt update
echo ">>> { Done: 已设置当前 Ubuntu 的 Repo 环境}"
echo ""

echo "#######################################################################################################################"
echo ""
echo ">>> { Step 2: 设置与 ROS 相关的 Repo : /etc/apt/sources.list.d/ros-latest.list }"
#This will add the ROS Noetic package list to sources.list
sudo sh -c "echo \"deb http://packages.ros.org/ros/ubuntu ${UBUNTU_VERSION_NAME} main\" > /etc/apt/sources.list.d/ros-latest.list"

#Checking file added or not
if [ ! -e /etc/apt/sources.list.d/ros-latest.list ]; then
echo ">>> { Error: 无法创建 sources.list, 退出 }"
exit 0
fi
echo ">>> { Done: 设置与 ROS 相关的 Repo : /etc/apt/sources.list.d/ros-latest.list }"
echo ""

echo "#######################################################################################################################"
echo ""
echo ">>> { Step 3: 设置与 ROS 相关的 Keys }"
echo ">>> { 安装 curl 工具 }"
sudo apt install -y curl
echo ">>> { 正在设置与 ROS 相关的 Keys, 请稍等片刻...... }"
config_key_return=$(curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -)
#Checking return value is OK
case $config_key_return in
"OK" )
;;
*)
echo ">>> { ERROR: 无法添加 ROS keys, 退出 }"
exit 0
esac
echo ">>> { Done: 设置与 ROS 相关的 Keys }"
echo ""

echo "#######################################################################################################################"
echo ">>> { Step 4: 更新 Ubuntu package index, 请稍等片刻...... }"
sudo apt update
echo ""

echo "#######################################################################################################################"
echo ">>> { Step 5: 安装 ROS, 请选择您需要安装的版本: }"
echo " [1. Desktop-Full Install: (推荐) : Desktop 中的所有内容以及 2D/3D 模拟器 ]"
echo ""
echo " [2. Desktop Install: ROS-Base 中的所有内容以及 rqt 和 rviz 等工具 ]"
echo ""
echo " [3. ROS-Base: (基本功能) ROS 工具包、框架和通信库。无 GUI 工具 ]"
echo ""
#Assigning default value as 1: Desktop full install
read -p ">>> 请输入您需要安装的版本 (默认为 1):" install_choice

case "$install_choice " in
1)
package_type="desktop-full"
;;
2)
package_type="desktop"
;;
3)
package_type="ros-base"
;;
* )
package_type="desktop-full"
;;
esac
echo ">>> { 开始安装 ROS, 请稍等片刻......}"
sudo apt-get install -y ros-${name_ros_distro}-${package_type}
echo ""

echo "#######################################################################################################################"
echo ">>> { Step 6: 设置 ROS 环境,这将把 ROS 环境添加到 .bashrc }"
echo ">>> { 添加后,你就可以在终端中访问 ROS 命令 }"
echo "source /opt/ros/noetic/setup.bash" >> /home/$user_name/.bashrc
source /home/$user_name/.bashrc
sudo apt install -y python3-pip python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
sudo rosdep init
rosdep update
echo ""

ROS2 Humble / linux 22.04

  1. 首先确保我们所使用的系统环境支持utf-8编码。使用如下命令查看:

    1
    locale	# check for utf-8
  2. 添加 ROS2 apt 的依赖:

    • 首先确保 Ubuntu Universe repository 是可用的:

      1
      2
      sudo apt install software-properties-common
      sudo add-apt-repository universe
    • 使用 apt 添加 ROS2GPG公钥

      1
      2
      sudo apt update && sudo apt install curl -y
      sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    • 然后添加 ROS2 的 repo 到我们的 sources.list 中:

      1
      echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
  3. 安装 ROS 开发工具

    • 安装公共基础软件包:

      1
      2
      3
      4
      5
      6
      sudo apt update && sudo apt install -y \
      python3-flake8-docstrings \
      python3-pip \
      python3-pytest-cov \
      python3-vcstool \
      ros-dev-tools
    • 安装基于 linux-22.04 版本的软件包:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      sudo apt install -y \
      python3-flake8-blind-except \
      python3-flake8-builtins \
      python3-flake8-class-newline \
      python3-flake8-comprehensions \
      python3-flake8-deprecated \
      python3-flake8-import-order \
      python3-flake8-quotes \
      python3-pytest-repeat \
      python3-pytest-rerunfailures
  4. 当我们要使用 ROS2 相关的命令时,我们每次都需要先输入以下命令:

    1
    2
    3
    # 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

    当然,我们可以将其写入 .bashrc 文件(或其他类似配置)中:

    1
    echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc		# 或其他类似的bash配置

    然后检查环境变量是否设置成功:

    1
    printenv | grep -i ROS

    检查ROS_DISTROROS_VERSION是否设置成功:

    1
    2
    3
    ROS_VERSION=2
    ROS_PYTHON_VERSION=3
    ROS_DISTRO=humble
  5. 克隆一个演示项目到 workspacesrc 中:

    1
    2
    3
    mkdir -p ~/ws_ros2_humble/src
    cd ~/ws_ros2_humble
    vcs import --input https://raw.githubusercontent.com/ros2/ros2/humble/ros2.repos src
  6. 使用 rosdep 安装这个演示项目需要使用的依赖:

    (推荐在每次安装新依赖之前更新一遍系统的依赖环境)

    1
    sudo apt upgrade
    1
    2
    3
    sudo rosdep init
    rosdep update
    rosdep install --from-paths src --ignore-src -y --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers"
  7. 创建(Build)工作空间下的所有代码:

    注意 ⚠️

    如果安装过其他 ROS 版本,请先确保在 .bashrc(或其他类似配置)中不存在 source /opt/ros/${ROS_DISTRO}/setup.bash,以确保避免其他 ROS 版本的干扰。

    同时,可以使用命令printenv | grep-i ROS,确保其输出为空。

    • Build the code
    1
    2
    cd ~/ros2_humble/
    colcon build --symlink-install
  8. 代码创建完成后,我们首先需要配置局部的运行环境:

    1
    2
    3
    # Replace ".bash" with your shell if you're not using bash
    # Possible values are: setup.bash, setup.sh, setup.zsh
    . ~/ros2_humble/install/local_setup.bash
  9. 然后尝试运行一些实例:

    • 终端1
    1
    2
    3
    # python ver.
    . ~/ros2_humble/install/local_setup.bash
    ros2 run demo_nodes_py listener
    • 终端2
    1
    2
    3
    # python ver.
    . ~/ros2_humble/install/local_setup.bash
    ros2 run demo_nodes_py talker

    观察程序运行的过程。

使用 RoboStack 安装 ROS

RoboStack 是一个 ROS 的软件包管理器,只作***为条件不具备情况下的备选方案***。推荐将 ***ROS 的完整版安装在物理宿主机***中:

【开发环境】

1. 简介

RoboStack 是一个将机器人操作系统(ROS)打包为适用于 Linux、Mac 和 Windows 的 Conda 软件包管理器的项目,由 Open Robotics 提供支持,提供 ROS1 版本的 Noetic 和 ROS2 版本的 Humble(以上均为 LTS 版本),同时还为 Jupyter Notebook 提供各种与 ROS 相关的插件。

这里是 RoboStack 使用的几个重要理由:

  1. 任意平台上的 ROS: 它支持在 Linux(任何发行版,新旧版本)、MacOS 和 Windows 上安装ROS。这种灵活性使开发者能够在不同操作系统上工作,避免兼容性问题。
  2. 便捷的安装: 用户可以在 Conda 环境中将ROS与流行的机器学习和计算机视觉库(如PyTorch和TensorFlow)一起安装。这简化了需要与这些库集成的机器人应用程序的设置过程。
  3. 无需 root 访问权限: 与传统的ROS安装方式不同,RoboStack 不需要 root 访问权限。这一特性使得在共享工作站和高性能计算机上安装ROS变得更加容易,无需管理员权限。
  4. 可重现性: RoboStack 支持可重现的环境,确保机器人研究和开发中的一致性。这对于复制实验和在研究社区中共享代码至关重要。
  5. 基于浏览器的 RViz 可视化: RoboStack 可以在浏览器中提供类似RViz的可视化效果,即使没有完整的ROS安装也可以实现。这增强了远程可视化和调试机器人应用程序的能力。
  6. 与 Jupyter Notebooks 集成: 它与Jupyter笔记本和JupyterLab无缝集成,允许开发者在笔记本环境中控制机器人并进行ROS功能的实验。
  7. 简化的软件包创建: 创建ROS组件的Conda软件包比传统的Debian软件包更简单,使开发者能够更轻松地分发和管理他们的ROS模块。
  8. 跨平台的持续集成: RoboStack支持ROS软件包的跨平台持续集成(CI),确保代码变更能够在不同操作系统上无缝测试。

2. 如何安装

RoboStack 是一个使用 Conda 软件包管理器为 Linux、Mac 和 Windows 打包的 ROS,基于 conda-forge。

2.1 安装 conda

要开始使用 Conda(或 mamba)作为软件包管理器,您需要先安装一个基本的 Conda 环境。建议不要使用 Anaconda(可能存在商用授权等风险),可以使用由开源软件组织 conda-forge 开发维护的开源软件 Miniforge。具体安装步骤请参考 GitHub 中的官方指南

以 Linux 系统(类Unix)为例:可以使用 curlwget 或其他程序下载安装程序,然后运行脚本

1
2
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh

1
2
wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh

脚本执行结束后可以使用如下命令检查是否成功安装:

1
2
conda -V
# conda 24.5.0

2.2 安装 mamba(可选)

1
conda install conda-forge::mamba

2.3 安装 ROS 框架

注意:

  1. 确保不要在 base 环境中安装 ROS 软件包。因为这会导致后续问题。另一方面,conda 和 mamba 不能安装在 ros_env,它们只能安装在基本环境中。
  2. 不要 source 本地系统中的 ROS 框架。当系统中安装了 ros 时,在非 conda 环境中会出现环境干扰。因为安装脚本中设置的 PYTHONPATH 会与 conda 环境冲突。
  3. Windows 系统只支持 CMD 终端,不支持 Powershell。

在 conda 中创建 ros 环境:

1
2
3
4
5
6
7
8
9
10
11
12
conda create -n ros1_env python=3.11

# 激活新创建的 ros 环境
conda activate ros1_env

# 退出当前环境
conda deactivate

# 在创建的环境中添加源
conda config --env --add channels conda-forge
conda config --env --add channels robostack
conda config --env --remove channels defaults

安装 ros 框架:

1
2
3
4
conda activate ros1_env

# 安装 ros-noetic 完整包
conda install robostack::ros-noetic-desktop-full

安装 ros 相关本地开发工具包

1
2
3
4
5
6
7
conda install compilers cmake pkg-config make ninja colcon-common-extensions catkin_tools rosdep
conda install conda-forge::libdrm
conda install conda-forge::mesalib
conda install conda-forge::libglu
conda install robostack::ros-noetic-realsense2-camera
conda install robostack::ros-noetic-rosauth
conda install robostack::ros-noetic-rosbridge-suite # 可能会出现依赖问题

如果安装 ros-noetic-rosbridge-suite 包时出现依赖问题,可以尝试解决该依赖冲突,或者使用源码编译的方式解决该问题。方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 首先安装 rosbridge-suite 包运行时所需的依赖
conda install autobahn
conda install twisted
conda install tornado
conda install pymongo # 需要该包中的 bson

cd <PROJECT_ROOT_DIR>/src

# 在 GitHub 中 clone rosbridge_suite 项目
git clone https://github.com/RobotWebTools/rosbridge_suite.git
cd rosbridge_suite
git checkout ros1

# 编译源代码
cd <PROJECT_ROOT_DIR>
catkin_make -DCATKIN_WHITELIST_PACKAGES="rosbridge_suite"
catkin_make -DCATKIN_WHITELIST_PACKAGES="rosbridge_library"
catkin_make -DCATKIN_WHITELIST_PACKAGES="rosbridge_msgs"
catkin_make -DCATKIN_WHITELIST_PACKAGES="rosbridge_server"
catkin_make -DCATKIN_WHITELIST_PACKAGES="rosapi"

# 测试 rosbridge-suite 相关功能
source devel/setup.zsh
roslaunch rosbridge_server rosbridge_websocket.launch
# 观察输出是否有报错
# 2024-07-19 09:34:34+0800 [-] [INFO] [1721352874.223170]: Rosbridge WebSocket server started at ws://0.0.0.0:9090

3. 编译项目

后续编译、运行方式与正常安装操作方式无异,具体请参考相关文档。

1. ROS 入门知识

1.1 ROS 简介

ROS(Robotic Operation System,机器人操作系统)。简单点说,ROS 就是一个分布式的通信框架,帮助程序进程之间更方便的通信。通常在一个机器人中包含多个部件,每个部件都有配套的控制程序;或者在机器人集群中协调多个机器人,这正是 ROS 设计的初衷。他并不是一个真正意义上的操作系统,其底层的任务调度、编译、设备驱动等还是由他它的原生操作系统 Linux 完成。

ROS 的核心思想就是将机器人的软件功能做成一个个节点,节点之间通过互相发送消息进行沟通。这些节点可以部署在同一台主机上,也可以部署在不同的主机、甚至互联网上。在 ROS1 中,网络通信机制中的主节点(master)负责对网络中各个节点之间的通信过程进行调度管理,同时提供一个用于配制网络中全局参数的服务。

同时,ROS 时松耦合软件架构,提供了丰富开源的功能库。

1.2 ROS 开发环境的搭建

1.2.1 ROS 的安装

使用 ROS 进行开发时,一般需要机器人工作台两个部分。机器人通常采用低功耗的 ARM 平台嵌入式主板作为主机;工作台大多选择 X86 平台作为主机。

image-20231109101914944

官员 ROS 的安装,详见第〇章

1.2.2 ROS 文件的组织方式

ROS 的文件被放在系统空间工作空间两个地方。

系统空间

系统空间是存放 ROS 系统安装包的目录,在 /opt/ros/ 目录中存放着 roscorervizrqt 等 ROS 核心工具。要使用他们,需要先使用如下命令激活系统空间:

1
2
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

工作空间

工作空间是用户开发自己程序的文件夹,里面用来存放用户开发的功能包程序,文件是源代码形式。ROS 的开源社区有非常多的功能包,用户可以直接使用 apt 命令安装二进制功能包到系统空间,也可以将源代码下载到工作空间中,然后手动编译。工作空间的功能包可以直接覆盖系统空间下的同名功能包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 新建工作空间
mkdir ~/catkin_ws/src

# 初始化 src 目录
cd ~/catkin_ws/src
catkin_init_workspace

# 对工作空间进行编译
cd ~/catkin_ws
catkin_make

# 激活工作空间
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

1.2.3 ROS 网络通行配置

在构成 ROS 网络通信的各台主机中,必须指明一台主机作为主节点负责管理整个 ROS 网络通信,同时要声明参与通信的各个主机(配置每台主机中的环境变量 MASTERHOST)。

打开 ~/.bashrc 文件,添加如下环境变量

1
2
export ROS_MASTER_URI=http://localhost:[PORT]
export ROS_HOSTNAME=localhost

1.3 ROS 系统架构

按照官方的说法,可以分别从计算图结构、文件系统和开源社区视角来理解 ROS 架构。

1.3.1 从计算图视角理解 ROS 结构

ROS 中可执行程序的基本单位叫节点(node),节点之间通过消息机制进行通讯,这样就组成了一张网状图,也叫计算图。如下图

image-20231109111439507

附录1. 一些常用的命令

1. 关于 ROS 文件系统的导航

  • 如何在 ROS 中找到某个 package 在本机文件系统中的位置:

    1
    rospack find [PACKAGE_NAME]

    rospack 命令可以允许我们得到和 package 相关的信息。

  • 如何查看 ROS 环境中生效的所有 package 在本机文件系统中的位置:

    1
    echo $ROS_PACKAGE_PATH
  • 如何直接切换到某个 package 在本机文件系统中的位置:ROS + cd

    1
    roscd <PACKAGE_NAME> [/SUBDIR]
  • 如何直接查看某个 package 下的内容:ROS + ls

    1
    rosls <PACKAGE_NAME> [/SUBDIR]

2. 关于 package 的创建

  • ROS Neotic 中 Workspace 的文件组成结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    workspace_floder/					-- WORKSPACE
    src/ -- SOURCE SPACE
    CMakeLists.txt -- 'Toplevel' Cmake file, provided by catkin
    package_1/
    CMakeLists.txt -- 'CMakeLists.txt' file for package_1
    package.xml -- Package manifest for package_1
    ...
    ...
    package_n/
    CMakeLists.txt -- 'CMakeLists.txt' file for package_n
    package.xml -- Package manifest for package_n
    ...
  • 如何创建一个 catkin package

    1
    cd [YOUR_CATKIN_WORKSPACE]/src
    1
    2
    catkin_create_pkg <PACKAGE_NAME> [DEPENDS...]
    # 这里的 [DEPENDS...] 可以有多个参数,这些 depends 都是该 package 的直接(first-order)依赖
  • 如何 build 整个工作空间并 source 这个工作空间的 setup.bash

    1
    2
    cd [YOUR_CATKIN_WORKSPACE]
    catkin_make

    此时,一些文件夹会在 [YOUR_CATKIN_WORKSPACE] 中创建,如 devel/

    1
    2
    # 这里取决于你用的bash的类型,普通的bash:setup.bash, zsh:setup.zsh
    source [YOUR_CATKIN_WORKSPACE]/devel/setup.bash
  • 如何查询一个 package 的依赖 (dependency):

    1
    2
    # 查询一个 package 的直接(first-order)依赖
    rospack depends1 [PACKAGE_NAME]
    1
    2
    # 查询一个 package 的所有依赖
    rospack depends [PACKAGE_NAME]

3. 关于 packagebuild(构建)

  • 如何使用 catkin_make 命令行工具:

    catkin_make 命令行工具将标准 CMake 工作流程中对 cmakemake 的调用结合在了一起。

    1
    catkin_make [MAKE_TARGETS] [-DCMAKE_VARIABLES=...]

    上述命令将构建在 src/ 文件夹中找到的任何 catkin 项目。如果您的源代码放在不同的地方,比如 my_src/,那么您可以这样调用 catkin_make

    1
    catkin_make --source my_src
  • 如何 build 整个工作空间并 source 这个工作空间的 setup.bash

    1
    2
    cd [YOUR_CATKIN_WORKSPACE]
    catkin_make

    此时,一些文件夹会在 [YOUR_CATKIN_WORKSPACE] 中创建,如 devel/

    1
    2
    # 这里取决于你用的bash的类型,普通的bash:setup.bash, zsh:setup.zsh
    source [YOUR_CATKIN_WORKSPACE]/devel/setup.bash

4. 关于 rosnode 的一些操作

  • 当我们使用 ROS 时,roscore 是第一个应该被执行的命令:

    1
    roscore
  • rosnode 显示当前正在运行的 ROS 节点信息:

    1
    2
    # 该命令会列出所有处于活动状态的 node
    rosnode list
    1
    2
    # 该命令会返回指定 node 的信息
    rosnode info <SPECIFIC_NODE>
    1
    2
    # 该命令会测试指定 node 是否可访问
    rosnode ping <SPECIFIC_NODE>

注意⚠️

每当打开一个新终端时,你的环境会被重置,你的 ~/.bashrc 文件也会被 source

如果在运行 rosnode 等命令时遇到困难,则可能需要在 ~/.bashrc 中添加一些环境设置文件,或手动重新获取它们。

  • rosrun 直接运行包中的节点:

    1
    rosrun <PACKAGE_NAME> <NODE_NAME>

5. 关于 rostopic 的操作

使用 rostopic 工具可以获取有关 ROS topic 的信息。

你可以使用帮助选项(-h)获取 rostopic 的可用子命令:

1
rostopic -h
1
2
3
4
5
6
rostopic bw 		# 显示 topic 使用的带宽
rostopic echo # 打印 topic 信息到屏幕
rostopic hz # 显示 topic 的发布率
rostopic list # 打印 topic 主题的信息
rostopic pub # 向 topic 发布数据
rostopic type # 打印 topic 类型
  • rostopic list

    rostopic list 返回当前订阅和发布的所有主题的列表。

    1
    rostopic list -h
    1
    2
    3
    4
    5
    6
    7
    8
    使用方法: rostopic list [/topic] (主题列表)

    选项
    -h、--help 显示帮助信息并退出
    -b BAGFILE, --bag=BAGFILE 列出 .bag 文件中的主题
    -v, --verbose 列出每个主题的全部详细信息
    -p 只列出发布者
    -s 只列出订阅者
  • rostopic type

    rostopic type 返回正在发布的任何主题的信息类型。

    1
    rostopic type [TOPIC]

    我们可以使用 Linux 系统中的管道符 | 来直接查看指定 topic 的内容格式定义,以消息 msg 为例:

    1
    rostopic type [TOPIC] | rosmsg show
  • rostopic pub

    rostopic pub 向当前指定 topic 的数据。

    1
    rostopic pub [TOPIC] [MSG_TYPE] [ARGS]

    turtlrsim 为例,

    1
    rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'

    其中:

    • rostopic pub 命令将向指定主题发布消息
    • 选项(-1)会导致 rostopic 只发布一条消息,然后退出
    • /turtle1/cmd_vel 是要发布到的主题名称
    • geometry_msgs/Twist 是发布到主题时要使用的信息类型
    • 选项(--)告诉选项解析器以下参数都不是选项。如果参数前有破折号"-",例如负数,则需要使用此选项
    • geometry_msgs/Twist 类型的 msg 有两个向量,每个向量有三个浮点元素:线性和角度。在本例中,"[2.0, 0.0, 0.0]" 是线性值,其中 x=2.0、y=0.0、z=0.0"[0.0, 0.0, 1.8]" 是角度值,其中 x=0.0、y=0.0、z=1.8。这些参数实际上是 YAML 语法,在 YAML 命令行文档中有更多描述。

    我们可以使用 rostopic pub -r 命令发布重复连续的命令。同样以 turtlrsim 为例,我们希望发送命令的频率为 1Hz,则有如下命令

    1
    rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'

6. rosservicerosparam 的相关操作

  • rosservice

    1
    2
    3
    4
    5
    rosservice list 		# 打印有关活动服务的信息
    rosservice call # 使用提供的参数调用服务
    rosservice tpye # 打印服务类型
    rosservice find # 按服务类型查找服务
    rosservice uri # 打印服务 ROSRPC uri
  • rosparam

    1
    2
    3
    4
    5
    6
    rosparam set 		# 设置参数
    rosparam get # 获取参数
    rosparam load # 从文件加载参数
    rosparam dump # 将参数转储到文件
    rosparam delete # 删除参数
    rosparam list # 列出参数名称

7. 使用 rqt 相关工具

  • 使用 apt 工具安装 rqt

    1
    sudo apt install ros-$ROS_DISTRO-rqt ros-$ROS_DISTRO-rqt-commom-plugins
  • 使用 rqt_graph 工具:

    rqt_graph 可以用于可视化 ROS 节点(nodes)、话题(topics)、服务(services)和参数(parameters)之间的关系,以帮助用户更好地理解和调试 ROS 系统。

    1
    rosrun rqt_graph rqt_graph
  • 使用 rqt_plot 工具:

    rqt_plot 工具用于实时绘制和可视化 ROS 话题(topics)中的数据。它通常用于监视传感器数据、控制输出或其他实时生成的数值数据,以便用户可以更轻松地理解和分析这些数据。

    1
    rosrun rqt_plot rqt_plot
  • 使用 rqt_consolerqt_logger_level 工具:

    rqt_console 工具用于查看和管理 ROS 系统中的日志消息。它主要用于调试和监视 ROS 节点的日志消息,帮助开发人员在开发和运行 ROS 应用程序时快速发现和解决问题。

    rqt_logger_level 工具用于查看和管理ROS节点的日志级别。

    1
    2
    rosrun rqt_console rqt_console
    rosrun rqt_logger_level rqt_logger_level

8. 使用 .launch 文件启动节点

关于 .launch 文件的讲解

1
roslaunch [PACKAGE] [FILENAME.launch]

附录2. 远程控制ROS机器人的两种方式

注意 ⚠️:

远程控制 ROS 机器人的方式可能并非两种,这里只是笔者接触到的两种最常用的控制形式。在此记录,防止日后忘记。同时也将记录使用这两种方法遇到的问题,以及当时的解决方法。

参考文章:

1. ROS 主从节点控制(ROS Noetic)

在 ROS 第一个版本中(这里指 ROS Noetic),可以设置一台主机(MASTER)和多台从机(SLAVE)。

注意 ⚠️:

这种“主从机制”只针对与 ROS 第一个版本,ROS2 中似乎不再有“主从”的概念。详细请参考ROS2。

我们可以在主机中启动 MASTER 节点,从机启动其他节点,这样就可以在这些配置好的主从机之间相互通信,其中的网络通信过程对于我们而言是透明的,就像是在同一台主机上。

以下是配置过程:

  1. 使用该方法的前提是两台均已安装好 ROS 环境的机器(最好是同版本,不同版本未测试)。

  2. 设置两台主机的 IP 地址:

    在同一网段下设置两台机器的静态 IP,以笔者测试环境举例:

    • 网关地址:192.168.2.1
    • 子网掩码:255.255.255.0
    • 主机 IP(Jetson):192.168.2.200
    • 从机 IP(Ubuntu):192.168.2.201
  3. 修改 .bashrc.zshrc 等系统环境配置:

    • 主机(Jetson)中添加:

      1
      2
      3
      export ROS_HOSTNAME=$YOUR_HOSTNAME	# 设置本机的Host,一般为IP地址
      export ROS_IP=$YOUR_IP # 设置本机的IP地址
      export ROS_MASTER_URI=http://$YOUR_IP:11311 # 将本机设置为主机
    • 从机(Ubuntu)中添加:

      1
      2
      3
      export ROS_HOSTNAME=$YOUR_HOSTNAME	# 设置本机的Host,一般为IP地址
      export ROS_IP=$YOUR_IP # 设置本机的IP地址
      export ROS_MASTER_URI=http://$YOUR_MASTER_NODE_IP:11311 # 将本机设置为主机

    注意 ⚠️:

    这里的 ROS_MASTER_URI 特指主机的 URI,受同时主机控制的从机该属性均相同。

  4. 修改 /etc/hosts 文件:

    我们需要修改主从机的 /etc/hosts 文件,这样主从机之间才可以相互通信。如果主机的 /etc/hosts 文件中没有添加从机的信息,从机只能接收到主机发来的消息,而不能向主机发送消息。

    1
    sudo vim /etc/hosts
    • 主机中添加:

      1
      2
      3
      # ROS_SLAVE_MACHINE
      # MACHINE_IP MACHINE_HOST
      $YOUR_SLAVE_IP $YOUR_SLAVE_HOSTNAME
    • 从机中添加:

      1
      2
      3
      # ROS_MASTER_MACHINE
      # MACHINE_IP MACHINE_HOST
      $YOUR_MASTER_IP $YOUR_MASTER_HOSTNAME
  5. 测试:

    现在我们就可以测试主从机之间能否顺利通信。

    • 主机上运行:

      1
      2
      roscore				# 开启 ROS 节点
      rostopic echo /test_topic # 监听 /test_topic 话题
    • 从机上运行:

      1
      2
      # 向 /test_topic 话题发送1次内容为 'Hello from the other side!' 的 std_msgs/String 消息
      rostopic pub /test_topic std_msgs/String -1 -- 'Hello from the other side!'
    • 观察主机输出:

      1
      data: "Hello from the other side!"

2. 通过 ROSBridge_server 的方法实现 websocket 控制

参考文章:

2.1 介绍 rosbridge_suite

ROSBridge非 ROS 程序提供了 ROS 功能的 JSON API。有多种前端与 rosbridge 交互,包括用于 Web 浏览器交互的 WebSocket 服务器。rosbridge_suite 是一个元包,包含 rosbridge、rosbridge 的各种前端包(如 WebSocket 包)和帮助程序包。

rosbridge 有两个部分:协议和实现。

2.1.1 ROSBridge 协议

ROSBridge 协议是用于向 ROS 发送基于JSON 的命令的规范。订阅主题的协议示例:

1
2
3
4
{ "op": "subscribe",
"topic": "/cmd_vel",
"type": "geometry_msgs/Twist"
}

该规范与编程语言和传输层无关。其目的在于,任何可以发送 JSON的语言或传输都可以通过 ROSBridge 协议与 ROS 交互。该协议涵盖订阅(Subscribe)和发布(Publish)主题、服务(Service)调用、获取和设置参数(Parameter),甚至压缩消息等。

2.1.2 ROSBridge 的实现

rosbridge_suite 包是实现 ROSBridge 协议并提供 WebSocket 传输层的包的集合。

rosbridge_suite 包括:

  • rosbridge_library:核心 rosbridge 包。rosbridge_library 负责获取 JSON 字符串并将命令发送到 ROS,反之亦然。
  • rosapi:通过通常为ROS 客户端库保留的服务调用来访问某些 ROS 操作。这包括获取和设置参数、获取主题列表等等。
  • rosbridge_server:虽然 rosbridge_library 提供 JSON<->ROS 转换,但它将传输层留给了其他人。rosbridge_server 提供WebSocket连接,以便浏览器可以“与 rosbridge 对话”。roslibjs是一个用于浏览器的JavaScript库,可以通过 rosbridge_server 与 ROS 通信。

2.2 ROSBridge 的安装

  • ROSBridge 已提供 Debian 版本以供安装。
1
2
sudo apt install ros-$ROS_DISTRO-rosbridge-suite
sudo apt install ros-$ROS_DISTRO-rosbridge-server

2.3 ROSBridge 的使用

要启动 ROSBridge 及其软件包(如 rosbridge_serverrosapi),安装中包含 .launch 文件。要启动该文件,输入如下命令运行:

1
roslaunch rosbridge_server rosbridge_websocket.launch address:=$YOUR_IP port:=$PORT	# default port 9090
  1. 我们在自己的工作空间下,从创建 package 开始:

    1
    2
    cd $YOUR_WORKSPACE/src
    catkin_create_pkg test_rosbridge --catkin-deps rosbridge_server
  2. package 中创建一个 .launch 文件:

    1
    2
    mkdir $YOUR_WORKSPACE/test_rosbridge/launch
    vim $YOUR_WORKSPACE/test_rosbridge/launch/test_websocket.launch

    test_websocket.launch 文件中写入如下的内容:

    1
    2
    3
    4
    5
    6
    <launch>
    <include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch">
    <-- 默认端口为 9090 --/>
    <arg name="port" value="9090"/>
    </include>
    </launch>
  3. 然后我们测试 rosbridge_server

    1
    2
    3
    4
    cd $YOUR_WORKSPACE
    catkin_make
    source devel/setup.bash # 或 devel/setup.zsh
    roslaunch test_rosbridge test_websocket.launch

    最后我们可以观察 terminal 中的输出:

    1
    [INFO] [1561100304.196110]: Rosbridge WebSocket server started at ws://0.0.0.0:9090

2.4 JavaScript 通过 WebSocket 使用 ROSBridge

  • 【目标】通过网页(JavaScript)与主机的 ROS 环境通信
  • 【前提配置】在同一网段下设置主机的静态 IP,以笔者测试环境举例:
    • 网关地址:192.168.2.1
    • 子网掩码:255.255.255.0
    • 主机 IP(Jetson):192.168.2.200

注意 ⚠️

这里是一个没有 ROS 环境的机器ROS 主机之间的通信。

2.4.1 创建一个 html 文件

  1. 我们从最简单的本机控制开始,创建一个 html 文件:

    1
    2
    mkdir $YOUR_WORKSPACE/web
    vim $YOUR_WORKSPACE/web/test_websocket.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <!DOCTYPE html>
    <html>

    <head>
    <meta charset="utf-8" />

    <!-- 导入 robot web tool JS 包 -->
    <script type="text/javascript"
    src="https://cdn.jsdelivr.net/npm/eventemitter2@6.4.9/lib/eventemitter2.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/roslib@1/build/roslib.min.js"></script>

    <script type="text/javascript" type="text/javascript">
    var ros = new ROSLIB.Ros({
    url: 'ws://192.168.2.200:9090'
    });

    ros.on('connection', function () {
    document.getElementById("status").innerHTML = "Connected";
    console.log('Connected to websocket server.');
    });

    ros.on('error', function (error) {
    document.getElementById("status").innerHTML = "Error";
    console.log('Error connecting to websocket server: ', error);
    });

    ros.on('close', function () {
    document.getElementById("status").innerHTML = "Closed";
    console.log('Connection to websocket server closed.');
    });
    </script>
    </head>

    <body>
    <h1>Simple ROS User Interface</h1>
    <p>Connection status: <span id="status"></span></p>
    <p>Last /test_topic received: <span id="msg"></span></p>
    </body>

    </html>

    💡代码解释:

    • 首先,我们从 Robot Web Tools CDN 导入需要的 JS 包;

    • 我们创建一个 ROS 对象,用于稍后与其他节点通信。目前,我们在同一台机器上运行 JS 和 rosbridge,因此监听的是 localhost 和默认的 9090 端口:

      1
      2
      3
      var ros = new ROSLIB.Ros({
      url: 'ws://localhost:9090'
      });

      当我们需要访问从远端访问同网段下的 ROS 主机时,我们需要将这里的 url 参数改为 ROS 主机参数:

      1
      url: 'ws://192.168.2.200:9090'
    • 我们为 ROS 事件(本例中为连接事件)创建了监听器。当事件发生时,我们会找到一个 id="status" 的 HTML 元素(在本例中是我们在 <body> 中定义的 <span> 元素),然后将 HTML 的内部更改为我们想要显示的信息,并在控制台打印消息。

      1
      2
      3
      4
      ros.on('connection', function () {
      document.getElementById("status").innerHTML = "Connected";
      console.log('Connected to websocket server.');
      });
    • 此时,我们用浏览器打开这个 HTML 文件,我们可以看到页面上显示:
    1
    Connection status: Connected
    • 打开浏览器的控制台(快捷键 F12),我们可以看到:
    1
    test_websocket.html:19 Connected to websocket server.
    • ROS 主机的 log 打印:
    1
    2
    [INFO] [1562100304.196110]: Client connected. 1 clients total.
    [INFO] [1562101304.196110]: Client disconnected. 0 clients total.

2.4.2 订阅一个主题

  1. 下一步,我们将订阅一个主题,接受字符串信息并将其显示在浏览器窗口中。我们将把代码放在 HTML 脚本中,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script type="text/javascript" type="text/javascript">
    var test_topic = new ROSLIB.Topic({
    ros: ros,
    name: '/test_topic',
    messageType: 'std_msgs/String'
    });

    test_topic.subscribe(function (data) {
    document.getElementById("msg").innerHTML = data.data;
    });
    </script>

    这段代码声明了一个名为 test_topic_listenerROSLIB.Topic 对象,该对象将监听 /test_topic主题上的消息,并接受 std_msgs/String 类型的消息。然后,我们创建一个事件监听器(test_topic.subscribe()),它将调用一个匿名函数,将 "msg"<span> 元素的内部 HTML 更改为消息的数据字段。

    要测试这段代码,请按以下步骤操作:

    • ROS 主机的 log 打印:

      1
      2
      [INFO] [1762100304.196110]: Client connected. 1 clients total.
      [INFO] [1762142345.496110]: [Client 2] Subscribed to /test_topic

      思考 ❓:

      在这里我们看到,[Client 2] 表现出 ROSBridge_server 应该存在一个数据结构来维护连接的 Client,而且刷新后数字会自增。那么通过用户的不断刷新,当这个数据结构的资源枯竭时,ROSBridge_server 的策略是什么?会直接退出?还是报错?或者会自动清除失效的连接(Session?)?

    • 在 ROS 主机上打开一个终端,输入:

      1
      2
      # 向 /test_topic 话题发送1次内容为 'Hello from Master Node!' 的 std_msgs/String 消息
      rostopic pub /test_topic std_msgs/String -1 -- 'Hello from Master Node!'
    • 观察 HTML 页面,信息应出现在我们为此创建的 <span> 字段中:

      1
      2
      Connection status: Connected
      Last /test_topic received: Hello from Master Node!

2.4.3 发布一个话题

  1. 与订阅主题的做法类似,我们依然使用 test_topicROSLIB.Topic 对象,该对象将在/test_topic 主题上发布 std_msgs/String 消息。然后,我们创建一个按钮(button),通过点击按钮来触发名为 test_publish() 的函数,通过 test_topic 对象发布该消息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <script type="text/javascript" type="text/javascript">
    var test_topic = new ROSLIB.Topic({
    ros: ros,
    name: '/test_topic',
    messageType: 'std_msgs/String'
    });

    function test_publish(){
    var msg = new ROSLIB.Message({
    data: 'Hello from JS!'
    });
    test_topic.publish(msg)
    }
    </script>

    <body>
    <button class="button" onclick="test_publish()">Publish</button>
    </body>

附录3. 关于 Linux 系统外接设备固定端口号的问题

1. USB 及串口设备

参考文件:


在开发 ROS 机器人的过程中经常需要会遇到的多个设备的接驳,但是在 Linux 系统中对于外部设备端口号的分配和上电顺序相关,所以系统每次读取到的设备端口号都不同。如果是开机启动或者接在同一 HUB 下面,则由系统枚举各 USB 设备的顺序相关。以下介绍通过 udev 工具实现为 USB 转串口设备固定串口名称的实现原理与方法,该方法也适用于其他 USB 设备驱动。`

为了使启动脚本顺利的运行,我们需要将这些端口号固定下来。

udev 运行在用户态,脱离驱动层的关联,基于这种设计实现,用户可以通过编写规则来动态删除和修改 /dev下的设备文件,任意命名设备。除了设备重命名外,还拥有修改设备访问权限的功能,可以实现在普通用户模式下操作 /dev 下系统设备,无需root模式下进行。

每当 udevd 收到 uevent 事件时就会去匹配规则,匹配成功后执行规则对应的操作。用户自定义规则放在 /etc/udev/rules.d/ 目录下,以 .rules 为扩展名。这些规则文件的文件名通常是两个数字开头,它表示系统应用该规则的顺序

规则文件里的规则有一系列的键/值对组成,键/值对之间用逗号分割。每一个键或者是用户匹配键,或者是一个赋值键。匹配键确定规则是否被应用,而赋值键表示分配某值给该键。这些值将影响 udev 创建的设备文件。赋值键可以处理一个多值列表。

***【规则说明】***

  1. udev 规则的所有操作符
操作符 含义
== 比较键、值,若等于,则该条件满足
!= 比较键、值,若不等于,则该条件满足
= 对一个键赋值
+= 为一个表示多个条目的键赋值
:= 对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值
  1. udev 规则的匹配键
匹配键 含义
ACTION 事件 (uevent) 的行为,例如:add( 添加设备 )、remove( 删除设备 )
KERNEL 内核设备名称,例如:sdacdrom
DEVPATH 设备的 devpath 路径
SUBSYSTEM 设备的子系统名称,例如:sda 的子系统为 block
BUS 设备在 devpath 里的总线名称,例如:usb
DRIVER 设备在 devpath 里的设备驱动名称,例如:ide-cdrom
ID 设备在 devpath 里的识别号
SYSFS{filename} 设备的 devpath 路径下,设备的属性文件“filename”里的内容。
例如:SYSFS{model}=="ST936701SS"表示:如果设备的型号为 ST936701SS,则该设备匹配该匹配键
ENV{key} 环境变量。在一条规则中,可以设定最多五条环境变量的匹配键
PROGRAM 调用外部命令
RESULT 外部命令 PROGRAM 的返回结果
  1. udev 的重要赋值键
赋值键 含义
NAME /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。
SYMLINK /dev/ 下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接
OWNER, GROUP, MODE 为设备设定权限
ENV{key} 导入一个环境变量
  1. 我们以向系统内接入一个 USB-HUB 连接着两个 USB-CAMERA 为例,我们可以使用 dmesg 命令打印自开机以来的相关信息:

    1
    2
    # 我们可以将 dmesg 打印的信息存入 ~/Desktop/dmesg.txt 文件中
    dmesg > ~/Desktop/dmesg.txt
  2. 我们在 ~/Desktop/dmesg.txt 中查找相关的硬件相关的加载信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    # USB-HUB 相关信息。经过实测,USB3.0 Hub 在硬件识别时会出现两套设备 usb 1-3 和 usb 2-3
    [ 2248.987744] usb 1-3: new high-speed USB device number 16 using xhci_hcd
    [ 2249.141283] usb 1-3: New USB device found, idVendor=05e3, idProduct=0610, bcdDevice= 6.56
    [ 2249.141304] usb 1-3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [ 2249.141312] usb 1-3: Product: USB2.1 Hub
    [ 2249.141319] usb 1-3: Manufacturer: GenesysLogic
    [ 2249.142815] hub 1-3:1.0: USB hub found
    [ 2249.143079] hub 1-3:1.0: 4 ports detected
    [ 2249.267971] usb 2-3: new SuperSpeed USB device number 4 using xhci_hcd
    [ 2249.291145] usb 2-3: New USB device found, idVendor=05e3, idProduct=0626, bcdDevice= 6.56
    [ 2249.291164] usb 2-3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [ 2249.291171] usb 2-3: Product: USB3.1 Hub
    [ 2249.291176] usb 2-3: Manufacturer: GenesysLogic
    [ 2249.294201] hub 2-3:1.0: USB hub found
    [ 2249.294522] hub 2-3:1.0: 4 ports detected

    # 摄像头 1 相关信息
    [ 2471.254480] usb 1-3.3: new high-speed USB device number 17 using xhci_hcd
    [ 2471.451382] usb 1-3.3: New USB device found, idVendor=05a3, idProduct=9230, bcdDevice= 1.00
    [ 2471.451406] usb 1-3.3: New USB device strings: Mfr=2, Product=1, SerialNumber=0
    [ 2471.451416] usb 1-3.3: Product: USB 2.0 Camera
    [ 2471.451424] usb 1-3.3: Manufacturer: LRCP V1080P
    [ 2471.458203] usb 1-3.3: Found UVC 1.00 device USB 2.0 Camera (05a3:9230)
    [ 2471.508051] input: USB 2.0 Camera: LRCP V1080P as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.3/1-3.3:1.0/input/input22

    # 摄像头 2 相关信息
    [ 2475.426695] usb 1-3.4: new high-speed USB device number 18 using xhci_hcd
    [ 2475.590900] usb 1-3.4: New USB device found, idVendor=0c45, idProduct=ae11, bcdDevice= 1.00
    [ 2475.590920] usb 1-3.4: New USB device strings: Mfr=2, Product=1, SerialNumber=3
    [ 2475.590929] usb 1-3.4: Product: LRCP imx291
    [ 2475.590936] usb 1-3.4: Manufacturer: LRCP Technology Co., Ltd.
    [ 2475.590942] usb 1-3.4: SerialNumber: SN0001
    [ 2475.593679] usb 1-3.4: Found UVC 1.00 device LRCP imx291 (0c45:ae11)
    [ 2475.620419] input: LRCP imx291: LRCP imx291 as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.4/1-3.4:1.0/input/input23
  3. /etc/udev/rules.d/ 文件夹下创建几个用户自定义规则,以 .rules 为扩展名。

    • 修改 USB-HUB 的相关信息

      1
      sudo vim /etc/udev/rules.d/10-usb-hub.rules    
      1
      2
      3
      4
      # cat /etc/udev/rules.d/10-usb-hub.rules 
      # 修改 USB-HUB 的相关信息
      SUBSYSTEM=="usb", ATTRS{idVendor}=="05e3", ATTRS{idProduct}=="0610", SYMLINK+="usb2_hub" # KERNEL=="1.x"
      SUBSYSTEM=="usb", ATTRS{idVendor}=="05e3", ATTRS{idProduct}=="0626", SYMLINK+="usb3_hub" # KERNEL=="2.x"
    • 修改两个相机的信息

      1
      sudo vim /etc/udev/rules.d/10-usb-cam.rules   
      1
      2
      3
      4
      # cat /etc/udev/rules.d/10-usb-cam.rules 
      # 修改 USB-CAMERA 的相关信息
      KERNEL=="video*", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="05a3", ATTRS{idProduct}=="9230", ATTR{index}=="0", MODE:="0777", SYMLINK+="rgb_cam_wide"
      KERNEL=="video*", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="ae11", ATTR{index}=="0", MODE:="0777", SYMLINK+="rgb_cam_1"
      • 如果系统内接入多个相同型号的设备(即 ATTRS{idVendor}ATTRS{idProduct} 都一样)时,可以用接入接口的不同来区分他们:
      1
      2
      KERNELS=="1-[0-9].3", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="ae11", ATTR{index}=="0", MODE:="0777", SYMLINK+="rgb_cam_1"
      KERNELS=="1-[0-9].4", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="ae11", ATTR{index}=="0", MODE:="0777", SYMLINK+="rgb_cam_2"
  4. 重新加载修改过后的规则:

    1
    2
    sudo udevadm control --reload-rules
    sudo udevadm trigger
  5. 重启后查看规则是否生效:

    1
    2
    3
    4
    ls -al /dev/usb*_hub
    ls -al /dev/rgb_cam*

    udevadm info -n /dev/usb2_hub

2. 网卡设备

参考文章:


udev 通常会根据系统物理特性分配接口名称。例如,enp2s1。如果你不确定你的接口名称,你可以在系统启动后运行如下命令查看:

1
ip link

为了使 ROS 启动脚本顺利的运行,我们需要将这些端口号固定下来。创建手动命名规则,比方说,通过MAC地址将接口命名成internet0dmz0lan0 这样。

为此,请在 /etc/systemd/network/ 中创建 .link 文件,为其中的一个、一些或者说你全部的接口赋予明确的名字或是更妥善的命名规则。示例:

1
sudo vim /etc/systemd/network/10-ether0.link
1
2
3
4
5
6
[Match]
# 可以使用网卡设备的MAC地址或pci编号用来匹配
MACAddress=<your-network-device-MAC-address>

[Link]
Name=ether0
1
2
3
4
5
# 编辑完之后重启网络服务
sudo systemctl restart systemd-networkd

# 重启系统
reboot