
ROS 和 Python 的版本纠葛几乎每个做机器人开发的人都经历过。你系统里跑着 ROS Noetic它绑死了 Python 3.8你新开一个感知模块想用最新的 PyTorch它要求 Python 3.10 起步你再去装个 OpenCV 的 contrib 版本pip 一执行把 ROS 依赖的 numpy 顺手升了个大版本回头rosrun直接报ImportError。这种连锁反应不是偶然是 ROS 的 Python 依赖和系统 Python 环境深度耦合的必然结果。Anaconda 虚拟环境就是用来切断这种耦合的——它让你在同一个 Ubuntu 系统上为 ROS 保留一套干净的、不被污染的 Python 环境同时为你的算法开发、深度学习、数据处理另开几套互不干扰的沙箱。这篇内容面向的是已经在用 ROS、被 Python 包冲突折磨过、或者正准备搭建机器人开发环境的工程师我会把从 Anaconda 安装、虚拟环境创建、ROS 环境隔离、到 IDE 配置的完整链路拆开讲清楚每一步都说明为什么这么做以及我实际踩过的坑。1. 先搞清楚 ROS 和 Anaconda 为什么会打架1.1 ROS 的 Python 依赖是系统级绑定ROS Noetic 在 Ubuntu 20.04 上的 Python 依赖是通过apt安装的落在/usr/lib/python3/dist-packages和/opt/ros/noetic/lib/python3/dist-packages这两个路径下。系统自带的 Python 3.8 在启动时会自动把这些路径加入sys.path所以rospy、rosbag、tf这些包才能被直接 import。问题在于Anaconda 安装后会修改你的 shell 启动脚本把conda的 base 环境默认激活。base 环境里的 Python 是 Anaconda 自带的版本通常是 3.11 或更高它的sys.path完全不包含 ROS 的那些 dist-packages 目录。结果就是你在终端里敲python进的是 conda 的 Pythonimport rospy直接失败。更隐蔽的情况是你pip install某个包时pip 指向的是 conda 环境的 site-packages装完之后 ROS 的python3却找不到它。这里有一个很多人忽略的细节ROS 的python3命令和 conda 的python命令在 shell 里可能是两个完全不同的解释器。你可以用which python3和which python分别确认。如果python3指向/usr/bin/python3而python指向~/anaconda3/bin/python那你的 ROS 节点跑起来用的是系统 Python但你在终端里手动测试代码用的是 conda Python两边环境不一致调试时会出现终端里能跑rosrun 就报错的诡异现象。1.2 conda 的 base 环境自动激活是冲突的导火索Anaconda 安装时默认会在~/.bashrc末尾追加一段初始化脚本大意是把~/anaconda3/bin加到PATH最前面并激活 base 环境。这意味着你每开一个新终端PATH里的python、pip、jupyter全部被 conda 接管。对于纯 Python 开发者这很方便。对于 ROS 开发者这是灾难的开始。因为 ROS 的很多工具链脚本比如roslaunch、catkin_make内部会调用python3如果PATH被 conda 改写这些脚本可能调用到 conda 的 Python进而找不到 ROS 的包。我见过最典型的场景是装完 Anaconda 后catkin_make编译正常但rosrun启动节点时报ModuleNotFoundError: No module named rospkg。原因就是rosrun脚本的 shebang 指向了 conda 的 Python而 conda 环境里没有装rospkg。1.3 正确的隔离思路让 ROS 用系统 Python让开发用 conda解决思路其实很清晰不要让 conda 的 base 环境自动激活让系统默认的python3始终指向/usr/bin/python3ROS 的所有工具链走系统 Python当你需要做算法开发时再手动激活对应的 conda 虚拟环境。这样做的代价是你每次开终端都要手动conda activate xxx才能进入开发环境。但换来的是 ROS 工具链的绝对稳定。对于机器人开发来说这个交换是值得的因为 ROS 环境的稳定性优先级远高于终端的便利性。下面这张表对比了三种常见的环境管理策略你可以根据自己项目的实际情况选择策略ROS 工具链Python 开发隔离程度推荐场景不装 conda全用系统 Python稳定包冲突频繁无纯 ROS 开发不碰深度学习conda base 自动激活经常报错方便差不推荐conda 手动激活 系统 Python 给 ROS稳定灵活好推荐本文方案Docker 容器隔离最稳定灵活最好多项目并行有运维基础2. Anaconda 安装时就要做对的几个选择2.1 安装包选择和下载渠道Anaconda 的官方安装包体积在 500MB 到 900MB 之间取决于版本。对于国内用户直接从官方源下载可能很慢。我的建议是优先使用国内镜像站提供的 Anaconda 安装包比如清华 TUNA 镜像站或阿里云镜像站它们同步了 Anaconda 的安装包和 conda 仓库。下载时注意选择对应架构的版本。现在大多数开发机是 x86_64安装包名类似Anaconda3-2024.02-1-Linux-x86_64.sh。如果你用的是 ARM 架构的开发板比如某些机器人主控需要选aarch64版本。安装命令很简单bash Anaconda3-2024.02-1-Linux-x86_64.sh安装过程中会问你是否接受 license、安装路径默认~/anaconda3、以及是否运行conda init。这里有一个关键决策点最后一步是否运行 conda init建议选 no。因为conda init会自动修改~/.bashrc把 base 环境激活逻辑写进去这正是我们想避免的。2.2 手动控制 conda 初始化避免污染 ROS 环境如果你在安装时选了 no那安装完成后conda命令还不能直接用。你需要手动在~/.bashrc里加一段但不要用conda init生成的那套逻辑。我的做法是加一个函数只在需要时激活 conda# 手动添加 conda 到 PATH但不自动激活 base export PATH$HOME/anaconda3/bin:$PATH # 定义一个快捷函数手动激活环境 conda_activate() { source $HOME/anaconda3/etc/profile.d/conda.sh conda activate $1 }这样你开终端后conda命令可用但 base 环境不会自动激活python3仍然指向系统 Python。需要开发时执行conda_activate myenv即可。如果你已经运行了conda init也不用重装。打开~/.bashrc找到类似下面这段# conda initialize # ... 一堆代码 ... # conda initialize 把conda activate base这一行注释掉或者把整段替换成上面手动控制的写法。改完后source ~/.bashrc生效。2.3 验证安装是否影响了 ROS安装完 Anaconda 并修改~/.bashrc后开一个新终端依次执行which python3 which python echo $PATH | tr : \n | head -5预期结果是python3指向/usr/bin/python3python可能指向~/anaconda3/bin/python因为 conda 的 bin 在 PATH 里PATH的前几项里 conda 的路径排在系统路径之后或者你手动调整了顺序。然后测试 ROSsource /opt/ros/noetic/setup.bash rosrun rospy_tutorials talker.py如果talker能正常发布消息说明 ROS 环境没有被 conda 破坏。如果报ModuleNotFoundError回到~/.bashrc检查 conda 的 PATH 是否排在了/usr/bin前面。提示which python和which python3的结果不一致是正常的但你要清楚每个命令背后是哪个解释器。ROS 工具链用的是python3所以只要python3指向系统 PythonROS 就安全。3. 创建虚拟环境时最容易忽略的配置细节3.1 指定 Python 版本要和你的依赖匹配创建虚拟环境的命令看起来很简单conda create -n ros_dev python3.8但 Python 版本的选择有讲究。如果你的虚拟环境是用来做 ROS 相关的 Python 开发比如写 ROS 节点、处理 bag 文件建议 Python 版本和 ROS 的 Python 版本保持一致也就是 3.8。这样你在虚拟环境里写的代码可以无缝迁移到 ROS 节点里运行不会因为版本差异出现语法或库行为不一致。如果你的虚拟环境纯粹用来做深度学习训练和 ROS 节点不直接交互那可以用更新的 Python 版本比如 3.10 或 3.11以便使用最新的 PyTorch 或 TensorFlow。我个人的习惯是建两个环境一个ros_devPython 3.8用来做和 ROS 交互的开发一个dl_devPython 3.10用来做模型训练。两个环境互不干扰。3.2 配置国内镜像源否则装包会等到怀疑人生conda 默认的仓库在国外装包速度很慢而且经常超时。创建完环境后第一件事就是配置国内镜像源。清华 TUNA 的 conda 镜像源是常用的选择conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --set show_channel_urls yes配置完后conda install会从国内镜像拉包速度提升明显。但要注意有些包在镜像站同步不及时如果遇到PackagesNotFoundError可以临时用-c指定官方源或者换用 pip 安装。pip 也要配置镜像源。在虚拟环境里执行pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple这样 pip 装包也走国内源。注意这个配置是写在虚拟环境的 pip 配置里的不会影响系统 pip。3.3 环境创建后的依赖安装顺序在ros_dev环境里装包顺序很重要。我的建议是先装numpy、scipy这类基础科学计算库指定版本避免后续被其他包自动升级。再装opencv-python注意选择opencv-python还是opencv-contrib-python后者包含 SIFT、SURF 等专利算法。然后装 ROS 相关的 Python 包比如rospkg、catkin_pkg、rosbag的 Python 接口。这些包在 PyPI 上有独立发行版可以 pip 安装。最后装深度学习框架比如 PyTorch。PyTorch 的安装命令要去官网根据 CUDA 版本生成不要直接pip install torch否则可能装到 CPU 版本。这里有一个坑rosbag的 Python 包在 PyPI 上叫rosbag但它依赖 ROS 的底层库pip 安装的版本可能和系统 ROS 的版本不兼容。如果你只是想在虚拟环境里读取 bag 文件更稳妥的做法是用rosbags这个纯 Python 库它不依赖 ROS 环境可以直接 pip 安装API 也更友好。4. 让 ROS 和 conda 环境在终端里和平共处4.1 写一个环境切换脚本一键进入工作状态每次开终端都要手动 source ROS 和 activate conda很繁琐。我写了一个 shell 函数放在~/.bashrc里ros_dev() { source /opt/ros/noetic/setup.bash source $HOME/anaconda3/etc/profile.d/conda.sh conda activate ros_dev echo ROS ros_dev 环境已激活 echo python3: $(which python3) echo python: $(which python) }执行ros_dev后ROS 环境和 conda 环境同时激活。注意顺序先 source ROS再 activate conda。如果反过来conda 的 PATH 会覆盖 ROS 的 PATH导致rosrun找不到。激活后python3会指向 conda 环境的 Python这意味着你在终端里跑 Python 脚本用的是 conda 环境。但 ROS 的rosrun脚本用的是 shebang 里的python3如果 shebang 是#!/usr/bin/python3那它仍然用系统 Python不受 conda 影响。这就是为什么rosrun在 conda 环境下仍然能正常工作的原因。4.2 验证 ROS 节点在 conda 环境下能否正常运行激活ros_dev环境后跑一个简单的 ROS 节点测试rosrun rospy_tutorials talker.py如果正常发布消息说明 ROS 工具链没有被 conda 干扰。然后开另一个终端同样激活环境跑rosrun rospy_tutorials listener.py如果 listener 能收到消息说明 ROS 的通信机制正常。接下来测试 conda 环境里的 Python 包python -c import numpy; print(numpy.__version__) python -c import cv2; print(cv2.__version__)确认这些包来自 conda 环境而不是系统环境。你可以用python -c import numpy; print(numpy.__file__)查看包的路径如果路径在~/anaconda3/envs/ros_dev/lib/python3.8/site-packages/下说明用的是 conda 环境的包。4.3 在 conda 环境里调用 ROS 的 Python 库有时候你需要在 conda 环境的 Python 脚本里 importrospy。这需要把 ROS 的 Python 路径加到sys.path里。有两种做法第一种是在脚本开头手动加路径import sys sys.path.append(/opt/ros/noetic/lib/python3/dist-packages) import rospy这种做法简单直接但每个脚本都要加比较繁琐。第二种是在 conda 环境里创建一个.pth文件把 ROS 的路径写进去。找到 conda 环境的 site-packages 目录python -c import site; print(site.getsitepackages()[0])然后在那个目录下创建一个ros_path.pth文件内容就一行/opt/ros/noetic/lib/python3/dist-packages这样每次启动这个 conda 环境的 Python都会自动把 ROS 的路径加入sys.path可以直接import rospy。但要注意这种做法会让 conda 环境里的 Python 和 ROS 的 Python 库混在一起可能出现版本冲突。比如 ROS 的numpy版本和 conda 环境的numpy版本不一致import 时会优先用哪个取决于sys.path的顺序。所以这种做法适合你明确知道两边库版本兼容的情况。5. IDE 和开发工具的配置要点5.1 VS Code 里指定 conda 环境的解释器VS Code 是 ROS 开发常用的编辑器。配置 conda 环境的关键是让 VS Code 的 Python 插件找到 conda 环境的解释器。打开 VS Code按CtrlShiftP输入Python: Select Interpreter然后选择~/anaconda3/envs/ros_dev/bin/python。选完后VS Code 的终端会自动激活这个 conda 环境代码补全和 linting 也会基于这个环境。但这里有一个问题VS Code 的集成终端激活 conda 环境后ROS 的环境变量可能没有 source。你需要在 VS Code 的settings.json里配置终端启动时自动 source ROS{ terminal.integrated.profiles.linux: { bash: { path: bash, args: [-l] } }, terminal.integrated.defaultProfile.linux: bash }然后在~/.bashrc里加上 ROS 的 source 命令。这样 VS Code 的终端启动时会自动 source ROS 和 conda 环境。5.2 PyCharm 配置 conda 环境的坑PyCharm 配置 conda 环境时最容易踩的坑是解释器路径选错。PyCharm 会让你选择 conda 可执行文件然后自动检测环境。如果检测不到手动指定解释器路径为~/anaconda3/envs/ros_dev/bin/python。另一个坑是 PyCharm 的终端默认不激活 conda 环境。你需要在Settings - Tools - Terminal里把 Shell path 改成/bin/bash并在Environment variables里加上 conda 的路径。或者更简单的方式是在 PyCharm 的终端里手动执行conda activate ros_dev。PyCharm 还有一个Run Configuration的概念。如果你在 PyCharm 里直接运行 ROS 节点脚本需要在 Run Configuration 里设置环境变量把 ROS 的PYTHONPATH加进去。具体是在 Run Configuration 的Environment variables里加PYTHONPATH/opt/ros/noetic/lib/python3/dist-packages这样 PyCharm 运行脚本时Python 才能找到rospy等 ROS 包。5.3 Jupyter Notebook 在 conda 环境里的配置如果你用 Jupyter Notebook 做数据分析或算法验证需要把 conda 环境注册到 Jupyter 的 kernel 列表里conda activate ros_dev pip install ipykernel python -m ipykernel install --user --nameros_dev --display-namePython (ros_dev)注册完后启动 Jupyter Notebook在 Kernel 菜单里就能看到Python (ros_dev)选项。选中后Notebook 里的代码就在 conda 环境下运行。如果需要在 Notebook 里 importrospy同样需要把 ROS 的路径加到sys.path或者在 conda 环境里创建.pth文件。我个人的做法是在 Notebook 的第一个 cell 里加import sys sys.path.append(/opt/ros/noetic/lib/python3/dist-packages)这样每个 Notebook 都能独立控制不会因为.pth文件影响其他项目。6. 实际项目中踩过的坑和解决方案6.1 catkin_make 编译时找不到 Python 库有一次我在 conda 环境下执行catkin_make编译时报错说找不到empy模块。empy是 ROS 编译时依赖的一个 Python 模板库系统 Python 里装了但 conda 环境里没有。原因是catkin_make内部调用了python3而当时终端里python3指向的是 conda 环境的 Python。解决方法有两个一是在 conda 环境里也装一个empypip install empy二是确保编译时python3指向系统 Python。我选择了后者因为不想让 conda 环境承担 ROS 编译的依赖。具体做法是在~/.bashrc里加一个函数专门用来在系统 Python 环境下编译ros_build() { export PATH/usr/bin:$PATH cd ~/catkin_ws catkin_make export PATH$HOME/anaconda3/bin:$PATH }这样编译时python3指向系统 Python编译完再恢复 conda 的 PATH。6.2 conda 环境里 pip 和 conda 混用导致的依赖混乱conda 和 pip 是两个不同的包管理器混用容易出问题。比如你用conda install numpy装了 numpy然后又用pip install装了一个依赖特定 numpy 版本的包pip 可能会把 conda 装的 numpy 覆盖掉导致 conda 的依赖记录和实际安装的包不一致。我的原则是在 conda 环境里优先用 conda 装包conda 没有的包再用 pip 装。装完之后用conda list查看包的来源如果看到某个包标记为pypi说明是 pip 装的要留意它是否和 conda 的包冲突。如果已经出现了依赖混乱最彻底的解决方法是删掉环境重建conda deactivate conda env remove -n ros_dev conda create -n ros_dev python3.8重建环境虽然麻烦但比逐个排查依赖冲突要快。6.3 虚拟环境迁移到另一台机器有时候你需要把配置好的 conda 环境迁移到另一台机器比如从开发机迁移到机器人主控。conda 提供了conda env export命令导出环境配置conda env export -n ros_dev ros_dev.yml然后在另一台机器上conda env create -f ros_dev.yml但这样导出的 yml 文件里包含了包的精确版本和构建号如果目标机器的系统架构或 CUDA 版本不同可能装不上。更稳妥的做法是手动写一个requirements.txt只列关键的包和版本范围numpy1.21,1.24 scipy1.7 opencv-python4.5 torch2.0然后在目标机器上创建环境后用 pip 安装。这样虽然不能保证版本完全一致但兼容性更好。注意如果目标机器没有网络可以在一台有网络的机器上用pip download把包下载到本地目录然后拷贝到目标机器上用pip install --no-index --find-links./packages安装。conda 也有类似的离线安装机制但配置起来更复杂pip 的离线安装更简单直接。6.4 ROS 2 环境下 conda 的配置差异ROS 2 和 ROS 1 在 Python 环境管理上有一些差异。ROS 2 Humble 默认使用系统 Python 3.10Ubuntu 22.04它的 Python 包路径在/opt/ros/humble/lib/python3.10/site-packages。conda 环境的配置逻辑和 ROS 1 类似但路径要相应调整。ROS 2 使用colcon作为编译工具colcon build时如果终端激活了 conda 环境可能会遇到类似catkin_make的问题。解决思路一样编译时确保python3指向系统 Python或者用--cmake-args -DPython3_EXECUTABLE/usr/bin/python3显式指定 Python 解释器。ROS 2 的rclpy在 conda 环境里 import 也需要加路径import sys sys.path.append(/opt/ros/humble/lib/python3.10/site-packages) import rclpy整体思路和 ROS 1 一致只是路径和 Python 版本不同。7. 一些让环境更稳的长期维护习惯7.1 定期清理无用的 conda 环境conda 环境用久了会积累很多占磁盘空间。定期用conda env list查看所有环境删掉不再用的conda env remove -n old_env同时清理 conda 的缓存conda clean --all这个命令会清理下载的包缓存、索引缓存等通常能释放几个 GB 的空间。7.2 用 environment.yml 管理环境配置对于长期维护的项目建议在项目根目录放一个environment.yml记录这个项目需要的 conda 环境配置。这样换机器或者团队协作时直接conda env create -f environment.yml就能复现环境。environment.yml的写法name: ros_dev channels: - defaults - conda-forge dependencies: - python3.8 - numpy1.23 - scipy1.9 - pip - pip: - opencv-python4.7.0.72 - rospkg - catkin_pkg注意pip下面的包是用 pip 安装的conda 不管理它们的依赖。这种混合写法适合那些 conda 仓库里没有、只能用 pip 装的包。7.3 备份关键环境避免重装系统后从零开始重装系统是每个开发者都会遇到的事。为了避免重装后从零配置环境我习惯把关键环境的environment.yml和requirements.txt备份到 Git 仓库或者云盘。同时把~/.bashrc里和环境相关的配置也备份一份。另外conda 环境本身也可以打包迁移。用conda pack可以把整个环境打包成一个 tar.gz 文件conda pack -n ros_dev -o ros_dev.tar.gz在目标机器上解压到 conda 的 envs 目录下即可使用。这种方式适合环境配置复杂、依赖包多、重新安装耗时的情况。但要注意conda pack打包的环境和系统架构绑定不能跨架构迁移。7.4 监控环境变化及时发现依赖冲突conda 环境用久了可能会因为某次pip install引入依赖冲突。我习惯在每次装完新包后用conda list和pip check检查一下conda activate ros_dev pip checkpip check会检查已安装包的依赖关系如果发现某个包的依赖不满足会给出提示。虽然它不能发现所有问题但能帮你及时察觉明显的冲突。另外如果你在 conda 环境里装了 ROS 相关的包建议定期测试一下 ROS 节点能否正常运行。我通常会在环境配置完成后跑一个简单的 talker/listener 测试确认 ROS 通信正常。这个测试只需要几秒钟但能避免在项目关键时刻发现环境问题。环境管理这件事说到底是在稳定性和便利性之间找平衡。ROS 开发者面对的是一个已经绑定了系统 Python 的工具链Anaconda 提供的是灵活的多环境能力两者要共存就必须在 PATH、sys.path、环境激活时机这几个关键点上做明确的约定。我自己的做法是让 ROS 始终走系统 Pythonconda 环境只在需要时手动激活编译 ROS 包时临时切回系统 Python。这套流程跑了两年多换过三台开发机没有出现过环境层面的严重问题。如果你刚开始搭环境建议先把本文第 2 节的安装配置做对后面的事情会顺很多。