我曾自诩"代码永动机",直到凌晨三点,在Anaconda温床里搭建的ROS工作空间用一连串刺眼的红色报警把我钉回原型。那一刻,我盯着终端满屏的缺失路径和python冲突堆栈,嘴里骂出整个互联网最狠的脏话——"你TM到底在加载谁的Python?"
这个项目,就是我与"Anaconda环境下catkin_make编译报错"的死磕全记录。核心痛点一句话:装了Anaconda后,ROS的catkin_make像失忆的醉汉,自动抱错了Python大腿,顺带还弄丢了empy模板引擎,导致actionlib、dynamic_reconfigure、pluginlib这些核心库全部编译失败。整个调试过程,我一次性踏平了Python路径冲突、empy模块缺失、CMakeCache残留三大天坑,最终干净利落地让工作空间重新"清爽编译"。
这篇文章不写花把势,只记录我在终端里真刀真枪的排查步骤、每一个让我血压飙升的报错、以及最终封神般的修复方案。如果你也被"anaconda环境下编译"折磨得怀疑人生,这份笔记能让你少走至少一宿弯路,直接从"WTF"状态回归"真香"。
1. 问题复盘:Anaconda祸根深种与"整条编译链"的血泪现场
Anaconda这东西,本身确实好用。虚拟环境隔离、IDE集成、一键部署Python生态,简直是把"环境管理"这四个字按在地上摩擦。但问题在于:它太霸道了。一旦装好,它便会永久篡改一切终端环境——不管你是用conda activate切了哪个虚拟环境,还是老老实实呆在系统默认的bash里,只要你的shell配置(.bashrc或.zshrc)里被写入了Anaconda的初始化代码,它就像一位强势的总经理,把系统中的一切python、pip命令强制指向自己地盘下的链接。
我当时就在默认的base环境下,直接跑ROS工作空间的编译命令,这就埋下了最深的雷。
1.1 编译时的诡异报错与"物理层面"的崩溃
你想象一下这个场景:我用catkin_make编译一个包含actionlib、geometry_msgs等常用包的机器人控制工作空间,IDE终端里跑着熟悉的编译指令,等着"Quick summary"出现。
结果终端上跳出来的是下面这几类致命伤:
-- ~~ ~~ -- ~~ ~~ -- ~~ ~~ -- ~~ ~~ -- ~~ ~~ -- -- Running ament_python_install_package() function -- Could NOT find PY_empy (missing: PY_EMPY) CMake Error at /opt/ros/melodic/share/genmsg/cmake/genmsg-extras.cmake:91 (message): generate_messages() must be executed in the 'messages' package还有运行rosdep或者python检查时,永远指向一个莫名的conda路径:
$ which python /home/yourname/anaconda3/bin/python当你手动用pip install empy,又发现装是装进去了,但catkin_make依然是:Could NOT find PY_empy。
最让人崩溃的是,这种状态下你连重装ROS包都没用。因为根本症结在于:整个CMake构建流程中的Python解释器指向了conda的Python,而conda的Python拥有的库目录结构和系统的/usr/lib不同,甚至系统里原本为ROS准备的二进制依赖在conda环境下根本找不到对应的.so动态库,所以错误链如多米诺骨牌般坍塌。
1.2 为什么Anaconda会和ROS天然"犯冲"
这里必须把我熬夜排查出来的底层逻辑说透。ROS (Robot Operating System)的catkin_make编译系统,底层是走CMake——它启动时会去系统的标准路径(/usr/bin/python)下寻找Python解释器,并获取该解释器的相关参数,进而绑定Python的库、头文件和相关的第三方模块(比如empy)。
而Anaconda一装,它干了一个极其"自作主张"的操作:往用户目录的.bashrc里注入以下一行。这一步,就是地狱之门开启的钥匙:
export PATH="/home/yourname/anaconda3/bin:$PATH"它的逻辑很简单:我想优先接管你终端里的所有命令。没错,它成功了。只要新开任何终端,PATH第一顺位就是anaconda3/bin,此时系统里原本的python3(比如/usr/bin/python3.6)被直接遮蔽。
然后,当你在这个"被污染"的终端下执行catkin_make时,CMake清晰地看到PYTHON_EXECUTABLE=/home/yourname/anaconda3/bin/python,于是它自然去conda的site-packages里寻找empy等模板模块。
但ROS的官方版本(比如Melodic)只在自己的/opt/ros树里和底层依赖中评估过与系统Python的兼容性——它需要的empy版本、PyYAML版本等和conda默认环境的版本存在碰撞风险,更别提很多conda环境干脆没装empy。于是,冲突、缺失、找不到,全部集中爆发。
注意:如果你说"那我conda install empy不就有了?"我试过,一装,又把系统里其他编译依赖关系给污染了,相当于按下葫芦浮起瓢,最后系统Python库文件都被搞得一团糟。
2. 拆掉雷区:Anaconda环境变量、依赖断链与CMake缓存清理
既然知道是环境变量和Python路径搞的鬼,那我们就反向拆解,从源头切断污染链。整个过程耗时1小时52分钟,每一步都凝结了踩碎地板的怒意。
2.1 制定"接线方案":检查清单、工具与预期节点
我当时的思路是:不用禁用Anaconda,因为本机还有其他项目的Python依赖靠它活着。我只需要让catkin_make在编译ROS项目时,能精准找到系统自带的Python,而不是被Anaconda半路劫持。
| 接线工程项 | 检查内容 | 预期处理节点 | 涉及工具/方法 |
|---|---|---|---|
| 环境变量盘点 | .bashrc中的PATH顺序,conda初始化位置 | 注释或隔离conda的export行 | sed、vim |
| Python解释器核验 | which python、python -V、/usr/bin/python -V | 确认系统Python可独立运行 | ls、which |
| 依赖模块摸底 | python -c "import empy"、pip3 list、conda list | 摸清empy在哪个环境 | pip3、conda list |
| CMake残留清理 | 删除build/和devel/目录 | 让整个工作空间从零感知新环境 | rm -rf |
| catkin_make的调用 | 确保编译时的Python为/usr/bin/python | 编译通过,所有包生成成功 | source、catkin_make |
检查清单写得再冷静,操作起来依然是火葬场。以下是我的实际执行记录。
2.2 第一步:彻底挪走Anaconda这尊"被供错位置的佛"
在明白了氧化剂和可燃物的关系后,我直接操刀修改bashrc,但这里有个细腻的坑——当时我们网上查了很多教程,都说把Anaconda的PATH注释掉即可,操作时发现没那么简单。我打开~/.bashrc,末尾触目惊心的一堆conda初始化代码块:
# >>> conda initialize >>> # !! Contents within this block are managed by 'conda init' !! __conda_setup="$('/home/yourname/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" if [ $? -eq 0 ]; then eval "$__conda_setup" else if [ -f "/home/yourname/anaconda3/etc/profile.d/conda.sh" ]; then . "/home/yourname/anaconda3/etc/profile.d/conda.sh" else export PATH="/home/yourname/anaconda3/bin:$PATH" fi fi unset __conda_setup # <<< conda initialize <<<我采用一个看似"多此一举"却极其有效的策略:将这段代码块用大括号括起来,并在块前加一个if [ -z "$ROS_IS_BUILDING" ]的哨兵条件,同时在运行catkin_make时临时设置环境变量ROS_IS_BUILDING=1,以彻底冻结conda环境的干扰。
但为了最快速度解决眼前编译红灯,我采用了更直接的手段:先备份bashrc,然后把整个conda初始化块整体注释掉(这里如果是生产环境,不建议修改全局bashrc,建议临时子shell编译,后文有讲)。
cp ~/.bashrc ~/.bashrc.bak.anaconda # 使用文本编辑器把 # >>> conda initialize >>> 到 <<< conda initialize <<< 全部注释掉同时注意,PATH可能还有残留线索,检查并清除bashrc或profile里所有手动添加的/home/yourname/anaconda3/bin导出语句。新开终端后,which python就应该指向/usr/bin/python。
2.3 第二步:让变量与环境"断干净"——删CMakeCache和build目录
很多用户踩过这个隐蔽的坑:即便你恢复并修正了bashrc环境变量,到了编译环节依旧报错,且错误指向的还是conda路径。
这是CMake缓存机制在作怪。catkin_make第一次编译时,会在build目录下的CMakeCache.txt中永久记录你当时的python路径、库路径等信息。就算你之后切换了环境,CMake依然会固执地读取缓存的旧值。
注意:这里不是要砸电脑,这个手段分毫不差:强制清空构建足迹,让重建逻辑从零开始跑一遍。如果你不解甲归田,整个工程就是困兽犹斗。
执行黑魔法指令,命令如下(我反复敲了三遍以确认没有误删代码目录):
cd ~/catkin_ws rm -rf build/ devel/这几个目录删了不可惜,它只包含编译产物。放心,你的src目录原封不动。操作后,CMake记录文件、生成的lib文件、环境变量setup文件被核平,等于把编译场炸平,铺上铁轨重新出发。
3. 核心枢纽:empy模块定位、安装验证与编译高光时刻
环境是与非、路径正与邪,在这里结束混战。但还有一关没过:empy到底去哪找,以及如何确保catkin_make正眼相待。
3.1 追查empy模块:它在系统Python的胃里,还是在conda的冰箱里?
empy(全称EmPy)是一个纯Python实现的模板引擎。ROS的message_generation机制用它来为.msg、.srv文件生成对应的Python/ C++代码。这是个底层依赖,没有它get到了也是白搭。
排查逻辑如下:既然我们决定让catkin_make使用系统Python,就必须确认系统Python能import到empy。
- 进入纯净终端(重启后不source conda环境)
- 运行系统Python的版本验证和模块探测:
# 1. 验证系统Python可达 /usr/bin/python --version # 输出: Python 2.7.17(这是ROS Melodic默认绑定的版本,所以系统py2必须健康) # 2. 直接探测empy /usr/bin/python -c "import empy; print(empy.__file__)" # 这里我遇到了第一个次生灾害:报错 `ImportError: No module named empy`这问题的性质是:ROS的Python2系统库是纯净的,但从未安装过empy。Anaconda那边,conda base环境可能有empy,但绑定的是python3.x,与我们需要的系统python2解释器又是鸡同鸭讲。
所以正确操作是:为系统自带的python2解释器安装empy(如果你使用的是Noetic或更新的版本,则对应系统python3)。这里我不建议使用pip,因为系统环境更复杂,直接用apt包管理器最省心:
# 对于 Ubuntu 18.04 / ROS Melodic (Python 2.7) sudo apt update sudo apt install python-empy其实如果ROS安装正确,这一步通常是自动依赖安装的,但你的环境被Anaconda折腾过,部分系统依赖可能被干掉了,重装它即可。如果你用的Noetic(系统python3),则是sudo apt install python3-empy。
这里是整个修复过程最关键的"验尸"环节,我强烈建议新开一个干净的终端验证三要素:
# 1. 确认终端不再指向conda head -n 1 $(which python) # 2. 确认系统Python可导入empy /usr/bin/python -c "import empy; print('Empy loaded:', empy.__file__)" # 3. 确认Ros相关Python能识别 /usr/bin/python -m catkin_pkg --version输出只要没有红色的Traceback,就说明天晴了。
3.2 构建执行:亲测可用的终极编译配置与启动命令
环境洁净度60%,但提到完整回归"清爽编译",还差最后一个关键的临门一脚——操作细节。以下是一套我个人专用于工程现场的高阶combo命令,请按序复制贴入一个新建的纯净终端,干净利落地完成使命:
#!/bin/bash # 强制指明PYTHON解释器版本,防止CMake胡猜 export PYTHON_EXECUTABLE=/usr/bin/python # 若你使用的是ROS Noetic,请注释上面,改执行: # export PYTHON_EXECUTABLE=/usr/bin/python3 # 强制指定Python相关依赖库路径(这步能有效防止未来conda阴魂不散) export PYTHON_INCLUDE_DIR=/usr/include/python2.7 export PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython2.7.so # 如果你是python3,这里是 /usr/include/python3.6m 和 libpython3.6m.so # 关键一步:清除可能残留的conda导出 unset PKG_CONFIG_PATH export PKG_CONFIG_PATH=/opt/ros/melodic/lib/pkgconfig # 编译启动 cd ~/catkin_ws source /opt/ros/melodic/setup.bash catkin_make这里没有玄学,每一步都是物理级精准定位。举个反例:若你不设置PYTHON_EXECUTABLE,即便前面环境干净,CMake的FindPythonInterp模块依然可能通过find_program自动find到一个诡异位置的python,比如之前被Anaconda在pycharm中设置的环境变量污染。我们直接硬编码路径,阻断它的一切不切实际的幻想。
编译开始后,我看到终端飞出一串串黄色、绿色的编译信息。从[ 57%] Building CXX object到[100%] Linking CXX executable,全程没有一条红色error出现。那一刻,我感觉自己不仅修复了一个环境,而是顺手治理了一场软件界的工厂污水,彻彻底底。
编译器最后抛出的不是Error,而是优雅的一行:
[100%] Built target my_sensor_driver与此同时,devel/setup.bash重新生成。那是把断了的路重新焊上钢轨的声音。
3.3 编译成功后的"验尸报告":快速验证环境是否已被彻底驯服
编译通过了,但还不能完全撒花。我的习惯是推演各种死角,做一个快速而残酷的复验。
注销并重新登录系统,或新开所有终端,然后把之前的检查命令重新执行一遍。必须保证没有source Anaconda的环境里,catkin_make依旧可以立等可取地编译。
我给出的最终验证三步法,可解决99%的"编译时好时坏"疑难杂症:
- 无痕构建测试:反复删除build/devel目录至少2次,并重新catkin_make,确认每次都能稳定通过(排除缓存侥幸通过的可能性)。
- 调用链确认。在src下新增一个简单的自定义msg包并重新编译,确保message_generation后端真正工作,empy顺利生成Python模块代码,自定义msg能被
rosmsg show正常读取。 - 跨环境兼容测试。保持当前纯净shell环境开启(不激活conda),尝试在终端运行
python -c "import rospy"(会将devel环境的依赖链一并激活,并通过)。
注意:这只是解决宿主机多环境矛盾的强心针,并不是让Anaconda与ROS从此完全免冲突。后续每次在conda虚拟环境跑数据训练代码前,或想在新终端编译工程,一定要看清你的PATH是否指向了conda,别等到编译爆炸再拍大腿。
4. 避坑手册:从根源乱麻到高效切换双环境的必杀技
至此,核心问题已解决。但为了让大家不再陷入这趟浑水,我觉得有必要分享几招我踩坑后总结的环境管理经验,能让你从"临时救火"进化成"预防火灾"。
4.1 为Anaconda与ROS打造"双轨制"的专属编译器调用
这里最值得投入的,是给不同工作区配置不同的终端启动脚本。我强烈建议不要在bashrc里一刀切。将Anaconda的初始化和ROS的环境初始化分离到不同的别名/脚本中。
比如,我在~/.bashrc里写了一个函数:
# 在bashrc中添加别名,实现一键切换 alias ros_env='export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH; unset CONDA_PREFIX; source /opt/ros/melodic/setup.bash' alias conda_env='source ~/anaconda3/etc/profile.d/conda.sh && conda activate base'ros_env带来的效果就是面部识别般精准地切换到"超级工程模式",而conda_env则是照顾纯AI优先模式。两者都互相屏蔽,杜绝环境污染。你只需要记得,在编译ROS前先敲下ros_env,在跑Python脚本前敲下conda_env。这套方案运行一个月后,环境问题基本归零。
提示:在某些"一键部署"的环境里,甚至可以通过
conda config --set auto_activate_base false关闭默认激活base环境,给PATH一个缓冲带。但记得,这不是根治本问题的核心手段,只是辅助。
4.2 那些年我在Anaconda环境里犯过的"隐性错误"
排查环境问题的过程是枯燥而痛苦的,但如果你能从下面几个别人踩坑的案例中获得警示,那这波学得硬核又有价值:
- 隐性之坑1:用conda安装了ROS环境依赖。比如觉得系统缺少某个图像库,猛地来一句
conda install opencv。好嘛,瞬间conda把一大堆依赖全拖进来,连系统底层的libpng都被抢占,下次编译保准挂。在编译ROS时,优先使用apt,屏蔽conda install的冲动。 - 隐性之坑2:清空pycache或者误删了系统python软链接。Anaconda自带的python有时候会和系统python打架,某些偏方建议删掉
/usr/bin/python的符号链接。这是最蠢的挂法!我亲眼见到有人这样操作后,Ubuntu桌面崩溃,直接黑屏。要动软链接前,请反复确认。 - 隐性之坑3:在conda虚拟环境内直接run
rqt、rviz等图形工具。这类工具编译基于系统资源,在conda的虚拟环境下运行时,常遇到"无法加载插件"或者"段错误",输出全是堆栈错误日志。别问我怎么知道的,说多了都是战损记录。
4.3 定时清理缓存,不给CMake任何"记仇"的机会
CMakeCache是编译者和构建工具之间的"记忆卡",有时记忆卡是福,有时则是毒。为了编译环境绝对干净,我推荐以下定期维护习惯(一个月搞一次,神清气爽):
# 在编译前,一键清理无用缓存并深度清除Anaconda的环境污染 find ~/.cache/pip -type f -delete 2>/dev/null rm -rf ~/catkin_ws/build ~/catkin_ws/devel真的,每次重新从零构建带来的安全感,远大于"上次编过、本次跳过"的侥幸感。既然你的宿主机装了两个"操作系统级"的开发环境,那就务必学会物理隔离与缓存爆破。
5. 对决终局:我的经验复盘与工程师的最后倔强
回顾这场和Anaconda、catkin_make、empy三方混战的战役。最初,我以为问题只会停留在简单的"模块缺失",结果升级成"环境主权争夺",最后演变为"系统底层生态刷新"。过程中,我看清了环境管理工具的本质——它们都是霸道的生态链顶端,谁在你的PATH最前,谁就掌握了系统解释权的生杀大权。
这套折腾下来,我不仅恢复了编译,还得到了一套独有的环境管理哲学:不要和环境变量打架,学会设置堡垒和安全闸门。如今,我在一台电脑上甚至可以同时维护ROS工程和深度学习的conda环境,来回切换如履平地,再也不会出现那种"新增一个文件,构建全线崩溃"的夜半惊魂。
最后分享一条压箱底的经验:如果你已经删除了所有conda相关PATH,但which python依然指向anaconda,请不要怀疑人生,去检查~/.profile文件,以及~/.bash_profile,或者是终端模拟器自带的"Command"预设,里面有时会写死python路径。上面几处往往是所有教程的视觉盲区,却恰恰能让你一瞬间回到解放前。
这也是为什么我强烈建议你直接在bashrc里新增ros_env()切换函数,而不是手动逐步导出。用工程方法替代直觉操作,稳定,且能救命。
编译工具链的坑,踩一个少一个。希望你读到这里时,已经学会跟Anaconda和ROS和平共处,或者正带着刚刚成功的编译返回路上,闻着devel目录里散发的"烧焦但最终成功"的机油味,心满意足地喝下第一口深夜鸡汤。祝好运,编译永不Error。