ROS 环境变量详解(ROS EnvironmentVariables)

ROS 环境变量详解(ROS EnvironmentVariables)

在进行实机测试时发生了一件事, 我通过网线连接到机器人, 机器人IP: 192.168.250.100, 我的IP: 192.168.250.145, 但是在配置联调时我的ROS_MASTER_URI=http://10.168.2.14, 此时我在PC启动的launch文件可以挂到机器人上的rosmaster下, 与机器人本机的节点进行通信, 这就已经很神奇了

此外, 我在尝试在机器人端以及PC发送消息, 互相又收不到, 但是在机器人端收发是可以的, 在PC收发, 也时可以的, 于是引发了我的好奇, 下面是我的调研结果

参考资料

官方说明: ROS/EnvironmentVariables

查看ROS的环境变量

在Ubuntu的终端输入以下命令

env | grep ROS

如果没有单独配置其他的环境变量, 至少你可以看到下面的几个内容, 我将其重现排列一下

# ROS版本信息
ROS_VERSION=1
ROS_DISTRO=noetic
ROS_PYTHON_VERSION=3

# ROS包的位置(正常情况下使用roscd可以自动切换到这个目录下, 比如roscd std_msgs)
# 当你通过sudo apt install ros--- 下载包以后
# 默认路径也在这里
ROS_PACKAGE_PATH=/opt/ros/noetic/share

ROSLISP_PACKAGE_DIRECTORIES=
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_ROOT=/opt/ros/noetic/share/ros

# 如果需要去进行联合调试就需要配置改变量
ROS_MASTER_URI=http://localhost:11311

环境变量概览

ROS环境变量的作用大致分为以下几种

  • Finding packages: First and foremost, the ROS_ROOT and ROS_PACKAGE_PATH enable ROS to locate packages and stacks in the filesystem. You must also set the PYTHONPATH so that the Python interpreter can find ROS libraries.

寻找包的位置, 主要用于搭配roslaunch或者rosrun这样的命令, 此时两个环境变量就会发生作用

  • Effecting a Node runtime: There are also several ROS environment variables that effect how a Node runs. The ROS_MASTER_URI is an important environment variable that tells a Node where the Master is. ROS_IP and ROS_HOSTNAME affect the network address of a Node and ROS_NAMESPACE lets you change its namespace. ROS_LOG_DIR lets you set the directory where log files are written. Many of these can be overridden by [Remapping Arguments](https://wiki.ros.org/Remapping Arguments) as well, which have precedence over environment variables.

影响ROS节点的运行, 比如想要把master挂在哪个IP下, ROS产生日志的目录, ROS的变量空间的名称等, 而里面的ROS_NAMESPACE等可以通过重映射来重新改变

Modifying the build system: ROS_BINDEPS_PATH, ROS_BOOST_ROOT, ROS_PARALLEL_JOBS, and ROS_LANG_DISABLE affect where libraries are found, how they are built, and which ones are built.

这几个环境变量的作用是影响库的查找、构建方式等

ROS_ROOT

​ 该环境变量指定了roscore的位置, 主要是一些rosbuild相关的配置文件

​ 上文可以看到, ROS_ROOT/opt/ros/noetic/share/ros, 切换到该目录, 可以看到一些配置文件

/opt/ros/noetic/share/ros/config 该目录下存放的是rosconsole控制台的配置

/opt/ros/noetic/share/ros/core/rosbuild 这里存放的是**构建(rosbuild)**相关的配置文件, 例如FindPkgConfig.cmake等等

ROS_PACKAGE_PATH

​ 这个应该都很熟悉, 就是ros包的位置, 当你想要使用一个ros包例如roslaunch turtle_tf2 turtle_tf2_demo.launch , ros会自动在该目录下查找是否存在, 如果存在就可以使用双tab补全并运行

​ 当使用source /devel/setup.bash时, 也会改变这个环境变量, ros会自动把这个包的路径添加到ROS_PACKAGE_PATH, 这里需要注意: 新添加的路径会在前面, 也就是说, 当你运行同名包时, ros会优先考虑catkin_ws/src下的包, 这一点可以通过env | grep ROS 查看环境变量的变化, 例如

source ~/test/devel/setup.bash 
env | grep ROS
# 输出的内容如下
ROS_PACKAGE_PATH=/home/smile/test/src:/opt/ros/noetic/share

​ 因此, 需要十分注意使用source的顺序, 之前还遇到过互相冲突的情况, 但我忘了例子是什么后来一般在进行source时, 尤其是写在bash文件中的source时, 总会加一个标志--extend, 这样在前面的路径下找不到包时, 会往后寻找, 直到所有路径下都没有包时才报错, 以下回答是GPT4的原文

让我们通过一个具体的例子来说明source devel/setup.bashsource devel/setup.bash --extend命令的区别。

假设你正在开发两个不同的ROS项目,它们分别位于~/ros_workspace1~/ros_workspace2两个工作区。每个工作区都有自己的包和环境设置。

  1. 不使用--extend:
  • 当你在终端执行source ~/ros_workspace1/devel/setup.bash时,你的环境变量(例如ROS_PACKAGE_PATH)将被设置为仅包含ros_workspace1中的路径。
  • 然后,如果你在同一个终端执行source ~/ros_workspace2/devel/setup.bash,环境变量将被更新为仅包含ros_workspace2的路径。这将覆盖之前ros_workspace1的设置。
  1. 使用--extend:
  • 首先执行source ~/ros_workspace1/devel/setup.bash
  • 然后,在同一个终端执行source ~/ros_workspace2/devel/setup.bash --extend。这时,ros_workspace2的路径被添加到环境变量中,而不会覆盖掉ros_workspace1的路径。

通过使用--extend选项,你可以同时访问两个工作区中的包和资源。这对于需要在多个项目之间进行切换和整合的开发者来说非常有用。这样,你就可以在不同的工作区间灵活地共享和测试代码,而不需要每次都重置整个环境变量

我对这段回答做了测试, 发现确实是被覆盖了, 整个终端的内容如下

不使用--extend

smile@smile:~$ cd test/
smile@smile:~/test$ source devel/setup.bash 
smile@smile:~/test$ env | grep ROS
ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/test/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/test/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic
smile@smile:~/test$ cd ~/Desktop/github/
smile@smile:~/Desktop/github$ source devel/setup.bash 
smile@smile:~/Desktop/github$ env | grep ROS
ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/Desktop/github/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/Desktop/github/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic

使用--extend

smile@smile:~$ cd test/
smile@smile:~/test$ source devel/setup.bash 
smile@smile:~/test$ env | grep ROS
ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/test/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/test/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic
smile@smile:~/test$ cd ~/Desktop/github/
smile@smile:~/Desktop/github$ source devel/setup.bash --extend
smile@smile:~/Desktop/github$ env | grep ROS
ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/Desktop/github/src:/home/smile/test/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/Desktop/github/devel/share/common-lisp:/home/smile/test/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic

ROS_MASTER_URI

单台主机(PC) , 分两个终端进行测试

运行roslaunch的终端配置 localhost:11311

终端1(执行ROS节点的终端)环境变量如下, 在整个单元该终端环境不变

ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/test/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/test/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic

roslaunch自动生成的log中可以看到以下内容

smile@smile:~/test$ roslaunch pointcloud_concatenate lidar_processor.launch 
... logging to /home/smile/.ros/log/360e2c34-b66e-11ee-a6d2-9bbe57a404cc/roslaunch-smile-16628.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://smile:33215/
# 还有这一句
auto-starting new master
process[master]: started with pid [16636]
ROS_MASTER_URI=http://localhost:11311

我去查了本机的hosts, 为什么两个不在一起?

smile@smile:~$ cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	smile

我问了GPT, 回答简单总结来说是这样的

  • ROS_MASTER_URI在同一个局域网下, 或者说整个系统中应该只存在一个

    • 主要作用是负责在节点之间交换信息, 当相关节点的ROS_MASTER_URI一致时, 节点之间就可以进行通信, 通常意义上的rostopic pub 就是将消息发到该地址, 同时由其他机器配置了相同ROS_MASTER_URI就可以进行通信

    • 需要注意的是, 这个ROS_MASTER_URI需要是一个实际存在的网络接口

    • roslaunch server在局域网下可以存在很多, 这个server的作用是负责节点的管理, 与消息通信无关, 因此不用太在意这个server

因此虽然这个server我没有配置, 但是节点之间通信是正常的, 即, 两个终端不进行任何配置时, 是可以进行通信的

接收端 ROS_MASTER_URI=http://localhost:11311
ROS_VERSION=1
ROS_PYTHON_VERSION=3
ROS_PACKAGE_PATH=/home/smile/test/src:/opt/ros/noetic/share
ROSLISP_PACKAGE_DIRECTORIES=/home/smile/test/devel/share/common-lisp
ROS_ETC_DIR=/opt/ros/noetic/etc/ros
ROS_MASTER_URI=http://localhost:11311
ROS_ROOT=/opt/ros/noetic/share/ros
ROS_DISTRO=noetic

可以正常通信

接收端ROS_MASTER_URI=http://10.168.2.183:11311

通过export ROS_MASTER_URI=http://ip:11311 修改 ROS_MASTER_URI

我的ip为10.168.2.183:11311

因此在发布终端测试如下

smile@smile:~/test$ export ROS_MASTER_URI=http://10.168.2.183:11311
smile@smile:~/test$ env | grep ROS_MASTER_URI
ROS_MASTER_URI=http://10.168.2.183:11311
smile@smile:~/test$ rostopic list | grep safe
/safe_lidar_state
smile@smile:~/test$ rostopic echo /tf_static 
transforms: 
  - 
    header: 
      seq: 0
      stamp: 
        secs: 1705629496
        nsecs: 954055797
      frame_id: "lidar"
    child_frame_id: "base_link"
    transform: 
      translation: 
        x: 0.0
        y: -1.0
        z: -0.5
      rotation: 
        x: 0.0
        y: 0.0
        z: 0.0
        w: 1.0
---

同样可以在这里进行通信, 获取话题列表, 获取消息

所以在这里, localhost跟本机ip, 都可以作为收发ros消息的uri, 而且可以同时收发localhost的消息

接收端ROS_MASTER_URI=http://123.123.123.123:11311

通过export ROS_MASTER_URI=http://123.123.123.123:11311 修改为新的网口, 该网口不存在, 随便写的

测试如下:

smile@smile:~/test$ export ROS_MASTER_URI=http://123.123.123.123:11311
smile@smile:~/test$ rostopic list | grep safe

无任何反应, 且整个终端阻塞在这行命令上, 需要Ctrl C终结, 因此不存在的网口无法和ROS通信

接收端ROS_MASTER_URI=http://192.168.158.145:11311

通过export ROS_MASTER_URI=http://ip:11311 修改为新的网口, 该网口是主机和雷达通信的网口

测试如下:

smile@smile:~/test$ export ROS_MASTER_URI=http://192.168.158.145:11311
smile@smile:~/test$ env | grep ROS_MASTER_URI
ROS_MASTER_URI=http://192.168.158.145:11311
smile@smile:~/test$ rostopic list | grep safe_lidar
/safe_lidar_state
smile@smile:~/test$ rostopic echo /tf_static 
transforms: 
  - 
    header: 
      seq: 0
      stamp: 
        secs: 1705629496
        nsecs: 954055797
      frame_id: "lidar"
    child_frame_id: "base_link"
    transform: 
      translation: 
        x: 0.0
        y: -1.0
        z: -0.5
      rotation: 
        x: 0.0
        y: 0.0
        z: 0.0
        w: 1.0
---

神奇的事情发生了, 即使我把ROS_MASTER_URI修改成了网口的IP, 他竟然还能收到!

这甚至让我开始好奇是不是出了什么问题, 但是截图证明, 确实是在localhost

ROS 环境变量详解(ROS EnvironmentVariables)_第1张图片

我只能将其解释为localhost在一台主机的任何终端, 可以代表任何存在于该主机硬件上的IP

两台主机 程序运行在10.168.2.177 接收端为10.168.2.183

roslaunch客户端: ROS_MASTER_URI=http://localhost:11311 接收端: ROS_MASTER_URI=http://localhost:11311

接收端:

smile@smile:~$ export ROS_MASTER_URI=http://localhost:11311
smile@smile:~$ rosnode list
ERROR: Unable to communicate with master!

无法读取消息, 显示没有master存在

roslaunch客户端: ROS_MASTER_URI=http://localhost:11311 接收端: ROS_MASTER_URI=http://10.168.2.177:11311

接收端:

smile@smile:~$ export ROS_MASTER_URI=http://10.168.2.177:11311
smile@smile:~$ rosnode list
/lidar1/lidar01
/lidar_processor
/map2baselink
/node_controller
/rosout
smile@smile:~$ rostopic echo /tf_static
transforms: 
  - 
    header: 
      seq: 0
      stamp: 
        secs: 1706060435
        nsecs: 182425397
      frame_id: "lidar"
    child_frame_id: "base_link"
    transform: 
      translation: 
        x: 0.0
        y: -1.0
        z: -0.5
      rotation: 
        x: 0.0
        y: 0.0
        z: 0.0
        w: 1.0
---

当在接收端将ROS_MASTER_URI修改为执行端的IP时, 可以正常通信, 获取消息, 发布消息都是正常的

ROS_IP

ROS_IP 环境变量在ROS(机器人操作系统)中的作用是指定其他ROS节点应该用来与当前节点通信的IP地址。这在多网卡环境或者复杂的网络配置中尤为重要。举个例子来说明其重要性:

假设你有一个机器人系统,其中包含多个计算节点(例如,一个笔记本电脑,一个台式机,和机器人上的一个嵌入式系统),它们都在同一个局域网中。每个计算节点都运行着一个或多个ROS节点(比如传感器数据处理器,控制算法,状态监视器等)。所有这些节点需要相互通信来交换信息,如传感器数据、控制命令等。

如果这些计算节点具有多个网络接口(例如,既有有线以太网连接,又有无线Wi-Fi连接),那么就需要明确指定哪个接口用于ROS通信。这就是ROS_IP的作用所在。

示例场景:

  1. 台式机:有一个IP地址为192.168.1.101的有线连接,另一个IP地址为192.168.1.201的无线连接。
  2. 笔记本电脑:有一个IP地址为192.168.1.102的有线连接,另一个IP地址为192.168.1.202的无线连接。
  3. 机器人上的嵌入式系统:只有一个IP地址为192.168.1.103的无线连接。

不使用ROS_IP的情况:

  • 如果不设置ROS_IP,ROS节点可能会自动选择一个网络接口(例如,根据默认路由选择)。在这种情况下,台式机可能会通过其无线接口与笔记本电脑通信,但由于机器人只有无线连接,它可能无法与台式机的有线接口通信。

使用ROS_IP的情况:

  • 在台式机上,设置ROS_IP=192.168.1.101,确保所有ROS通信都通过有线接口进行。
  • 在笔记本电脑上,设置ROS_IP=192.168.1.102,同样确保通过有线接口进行通信。
  • 机器人上的嵌入式系统只有一个网络接口,因此可能不需要设置ROS_IP

通过这种方式,所有节点都能够通过有线网络高效地相互通信,同时确保机器人能够通过无线连接加入到ROS网络中。这就体现了ROS_IP在确保正确和高效通信方面的重要性。

你可能感兴趣的:(机器人)