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
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
# =============================================================================
# 创建 / 修复 Gazebo 仿真用的独立 conda 环境。
#
# 为什么单独一个环境,而不是往 ros_flexiv 里装:
# ros_flexiv 是**能跑真机**的那一套。仿真需要往环境里加 Gazebo 及其一大堆
# 依赖(protobuf / ignition / 渲染栈),和真机环境混装随时可能把它弄坏。
# 真机环境坏了,代价远大于仿真带来的便利。所以两者物理隔离。
#
# 用法:
# bash scripts/install_sim_env.sh # 创建或补齐(幂等,可重复跑)
# bash scripts/install_sim_env.sh --verify # 只验证现有环境,不安装
#
# 装完用 scripts/sim_env.sh 激活,用 scripts/run_sim_gz.sh 启动。
# =============================================================================
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_NAME="${SIM_ENV_NAME:-ros_gz_sim}"
CONDA_SH="${HOME}/anaconda3/etc/profile.d/conda.sh"
# 频道组合照抄现有 ros_flexiv 环境的安装记录,那是已经证明可用的搭配。
CHANNELS=(-c robostack-staging -c conda-forge)
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
info() { printf ' \033[36m·\033[0m %s\n' "$1"; }
die() { printf ' \033[31m✗ %s\033[0m\n' "$1" >&2; exit 1; }
if [[ ! -f "${CONDA_SH}" ]]; then
die "找不到 conda: ${CONDA_SH}(本脚本假定 anaconda 装在 ~/anaconda3)"
fi
# shellcheck disable=SC1090
source "${CONDA_SH}"
# --- 验证 -------------------------------------------------------------------
verify() {
info "激活 ${ENV_NAME} 并自检…"
conda activate "${ENV_NAME}" 2>/dev/null || die "激活失败,环境可能没建好"
echo
echo "1) 仿真器本体"
# 注意:Humble 配的是 Gazebo Fortress,命令行是 `ign gazebo`,不是 `gz sim`。
# 现代包名 gz-sim 在 conda-forge 上要 libprotobuf>=6.31,而 RoboStack 的
# Humble 把 protobuf 钉在 5.29.3,混装会解不开依赖。Fortress 是官方配对版本。
ign gazebo --version 2>&1 | head -1 | sed 's/^/ /'
echo
echo "2) ROS 侧桥接与力控"
local missing=0 p
for p in ros_gz_sim ros_gz_bridge gz_ros2_control controller_manager ros2_control xacro; do
if ros2 pkg prefix "$p" >/dev/null 2>&1; then
ok "$p"
else
printf ' \033[31m✗ %s 缺失\033[0m\n' "$p"; missing=1
fi
done
echo
echo "3) 构建工具链(Humble 的 rosidl 需要 empy 3.x)"
local empy_ver
empy_ver="$(python -c 'import em; print(em.__version__)' 2>/dev/null || echo '?')"
if [[ "${empy_ver}" == 3.* ]]; then
ok "empy ${empy_ver}"
else
printf ' \033[31m✗ empy %s —— 必须是 3.x,否则 rosidl 生成接口时会报\n' "${empy_ver}"
printf ' "AttributeError: NoneType object has no attribute shutdown"\033[0m\n'
missing=1
fi
echo
echo "4) 仿真能真跑起来吗(无头空跑 30 步)"
local world
world="$(find "${CONDA_PREFIX}/share" -name empty.sdf -path '*gazebo*' 2>/dev/null | head -1)"
if [[ -n "${world}" ]] && timeout 60 ign gazebo -s -r --iterations 30 "${world}" >/dev/null 2>&1; then
ok "仿真引擎正常启动"
else
printf ' \033[31m✗ 仿真引擎启动失败\033[0m\n'; missing=1
fi
echo
if [[ "${missing}" -eq 0 ]]; then
echo "环境就绪。下一步:"
echo " source scripts/sim_env.sh"
echo " bash scripts/build_sim.sh"
echo " bash scripts/run_sim_gz.sh"
else
echo "环境不完整,重跑 bash scripts/install_sim_env.sh 补齐。"
return 1
fi
}
if [[ "${1:-}" == "--verify" ]]; then
verify
exit $?
fi
# --- 安装 -------------------------------------------------------------------
echo "=== 1/4 创建环境 ${ENV_NAME} ==="
if conda env list | awk '{print $1}' | grep -qx "${ENV_NAME}"; then
ok "已存在,跳过创建"
else
conda create -y -n "${ENV_NAME}" python=3.11 pip || die "创建环境失败"
ok "已创建"
fi
echo
echo "=== 2/4 安装 ROS 2 Humble 仿真栈 ==="
info "ros-humble-ros-gz 会一并带入 Gazebo Fortress(ignition-gazebo6)"
conda install -y -n "${ENV_NAME}" "${CHANNELS[@]}" \
ros-humble-ros-base \
ros-humble-ros-gz \
ros-humble-gz-ros2-control \
ros-humble-ros2-control \
ros-humble-ros2-controllers \
ros-humble-controller-manager \
ros-humble-robot-state-publisher \
ros-humble-joint-state-publisher \
ros-humble-joint-state-publisher-gui \
ros-humble-xacro \
ros-humble-tf2-ros \
ros-humble-camera-info-manager \
ros-humble-image-transport-plugins \
compilers cmake make pkg-config catkin_pkg lark-parser numpy \
colcon-common-extensions || die "ROS 仿真栈安装失败"
echo
echo "=== 3/4 钉住 empy 3.x(Humble 的硬性要求)==="
# 坑:conda 默认会给 empy 4.x,而 Humble 的 rosidl_adapter 用 empy 3.x 的 API。
# 症状是构建自定义消息包时报 '_interpreter.shutdown()' 的 AttributeError,
# 报错点在 rosidl_adapter 内部,看起来完全不像 empy 的问题。
conda install -y -n "${ENV_NAME}" "${CHANNELS[@]}" "empy=3.3.4" || die "empy 降级失败"
ok "empy 3.3.4"
echo
echo "=== 4/4 自检 ==="
verify