用 Qt 给 ROS2 做命令大全:为什么当年死活跑不通?

一次被
command not found卡死的项目,一次迟来的复盘。
前言
几年前,我想做一个很简单的小工具。
左边是一列 ROS2 常用命令:
- ros2 topic list
- ros2 node list
- ros2 service list
- ros2 param list
右边一个「执行」按钮。
点一下。
自动打开一个终端。
自动执行命令。
对于新手来说,不需要记命令,不需要查文档,只需要点按钮。
我甚至连界面都已经用 Qt 做好了。
结果项目死在了最后一步。
终端弹出来了。
命令也输入进去了。
但所有 ROS2 命令全部报错:
bash
ros2: command not found
当时折腾了很久。
最后项目被我扔进了硬盘。
直到最近重新翻到当年的代码,我才发现,问题根本不在 Qt。
而在 Linux Shell。
为什么手动打开终端没问题?
ROS2 用户几乎都会在:
bash
~/.bashrc
里面写:
bash
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
所以每次打开终端:
bash
Ctrl + Alt + T
ROS2 都可以直接使用:
bash
ros2 topic list
这让很多人误以为:
ROS2 命令本来就存在于系统中。
实际上并不是。
真正发生的是:
text
打开终端
↓
bash 启动
↓
读取 ~/.bashrc
↓
source ROS2
↓
环境变量加载
↓
ros2 可执行
而 Qt 启动终端时,这个流程发生了变化。
Qt 打开的终端为什么不行?
Qt 通常通过:
cpp
QProcess
或者:
cpp
system()
启动外部终端。
例如:
cpp
gnome-terminal -- bash -c "ros2 topic list"
这里的:
bash
bash -c
属于非交互 shell。
而非交互 shell:
- 不读取 ~/.bashrc
- 不读取 ROS 环境
- 不加载 PATH
于是:
bash
ros2
根本不存在。
最终就出现:
bash
command not found
这也是为什么:
- 你自己打开终端没问题;
- Qt 打开的终端全部报错。
因为它们根本不是同一种 shell。
第一种解决方案:显式 source
最直接的方法。
执行命令之前先手动加载环境。
cpp
QString cmd =
"source /opt/ros/humble/setup.bash && "
"source ~/ros2_ws/install/setup.bash && "
"ros2 topic list";
然后:
cpp
gnome-terminal -- bash -c "..."
最终执行:
bash
source /opt/ros/humble/setup.bash &&
source ~/ros2_ws/install/setup.bash &&
ros2 topic list
这是最稳定的方案。
无论用户有没有配置 bashrc。
都能运行。
第二种方案:交互式 shell
bash:
bash
-i
表示:
text
interactive shell
于是:
cpp
bash -ic "ros2 topic list"
bash 会主动读取:
bash
~/.bashrc
如果用户提前配置:
bash
source /opt/ros/humble/setup.bash
那么命令就能执行。
但缺点也很明显。
换台电脑可能直接失效。
第三种方案:登录 shell
还有:
bash
-l
即:
bash
login shell
它会读取:
bash
~/.profile
~/.bash_profile
效果类似。
不过 Linux 用户配置方式差异很大。
实际项目里并不推荐依赖。
后来我发现:别人根本不这么干
后来我去翻了一些 Qt + ROS2 项目。
结果发现一个很有意思的现象。
它们几乎都不会:
text
Qt
↓
打开新终端
↓
执行 ros2
而是:
text
source 环境
↓
ros2 run xxx
↓
启动 Qt
↓
QProcess 调 ros2
也就是说:
Qt 本身就是 ROS2 节点。
这样:
- Qt 有环境;
- 子进程继承环境;
- 根本不会出现 source 问题。
问题从根源上消失。
于是出现了两条路线
路线一:做成 ROS2 包
启动:
bash
ros2 run ros2_command_gui app
优点:
- 环境永远正确;
- 部署简单;
- 不需要处理 source;
- 可以内嵌终端。
这是现在主流 GUI 工具的做法。
路线二:继续打开外部终端
仍然:
text
点击按钮
↓
打开终端
↓
执行命令
但在执行前:
bash
source ROS2
这样就能保证环境正确。
这种模式其实目前很少有人做。
反而可能是一个空白方向。
这几年最大的收获
回头看。
当年让我放弃整个项目的问题。
其实只有一句话:
非交互 shell 不会读取 bashrc。
Qt 没问题。
ROS2 没问题。
QProcess 也没问题。
真正的问题,是当时对 Linux shell 的理解还不够。
很多时候。
一个看似卡了几年的坑。
最终可能只是一行:
bash
source setup.bash
而现在,这个项目终于又可以重新开始了。