P5c-1:把 Gazebo 仿真环境真正装起来(独立 conda 环境),并固化安装/构建流程

在此之前,仓库里所有 Gazebo 相关的东西都是"声明"而非"事实":
arm.gazebo.xacro 零引用、stack.launch.py 的 isaac 分支只打印日志、
没有 world 文件、devices/sim/gazebo 下的目录全是空占位。
更关键的是:**这台机器上根本没装 Gazebo**,而 ROS 在 conda 里,没有 /opt/ros,
apt 的 ros-humble-gazebo-* 装了也不会被 conda 的 ROS 认到。

环境(与真机环境物理隔离)
- 新建 conda 环境 ros_gz_sim,**不碰能跑真机的 ros_flexiv**。
  仿真要往环境里塞 Gazebo 及 protobuf/ignition/渲染一整条链,
  和真机环境混装随时可能把它弄坏,代价远大于仿真带来的便利。
- 频道组合照抄 ros_flexiv 的安装记录(robostack-staging + conda-forge),
  那是本机已经证明可用的搭配。

两个实测踩到的坑(都写进了脚本注释,避免下次重踩)
1. empy 版本:conda 默认给 empy 4.x,而 Humble 的 rosidl_adapter 用的是 3.x 的
   API。症状是构建自定义消息包时报 `_interpreter.shutdown()` 的 AttributeError,
   报错点在 rosidl_adapter 内部,完全看不出跟 empy 有关。必须钉 empy=3.3.4。
2. conda 激活是**堆叠**的:同一 shell 里从 ros_gz_sim 切到 ros_flexiv 后,
   `which python` 指向 ros_flexiv,但 `python -m pip` 却解析到 ros_gz_sim 的 pip,
   导致 colcon --symlink-install 报 "option --editable not recognized"。
   这个报错和真实原因毫无关系,极易误判成"环境被搞坏了"。
   现在 scripts/lib/conda_switch.sh 会在切换前退到 base,两个 env 脚本共用。

仿真用独立构建产物
- build_sim/ + install_sim/(scripts/build_sim.sh)。工作区只有一份源码,
  若仿真也写 install/,就会覆盖真机的构建产物。
- 排除 11 个真机专属包(flexiv_hardware 需要 Flexiv RDK C++ SDK,根本装不上;
  另有真机驱动/GPIO/MoveIt 配置/真机 bringup),每个都在脚本里写了理由。

已验证(不是只看退出码)
- Gazebo Fortress 6.16 无头启动正常,物理初始化完成
- /clock 桥接到 ROS 成功收到仿真时间;ros_gz_sim create 成功往世界里建物
- 真机构建 18 包通过、233 项测试通过,build/ install/ 时间戳未变(没被覆盖)
- 仿真侧 7 包构建通过;两环境互切后各自都能正常构建

注:Humble 配的是 Gazebo Fortress(ignition-gazebo6,命令行 `ign gazebo`),
不是现代的 gz-sim —— 后者要 libprotobuf>=6.31,与 RoboStack Humble 钉住的
5.29.3 解不开依赖。Fortress 属于新 Gazebo 家族(非已 EOL 的 Classic),
且是 Humble 的官方配对版本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
arm_dev
2026-09-23 13:52:04 +08:00
parent d289168dd0
commit a89250d05d
6 changed files with 366 additions and 1 deletions
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# =============================================================================
# conda 环境切换的公共逻辑。被 scripts/env.sh 与 scripts/sim_env.sh 共用。
#
# 为什么需要这个:conda 的 activate 是**堆叠**的。在同一个 shell 里反复
# activate / 切换不同环境,会不断往 PATH、PYTHONPATH 里追加条目,而不会
# 清掉前一个环境留下的。实测的后果非常隐蔽:
#
# 在同一个 shell 里先 activate ros_gz_sim 再切 ros_flexiv 之后,
# `which python` 指向 ros_flexiv/bin/python,
# 但 `python -m pip` 却解析到 ros_gz_sim 的 pip!
# 于是 colcon --symlink-install 报:
# error: option --editable not recognized
# 这个报错和真实原因(shell 脏了)毫无关系,极易被误判成
# "环境被搞坏了 / setuptools 版本不对",进而去做一堆无用的修复。
#
# 对策:切换前先把堆叠的激活全部退掉(退到 base),再激活目标环境。
# =============================================================================
# conda_deactivate_all: 退到 base(CONDA_SHLVL=1)。任何环境下调用都安全。
conda_deactivate_all() {
local guard=0
# CONDA_SHLVL 是 conda 自己维护的激活层数;base 为 1。
# 加 guard 是为了防止某些畸形状态下无限循环。
while [[ "${CONDA_SHLVL:-1}" -gt 1 && "${guard}" -lt 20 ]]; do
conda deactivate 2>/dev/null || break
guard=$((guard + 1))
done
return 0
}
# conda_switch_clean <env_name> [<other_env_name>]
# 干净地激活 <env_name>;若当前已在 <other_env_name> 里,先退到 base 再切,
# 并提示用户最好开新 shell。
# 返回 0 = 成功;非 0 = 目标环境不存在。
conda_switch_clean() {
local target="$1"
local other="${2:-}"
if [[ -n "${other}" && "${CONDA_DEFAULT_ENV:-}" == "${other}" ]]; then
echo "提示: 当前 shell 激活的是 ${other},即将切到 ${target}。" >&2
echo " conda 的激活是堆叠的,同一 shell 里来回切会残留 PATH/PYTHONPATH," >&2
echo " 典型症状是构建报 \"--editable not recognized\" 这类与真实原因无关的错。" >&2
echo " 这里会先退到 base 再切以规避;但最稳妥仍是开一个新 shell。" >&2
fi
conda_deactivate_all
if ! conda activate "${target}" 2>/dev/null; then
return 1
fi
return 0
}
# 供调用方按需取消设置,避免污染使用者的变量空间。
# (bash 没有"私有函数"概念,命名上统一加 conda_ 前缀降低撞名概率。)