在进行实机测试时发生了一件事, 我通过网线连接到机器人, 机器人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
在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
andROS_PACKAGE_PATH
enable ROS to locate packages and stacks in the filesystem. You must also set thePYTHONPATH
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
andROS_HOSTNAME
affect the network address of a Node andROS_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
, andROS_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
时, 也会改变这个环境变量, 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.bash
和source devel/setup.bash --extend
命令的区别。假设你正在开发两个不同的ROS项目,它们分别位于
~/ros_workspace1
和~/ros_workspace2
两个工作区。每个工作区都有自己的包和环境设置。
- 不使用
--extend
:
- 当你在终端执行
source ~/ros_workspace1/devel/setup.bash
时,你的环境变量(例如ROS_PACKAGE_PATH
)将被设置为仅包含ros_workspace1
中的路径。- 然后,如果你在同一个终端执行
source ~/ros_workspace2/devel/setup.bash
,环境变量将被更新为仅包含ros_workspace2
的路径。这将覆盖之前ros_workspace1
的设置。
- 使用
--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
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
我只能将其解释为localhost在一台主机的任何终端, 可以代表任何存在于该主机硬件上的IP
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存在
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(机器人操作系统)中的作用是指定其他ROS节点应该用来与当前节点通信的IP地址。这在多网卡环境或者复杂的网络配置中尤为重要。举个例子来说明其重要性:
假设你有一个机器人系统,其中包含多个计算节点(例如,一个笔记本电脑,一个台式机,和机器人上的一个嵌入式系统),它们都在同一个局域网中。每个计算节点都运行着一个或多个ROS节点(比如传感器数据处理器,控制算法,状态监视器等)。所有这些节点需要相互通信来交换信息,如传感器数据、控制命令等。
如果这些计算节点具有多个网络接口(例如,既有有线以太网连接,又有无线Wi-Fi连接),那么就需要明确指定哪个接口用于ROS通信。这就是
ROS_IP
的作用所在。
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
在确保正确和高效通信方面的重要性。