
1. 先把版本关系捋清楚再动手Ubuntu 20.04 上装 Gazebo绝大多数人第一条命令就是apt install gazebo11十分钟搞定能跑仿真能开 GUI看起来没什么问题。可一旦你要在 Gazebo 本体上动点东西——比如改物理引擎的积分步长、给渲染层打一个自定义 patch、排查为什么某个 collision 检测在特定网格上会漏、或者想在 CI 里固定一个可复现的仿真环境——apt 给的二进制包就不够用了。它是一堆已经编译好的.so报错信息只到符号级别你改不了也看不到中间过程。Gazebo 源码安装这件事本质是把一个由四十多个第三方库撑起来的仿真平台在自己的机器上重新走一遍构建链路。Ubuntu 20.04focal这个发行版很有意思它的系统源里同时存在 Gazebo 9 和 Gazebo 11 的软件包还有一整套 ignition 系列的库版本组合被锁得很死。这意味着你既有依赖大部分能 apt 装好的便利也有某个库版本对不上就得自己补编译的麻烦。这篇内容面向三类人一是在做机器人仿真相关开发需要改 Gazebo 源码或调试其内部逻辑的二是环境受限、不能从外部源装包必须离线自建的三是已经踩过apt install版本不匹配的坑想彻底搞清楚依赖关系的人。我会把版本选型、依赖梳理、CMake 参数设计、编译排错、多版本共存这几件事按我实际操作的顺序讲一遍中间穿插踩过的坑和判断依据。1.1 Ubuntu 20.04 上的版本选择空间为什么这么窄Gazebo 的版本号有一条隐性规则它和 sdformat 的大版本是绑定死的。Gazebo 9 配 sdformat6Gazebo 11 配 sdformat9Gazebo 10 是个过渡版本配 sdformat8。你在 focal 的系统源里能看到libsdformat9-dev这是一个很强的信号——这个发行版是奔着 Gazebo 11 去的。事实上 focal 的官方仓库里就有gazebo11这个包说明上游已经把 11 作为这个 LTS 版本的默认仿真平台。明白这层关系之后选版本就有了依据。如果你只是想在 focal 上做 Gazebo 的源码构建锁定 Gazebo 11 sdformat9 ignition-math6 这条线是最省事的组合因为这三个东西全部能从 focal 的系统源里直接装到开发包你只需要编译 Gazebo 本体。反过来如果你想编 Gazebo 9就得自己补 sdformat6而 sdformat6 在 focal 源里是没有的得从源码拉一环套一环工作量翻倍。还有一层是编译器。focal 默认的 gcc 是 9.4.0支持到 C17。Gazebo 11 的构建脚本要求 C17这条正好卡在边界上——gcc 9 刚好够用gcc 8 就不行了。所以如果你在一台从 18.04 升级上来的机器上折腾先gcc --version确认一下别在这上面浪费半天。注意Gazebo 和 ROS 的版本也是绑定的。ROS Noetic 官方支持的 Gazebo 就是 11它自带的ros-noetic-gazebo-ros-pkgs编译时链接的是 apt 版 gazebo11。你在源码里装一份自定义的 Gazebo运行时很容易和 ROS 那套撞车这个后面第 6 章专门讲。1.2 三条路线的成本对照在动手敲命令之前值得花五分钟对比一下三条常见路径别一头扎进去。我按自己实际干过的经验整理成表路线具体做法耗时可控程度适合场景纯 aptapt install gazebo11 libgazebo11-dev5 分钟低完全黑盒只做上层应用开发不碰引擎混合式依赖全部 apt 装只源码编译 Gazebo 本体40~70 分钟中高能改 Gazebo 代码改 Gazebo 源码、调引擎参数、打补丁全源码ignition-math6 → sdformat9 → Gazebo 全链自编2~4 小时最高依赖版本完全自控离线环境、需要非常规库版本、做发行定制混合式是我推荐的默认选择理由很实在ignition-math6 和 sdformat9 这两个库你几乎没有理由去改它们它们提供的是数学类型和 SDF 解析属于稳定的地基。从源码编它们除了浪费时间还会带来 ABI 兼容的额外风险比如你的 sdformat9 和别人的 sdformat9 符号不一致动态库互相覆盖。而 Gazebo 本体才是你真正要动的部分把它单独拎出来编译收益最大、成本最低。全源码那条路我走过一次是为了在一个无外网的内网环境里搭仿真节点。那次最大的教训是依赖链的编译顺序错了会导致 CMake 找不到刚装好的库因为find_package走的是CMAKE_PREFIX_PATH你必须保证每个前置库 install 完成后后续构建时能把路径带进去。这个细节在第 2 章会展开。1.3 从依赖图看编译顺序把依赖关系画成文字比画图更清楚。Gazebo 11 的依赖大致分四层第一层是 C 基础库和系统库Boost线程、文件系统、正则、Protobuf消息序列化、tinyxml2XML 解析、libtar、libusb、sqlite3、jsoncpp、yaml-cpp、zmq。这些全部 apt 装没有例外。第二层是数学与格式层ignition-math6 提供 Vector3、Matrix4、Quaternion 这些类型sdformat9 依赖 ignition-math6负责把.sdf文件解析成内存对象。这两个是 Gazebo 的骨架。第三层是传输与资源层ignition-msgs5消息定义、ignition-transport8节点间通信、ignition-common3通用工具、材质、动画、ignition-fuel-tools4在线模型库拉取、ignition-plugin1插件加载。这一层是 gzserver 和 gzclient 之间说话用的。第四层是功能后端OGRE 1.9渲染、DART 6.9 或 Bullet 或 Simbody物理、FFmpeg视频录制、Qt5GUI 控件、FreeImage贴图、GTS三角网格布尔运算、OpenCV可选视觉传感器。顺序上自底向上同层内先数学后格式。如果你走全源码路线顺序必须是ignition-math6 → sdformat9 → ignition-common3 → ignition-msgs5 → ignition-transport8 → ignition-fuel-tools4 → ignition-plugin1 → gazebo。搞错任何一个后一个的 CMake 就会报Could not find a configuration file for package xxx。2. 编译前的环境基线与依赖清单这一步最容易被跳过但它决定了后面是顺风顺水还是连环报错。我习惯在动手前先做一次体检把所有可能出问题的地方提前暴露。2.1 系统基线检查磁盘、内存、编译器一个都不能少先说磁盘。Gazebo 的源码树本身不大几百 MB但 build 目录会膨胀得厉害。我实测下来-DCMAKE_BUILD_TYPERelease情况下build 目录峰值在 3.5~4.5 GBinstall 目录约 800 MB。加上源码和 git 历史预留 15 GB 是比较安心的数字。用df -h /看一眼如果只剩 8 GB 就别硬上编到 80% 报No space left on device是真的会让人血压升高。内存这条更关键。Gazebo 里有好几个巨型翻译单元比如physics/src/下那些模板展开极深的文件单个.o编译进程峰值能吃 1.5~2 GB。用make -j$(nproc)在 8 核机器上会同时跑 8 个编译进程理论峰值 12~16 GB实际到 10 GB 左右。如果你的机器只有 8 GB 内存又开了 8 线程十有八九会被 OOM killer 干掉表现出来是make突然报internal compiler error: Killed (program cc1plus)——这个错误信息极具迷惑性很多人以为是代码问题其实是内存不够。计算的简单办法并行度 floor(可用内存GB / 2)同时不超过nproc。16 GB 内存 8 核取-j88 GB 内存 8 核取-j44 GB 内存就别多线程了老老实实-j2。编译器版本用gcc --version确认9.x 即可。CMake 要求 3.10 以上focal 自带 3.16.3够用。还有pkg-config这个经常被遗忘但它决定了 CMake 能不能找到一堆系统库。# 一次性体检 df -h / free -g nproc gcc --version | head -1 cmake --version | head -1 pkg-config --version2.2 一次性装齐 apt 依赖这里是重头戏。Gazebo 官网给的依赖清单是按功能分组的很长但可以合并成一条命令。我把它整理成下面这样直接复制执行sudo apt update sudo apt install -y \ git cmake build-essential pkg-config ruby \ libboost-all-dev \ libprotobuf-dev protobuf-compiler \ libtinyxml2-dev libtar-dev libusb-1.0-0-dev \ libsqlite3-dev libjsoncpp-dev libyaml-dev libzmq3-dev \ libfreeimage-dev libgts-dev libcurl4-openssl-dev \ libignition-math6-dev libsdformat9-dev \ libignition-common3-dev libignition-msgs5-dev \ libignition-transport8-dev libignition-fuel-tools4-dev \ libignition-plugin-dev \ libogre-1.9-dev libogre-1.9.0-dev \ libdart-dev libbullet-dev libsimbody-dev \ libqt5core5a libqt5gui5 libqt5widgets5 libqt5opengl5-dev \ libavformat-dev libavcodec-dev libavutil-dev libswscale-dev \ libopencv-dev几个必须单独强调的点libogre-1.9-dev和libogre-1.9.0-dev要一起装。focal 里同时有 OGRE 1.9 和 1.12Gazebo 11 的 CMake 脚本是按 1.9 写的探测逻辑会去找OGRE-1.9这个 pkg-config 名字。只装libogre-1.12-dev的话配置阶段就会报找不到 OGRE。而libogre-1.9.0-dev提供的是插件和资源文件的开发头两个包合起来才完整。libqt5opengl5-dev不是可选项。少了它Qt5 的Qt5::OpenGL组件找不到GUI 相关的gzclient目标会被跳过你编完发现没有gzclient可执行文件还得回头重编。libignition-plugin-dev的包名不带版本号容易和libignition-plugin1-dev混淆。focal 里正确的名字就是前者。OpenCV 是可选的。如果你的场景里不用摄像头仿真可以不装能省下 500 MB 磁盘和十几分钟配置时间。但要注意Gazebo 的 CMake 是自动探测的你不装它就不编视觉插件不会报错。提示装完跑一次dpkg -l | grep -E ignition|sdformat|ogre|dart把版本号记下来。后面排查问题的时候我到底装的哪个版本是最常问自己的问题。2.3 需要源码编译的依赖及其先后次序混合式路线下你不需要自编 ignition-math6 和 sdformat9直接用 apt 的就行。但如果你确实要走全源码这里有个必须讲清楚的机制问题。CMake 的find_package(sdformat9)靠三种途径定位CMAKE_PREFIX_PATH环境变量、sdformat9_DIR变量、或者系统默认搜索路径/usr、/usr/local。如果你把 ignition-math6 编到了/opt/ignsdformat9 也是编到/opt/ign那么编 Gazebo 的时候必须显式告诉它export CMAKE_PREFIX_PATH/opt/ign:$CMAKE_PREFIX_PATH否则 CMake 会先找到/usr下 apt 装的那份然后你的源码版就白编了运行的时候还会因为符号不一致出现各种诡异问题。全源码的编译顺序和命令骨架如下以/opt/ign为前缀PREFIX/opt/ign JOBS6 # 1. ignition-math6 git clone -b ign-math6 https://github.com/ignitionrobotics/ign-math.git mkdir -p ign-math/build cd ign-math/build cmake .. -DCMAKE_INSTALL_PREFIX$PREFIX -DCMAKE_BUILD_TYPERelease make -j$JOBS sudo make install cd ../.. # 2. sdformat9需要 ign-math6 已安装 git clone -b sdf9 https://github.com/gazebosim/sdformat.git mkdir -p sdformat/build cd sdformat/build cmake .. -DCMAKE_INSTALL_PREFIX$PREFIX -DCMAKE_BUILD_TYPERelease \ -DCMAKE_PREFIX_PATH$PREFIX make -j$JOBS sudo make install cd ../..中间那几个 ignition 库同理每个都要带-DCMAKE_PREFIX_PATH$PREFIX。这是一条硬性纪律漏一个就是半小时的排查。我个人建议除非你有非常明确的理由不要走全源码。混合式的可控程度已经足够应付 90% 的定制需求而全源码带来的 ABI 隐患、版本漂移、每次重装系统都要重来一遍的负担收益比很低。2.4 一个容易被忽略的前置动作清理环境变量污染这条我单独拎出来讲因为它坑过我两次。如果你机器上装了 Anaconda、Miniconda 或者某些 Python 环境管理器它们会往PATH里塞自己的bin目录里面有protoc、cmake、甚至gcc。后果是什么编译 Gazebo 的时候CMake 探测到的是 conda 的 protobuf比如 3.20而系统libprotobuf-dev是 3.11。头文件和库版本不匹配链接阶段直接一堆undefined reference to google::protobuf::internal::...。判断方法很简单which protoc protoc --version dpkg -l | grep libprotobuf-dev如果which protoc指向~/anaconda3/bin/protoc那就是污染了。编译时临时把 conda 从 PATH 里摘掉export PATH$(echo $PATH | tr : \n | grep -v -i conda | paste -sd:)或者更干脆在一个干净的 shell 里编。这个动作花 10 秒能省你两小时。3. 源码获取与CMake参数设计依赖齐了接下来是拉源码和配参数。这两个步骤决定了最终产物长什么样。3.1 分支与标签怎么选Gazebo 的仓库分支命名规则是gazebo大版本比如gazebo11。这个分支上的提交是持续更新的属于滚动稳定版。如果你要复现某篇论文或者某个 issue 里的行为直接 clone 分支是不够的——今天和明天的代码可能不一样。做可复现构建一定要 checkout 到具体的 tag。Gazebo 的 tag 格式是gazebo11_11.10.2这种。用git tag -l gazebo11*能看到全部挑最新的稳定版即可。git clone https://github.com/gazebosim/gazebo.git cd gazebo git tag -l gazebo11* | tail -20 git checkout gazebo11_11.10.2 # 按实际最新 tag 替换为什么不建议直接--depth 1浅克隆分支因为后面如果你要git bisect定位某个行为变化是从哪个提交引入的浅克隆会让你很痛苦。源码安装的场景里多拉几十 MB 历史是值得的。另外提醒一句不要用master分支。Gazebo 的 master 早已切到 Gazebo Classic 之后的架构依赖的是 ignition-gazebo 那一套新库和 focal 的 sdformat9 完全不兼容配置阶段就会炸。3.2 CMake 选项逐条解释在 build 目录里跑 cmake参数不是随便写的每一个都有对应的意图。我把自己常用的组合列出来逐条讲清楚mkdir build cd build cmake .. \ -DCMAKE_INSTALL_PREFIX/opt/gazebo11 \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_PREFIX_PATH/opt/ign \ -DENABLE_TESTS_COMPILATIONOFF \ -DBUILD_TESTINGOFF \ -DUSE_UPSTREAM_CFLAGSOFF-DCMAKE_INSTALL_PREFIX/opt/gazebo11是我强烈建议的。默认前缀是/usr/local装进去之后会和/usr下的 apt 版 gazebo11 形成两套并存的局面头文件、库文件、可执行文件都在不同的路径上很容易串。用一个独立的/opt/gazebo11路径清晰卸载的时候rm -rf一句话搞定不会误删系统文件。这是源码安装最重要的一个好习惯。-DCMAKE_BUILD_TYPERelease决定优化等级。Gazebo 是重计算的仿真平台Debug 版本跑起来慢到没法用物理步进能慢 5~10 倍。如果你需要调试 Gazebo 内部别用全局 Debug用RelWithDebInfo更合适——既有-O2的性能又保留符号表-DCMAKE_BUILD_TYPERelWithDebInfo代价是编译时间增加约 20%产物大一点但用 gdb 挂上去能看到行号值。-DENABLE_TESTS_COMPILATIONOFF和-DBUILD_TESTINGOFF是一对作用是跳过单元测试的编译。Gazebo 的测试代码量很大编它们要多花 15~20 分钟而且测试依赖 gtest有时候会引入额外的版本冲突。除非你要跑上游的测试套件否则关掉。-DUSE_UPSTREAM_CFLAGSOFF控制是否使用上游推荐的编译标志。Gazebo 的 CMake 里有一组-Wall -Wextra之类的严格警告配置开着会刷出大量警告信息把真正的错误淹掉。关掉之后输出清爽很多。配置完成后一定要扫一眼 CMake 的总结输出。Gazebo 的 CMake 会在最后打印一大段-- Enabled features:和-- Found xxx: version这里能看到它探测到的每个依赖的版本和路径。如果 OGRE 显示的是/usr/lib/x86_64-linux-gnu/OGRE-1.9说明找对了如果显示NOT FOUND别急着 make先把这个问题解决。3.3 并行度与内存的估算方法前面说过-j和内存的关系这里补充一个实操技巧。如果你不确定自己的机器扛不扛得住-j$(nproc)可以先跑一个小规模测试make -j4编 5 分钟然后另开一个终端free -g看剩余内存。如果可用内存掉到 2 GB 以下说明 4 线程就已经吃紧了别往上加。还有一个更稳的办法用一个脚本监控# 另开终端每 2 秒打印一次可用内存 while true; do free -m | awk NR2{print $7 MB free}; sleep 2; done我一般就是这么干的一边编一边盯。看到 free 掉到 500 MB 以下立刻CtrlC降并行度重来。比被 OOM 杀掉之后重新编要省时间——因为 OOM 之后 build 目录里的中间文件是完整的重新make会从断点继续但被 kill 的那个文件可能留下损坏的.o导致下次链接报错反而更麻烦。4. 完整编译安装过程实录这一章把我实际跑一遍的完整流程写下来包括终端输出里值得注意的信息。4.1 目录规划与源码拉取我习惯把所有源码放在~/src下build 目录和源码分离out-of-source build这样源码树保持干净随时可以git status看有没有被污染也方便同一份源码用不同参数编多个版本。mkdir -p ~/src cd ~/src git clone https://github.com/gazebosim/gazebo.git gazebo11-src cd gazebo11-src git checkout gazebo11_11.10.2 git log -1 --oneline最后那条git log用来确认当前 HEAD 的提交哈希记下来写进你的构建记录里。将来出问题追溯的时候这个哈希比版本号还管用。4.2 配置阶段mkdir -p ~/src/gazebo11-build cd ~/src/gazebo11-build cmake ~/src/gazebo11-src \ -DCMAKE_INSTALL_PREFIX/opt/gazebo11 \ -DCMAKE_BUILD_TYPERelease \ -DENABLE_TESTS_COMPILATIONOFF \ -DBUILD_TESTINGOFF \ -DUSE_UPSTREAM_CFLAGSOFF \ 21 | tee cmake-configure.logtee这个动作很重要。CMake 的配置输出有几百行滚屏之后你想回看某个依赖的探测结果就没了。存成日志grep -i not found cmake-configure.log一秒钟定位问题。配置成功时输出末尾会有一段类似这样的总结版本号会因环境不同而有差异-- BUILD TYPE: Release -- Build Type: Release -- Found Boost: /usr/include (found suitable version 1.71.0) -- Found ignition-math6: /usr/lib/x86_64-linux-gnu/cmake/ignition-math6 -- Found sdformat9: /usr/lib/x86_64-linux-gnu/cmake/sdformat9 -- Found OGRE: /usr/lib/x86_64-linux-gnu/OGRE-1.9 -- Found DART: /usr/lib/x86_64-linux-gnu/cmake/dart -- Found Qt5: /usr/lib/x86_64-linux-gnu/cmake/Qt5 -- Configuring done -- Generating done重点看三处sdformat9的路径必须是/usr下的除非你走全源码、OGRE必须带1.9、DART必须找到。这三处任何一个不对后面的编译都不会顺利。如果看到Could NOT find sdformat9八成是libsdformat9-dev没装或者你装了但 CMake 缓存里存着上次的失败结果。解决办法是删掉 build 目录重建别试图在缓存上打补丁CMakeCache.txt会记住旧的NOTFOUND。4.3 编译与安装cd ~/src/gazebo11-build make -j6 21 | tee build.log-j6是我在一台 16 GB 内存 8 核机器上的选择。实际耗时约 45 分钟含一次中途降并行重来的时间。纯编译时间在 8 核 16 GB 上大概是 35 分钟4 核 8 GB 上要 70 分钟以上。编译过程中值得关注的是进度百分比。Gazebo 的 Makefile 会打印[ 45%] Building CXX object ...如果你发现进度卡在某个百分比超过 10 分钟不动八成是那个文件太大正在硬啃比如physics/src/ode相关的正常。但如果卡住超过 30 分钟且 CPU 占用不高可能是链接阶段在等 IO或者有进程死锁这时候top看一眼就知道了。编译完成后sudo make install安装到/opt/gazebo11大概 800 MB。装完检查一下目录结构ls /opt/gazebo11/bin | head # gz、gzclient、gzserver、gz topic 等 ls /opt/gazebo11/share/gazebo/setup.shsetup.sh是关键文件它里面设置了GAZEBO_RESOURCE_PATH、GAZEBO_PLUGIN_PATH、GAZEBO_MODEL_PATH、LD_LIBRARY_PATH这一堆环境变量。这个文件是源码安装能不能跑起来的分水岭很多人编完了发现gzserver起不来就是因为没 source 它。4.4 环境变量与验证source /opt/gazebo11/share/gazebo/setup.sh echo $GAZEBO_RESOURCE_PATH which gzserver gzserver --version--version应该输出你编的这个版本号比如11.10.2。如果输出的是 apt 版的版本号说明PATH里/usr/bin排在了/opt/gazebo11/bin前面setup.sh没生效或者被后面的脚本覆盖了。做一个真实的启动验证别只看版本号# 起一个空世界跑 5 秒看看 gzserver --verbose -s libgazebo_ros_init.so 2/dev/null || gzserver --verbose sleep 5 gz topic -l | head kill %1如果gz topic -l能列出/clock、/gazebo/default/...这些话题说明核心链路是通的。这一步比--version有说服力得多。再验证 GUIgzclient看到三维场景和左侧的模型树就成功了。如果报[Err] [RenderEngine.cc:xxx] Cannot initialize render engine说明 OpenGL 环境有问题在无显示器的服务器上这是正常现象跳过 GUI 验证即可或者用xvfb-run gzclient套一层虚拟显示。实操心得把这个 source 动作写进~/.bashrc里要谨慎。一旦写死你每次开终端都会加载源码版的 Gazebo 环境变量和 ROS 的setup.bash顺序一乱就会互相覆盖。我的做法是写成一个独立的脚本~/use-gazebo11-src.sh需要的时候手动 source不用的时候环境是干净的。5. 报错排查实录从CMake到运行时下面这些错误按配置阶段 → 编译链接阶段 → 运行时的顺序排都是我实际遇到过的附上判断依据和处理办法。5.1 配置阶段报错错误一Could NOT find ignition-math6 (missing: IGNITION-MATH6_INCLUDE_DIR)八成是libignition-math6-dev没装。但还有一种隐蔽情况你装了同时又在/opt/ign下编过一份CMAKE_PREFIX_PATH指向了后者而后者只装了库没装 cmake config 文件。解决办法是unset CMAKE_PREFIX_PATH重跑或者显式指定-Dignition-math6_DIR/usr/lib/x86_64-linux-gnu/cmake/ignition-math6。错误二Could NOT find OGREfocal 上 90% 是包没装全。检查dpkg -l | grep ogre必须同时有libogre-1.9-dev和libogre-1.9.0-dev。另外libogre-1.9-dev依赖libogre-1.9.0运行时库如果系统里被误删了也找不到。错误三By not providing FindQt5.cmake ... Qt5 not found装libqt5opengl5-dev。注意libqt5core5a、libqt5gui5、libqt5widgets5这三个是运行时库不带头文件光装它们没用必须装-dev结尾的。错误四CMake 版本相关的Policy CMP0074 is not set警告刷屏这是 CMake 3.12 引入的Package_ROOT变量策略。警告不影响构建但会淹没真正的错误。在 CMakeLists 里加一行cmake_policy(SET CMP0074 NEW)可以消掉但改上游文件不划算。直接用-Wno-dev关掉开发者警告cmake .. -Wno-dev ...5.2 编译链接阶段报错错误五internal compiler error: Killed (program cc1plus)这就是前面说的 OOM。不是代码问题是内存不够。降到-j2或者-j1单独编那个文件然后回到-j6继续。找出是哪个文件太能吃内存可以看 build.log 里最后一行Building CXX object的路径。错误六undefined reference to google::protobuf::internal::...protobuf 版本混用。which protoc和dpkg -l | grep libprotobuf-dev对一遍不一致就说明头文件来自 conda 或/usr/local和链接库来自 apt不是一套。清 PATH 重编。错误七fatal error: ignition/math6/ignition/math/Vector3.hh: No such fileignition-math6 的头文件路径没被识别。focal 的libignition-math6-dev把头文件装在/usr/include/ignition/math6/CMake 应该自动带上-I/usr/include/ignition/math6。如果报这个错检查一下 CMake 日志里ignition-math6的INCLUDE_DIR是不是空的——空的话说明探测到了 CMake config 但变量没填通常是包损坏重装一遍libignition-math6-dev。错误八链接时报cannot find -lBulletSoftBodylibbullet-dev装了但libbullet-extras-dev没装。focal 里这两个是分开的Gazebo 需要 extras 里的 SoftBody 组件。sudo apt install -y libbullet-extras-dev5.3 运行阶段报错错误九gzserver: symbol lookup error: /opt/gazebo11/lib/libgazebo.so.11: undefined symbol: ...这是最典型的两套库串了。你的可执行文件想用/opt/gazebo11的库但LD_LIBRARY_PATH里混进了/usr/lib/x86_64-linux-gnu/libgazebo.so.11apt 版符号对不上就崩。排查方法ldd /opt/gazebo11/bin/gzserver | grep gazebo输出里如果出现/usr/lib/x86_64-linux-gnu/libgazebo.so.11说明串了。解决办法是保证setup.sh里的LD_LIBRARY_PATH排在最前面并且不要在同一个 shell 里同时 source 两套环境。错误十[Err] [RenderEngine.cc:xxx] Cannot initialize render engine无显示环境下的正常现象。服务器上没有 X11OGRE 初始化失败。要跑无头仿真就用gzserver别用gzclient。如果非要跑 GUI 做截图用xvfb-run -s -screen 0 1280x1024x24 gzclient。错误十一模型加载失败[Err] [SystemPaths.cc:xxx] Unable to find model[...]GAZEBO_MODEL_PATH没设对。源码安装后内置模型在/opt/gazebo11/share/gazebo-11/modelssetup.sh会自动加上。但如果你自己有一批自定义模型需要手动追加export GAZEBO_MODEL_PATH$GAZEBO_MODEL_PATH:/home/user/my_models注意setup.sh里用的是GAZEBO_MODEL_PATH不要写成GAZEBO_MODEL_PATHS有些旧文档写错了。5.4 常见问题速查表现象大概率原因快速处理CMake 找不到 sdformat9libsdformat9-dev未装apt 装上删 build 重建CMake 找到的 sdformat 版本是 6环境里残留/opt前缀unset CMAKE_PREFIX_PATH编译被 Killed内存不足降-j或加 swapprotobuf 链接错误conda / 自编版本干扰清理 PATH只用 apt 版找不到 OGRE只装了 1.12装libogre-1.9-devlibogre-1.9.0-devgzclient 编不出来缺libqt5opengl5-dev装上后重跑 cmake运行时报 symbol lookup error两套 libgazebo 混用用ldd确认清理LD_LIBRARY_PATHGUI 起不来无 X11用gzserver或xvfb-run模型找不到GAZEBO_MODEL_PATH未设sourcesetup.sh后追加路径插件加载失败GAZEBO_PLUGIN_PATH指向旧版检查是否指向/opt/gazebo11/lib6. 与 ROS 共存、多版本切换与清理源码装完之后真正麻烦的事才开始怎么让它和已有的 ROS 环境和平共处。6.1 与 apt 版 Gazebo 的隔离策略ROS Noetic 安装时会带上gazebo11的 apt 包ros-noetic-gazebo-ros-pkgs编译时链接的是那份。现在你有了一份/opt/gazebo11的源码版两套并存冲突点有三处第一处是路径优先级。/usr/bin/gzserver和/opt/gazebo11/bin/gzserver同时存在谁在前面谁生效。setup.sh会把/opt/gazebo11/bin插到PATH最前面但你source /opt/ros/noetic/setup.bash之后ROS 又可能把它自己的一些路径插进来顺序就不确定了。第二处是动态库。ROS 的 gazebo 插件比如libgazebo_ros_api_plugin.so是针对 apt 版 Gazebo 编译的链接的是/usr/lib/x86_64-linux-gnu/libgazebo.so.11。你在源码版 Gazebo 里加载它如果两边的 ABI 有细微差别哪怕版本号一样编译选项不同也会导致就会出现加载失败或者运行时崩溃。第三处是消息定义。gazebo_msgs里的消息类型在两个版本之间如果有差异序列化会对不上。我的处理策略是物理隔离按需切换把源码版的环境变量写成一个独立脚本只在需要调 Gazebo 内部逻辑的时候 source 它且此时不 source ROS 的 gazebo 相关环境做 ROS 上层开发的时候用 apt 版不去碰/opt/gazebo11。# ~/use-gazebo11-src.sh export GAZEBO_HOME/opt/gazebo11 export PATH$GAZEBO_HOME/bin:$PATH export LD_LIBRARY_PATH$GAZEBO_HOME/lib:$LD_LIBRARY_PATH export GAZEBO_RESOURCE_PATH$GAZEBO_HOME/share/gazebo-11:$GAZEBO_RESOURCE_PATH export GAZEBO_PLUGIN_PATH$GAZEBO_HOME/lib:$GAZEBO_PLUGIN_PATH export GAZEBO_MODEL_PATH$GAZEBO_HOME/share/gazebo-11/models:$GAZEBO_MODEL_PATH使用时source ~/use-gazebo11-src.sh用完了exec bash开个新 shell 回到干净环境。这个习惯能避免 90% 的明明昨天还好好的今天怎么崩了。6.2 多版本切换脚本如果你不止一份源码版比如同时维护 Gazebo 9 和 Gazebo 11 的定制分支可以做一个更通用的切换函数塞进~/.bashrcgzs() { local target$1 case $target in sys) unset GAZEBO_HOME ;; src11) export GAZEBO_HOME/opt/gazebo11 ;; src9) export GAZEBO_HOME/opt/gazebo9 ;; *) echo usage: gzs {sys|src9|src11}; return 1 ;; esac # 先清掉旧的 export PATH$(echo $PATH | tr : \n | grep -v ^/opt/gazebo | paste -sd:) export LD_LIBRARY_PATH$(echo $LD_LIBRARY_PATH | tr : \n | grep -v ^/opt/gazebo | paste -sd:) if [ -n $GAZEBO_HOME ]; then export PATH$GAZEBO_HOME/bin:$PATH export LD_LIBRARY_PATH$GAZEBO_HOME/lib:$LD_LIBRARY_PATH fi echo Gazebo - ${GAZEBO_HOME:-system} which gzserver }关键是先清理再设置。如果只加不删来回切换几次之后PATH会变成一长串重复路径排查的时候看瞎眼。这个函数里用grep -v把所有/opt/gazebo*前缀的条目筛掉保证每次都是干净的一层。6.3 卸载与残留清理源码安装最大的好处就是卸载干净。因为所有文件都在/opt/gazebo11下面一句sudo rm -rf /opt/gazebo11就够了。但有几个残留点要顺手清掉一是源码和 build 目录。~/src/gazebo11-src和~/src/gazebo11-build加起来四五个 GB别忘了删。二是~/.gazebo。这是 Gazebo 首次运行时自动创建的存着模型缓存、日志、配置。源码版和 apt 版共用同一个~/.gazebo如果你在里面改过什么配置删之前确认一下。du -sh ~/.gazebo ls ~/.gazebo # 常见的models/ gui/ server.config gazebo.log三是~/.bashrc里手动加的 export。用grep -n gazebo ~/.bashrc检查一遍避免留下指向不存在路径的环境变量那种情况下每条命令都会刷一行警告。四是如果你装过.desktop快捷方式在/usr/share/applications/顺手删掉否则应用菜单里会留一个点不开的图标。常见问题rm -rf /opt/gazebo11之后gzserver还能跑而且版本号还是源码版的那个。原因是 shell 里PATH和LD_LIBRARY_PATH还指着旧路径进程映射的内存页还在。exec bash或者重开终端就正常了这是缓存效应不是真的没删掉。清理完之后如果你还想确认没有残留的符号链接或配置文件可以用locate扫一遍前提是updatedb跑过sudo updatedb locate /opt/gazebo11 | head输出为空就干净了。整个过程下来从源码编译安装到彻底卸载一个/opt下的独立前缀让这件事变得可控——这也是我一开始坚持不用/usr/local的理由图一时省事后面清理的时候要多花好几倍时间。