玩 Jetson Nano 的人应该都有过这种体验板子一到手先刷系统、配环境然后跑通第一个深度学习推理程序成就感满满。可是没几天当你开始同时折腾两三个项目时Python 环境就变成了一团乱麻。今天给项目 A 装了个依赖明天项目 B 启动时报错为了满足某个库的版本要求被迫把系统里的包升级了一遍结果整个系统环境都变得不稳定。这就是典型的“全局 Python 环境没有隔离”的问题。Jetson Nano 和普通电脑不一样它的系统里有很多定制的底层组件直接往系统 Python 里乱装包风险比在台式机上高很多。virtualenv 作为 Python 生态里最经典的环境管理工具可以把每个项目的依赖封装在独立目录里互不干扰。这篇文章我把在 Jetson Nano 上安装和使用 virtualenv 的完整流程、背后原理、踩坑经验一次性讲清楚适合刚入手 Jetson Nano 的新手也适合已经被环境问题折磨过一轮的老手。1. 为什么说 Jetson Nano 上必须重视 Python 环境隔离1.1 Jetson Nano 默认 Python 环境的真实处境Jetson Nano 到手之后系统通常是预装好的基于 Ubuntu。系统里自带的 Python 并不是一个“纯净的 Python”它是整个系统的一部分很多底层工具都依赖它。我手上的板子默认是 Python 3.6.9另外还带着一堆已经被 JetPack 环境装好的包比如 NumPy、OpenCV 的 Python 绑定、各种和硬件加速相关的库。很多初学者习惯拿到板子先跑pip install缺什么装什么全装到系统 Python 里。这种用法在普通电脑上也许只是造成一些小麻烦但在 Jetson Nano 上往往会直接搞坏系统环境。因为系统里已经有大量定制过的包你用 pip 强制更新某个包的版本可能刚好破坏了另一个底层组件的依赖关系。我见过一个朋友的板子本来摄像头推理程序跑得好好的为了装一个新库顺手升级了 NumPy结果系统自带的一堆工具全报错最后只能重新刷系统。这个代价很惨所以我才强调环境隔离。还有一点需要注意Jetson 的系统包不是越新越好。由于硬件平台比较特殊很多软件包是专门适配过的官方源和普通 Ubuntu 源并不完全一样。你在全局环境里随便改版本很容易踩进“依赖地狱”。虚拟环境能把你项目的包圈在一个独立区域里至少不会全局环境越搞越乱。1.2 项目依赖冲突是一场灾难我再讲一个很常见的场景。你在 Jetson Nano 上同时有两个项目一个是图像识别项目需要用到某个版本的图像处理库另一个是传感器数据采集项目依赖另一个框架的早期版本。如果两个项目共用一个全局 Python 环境那安装操作就变成了一场拔河比赛装 A 项目依赖时B 项目用不了了切到 B 项目重新装依赖A 项目又废了。这里可以用一个生活里的类比一个公共厨房里有人做菜要放辣椒有人做菜要清淡最后所有人放在同一个锅里炒谁都吃不好。virtualenv 做的事情很简单就是给每个项目准备一口独立的锅、一套独立的调味料。代码层面它会为项目生成一个新的目录里面有独立的 Python 解释器入口、独立的site-packages目录。你在这个虚拟环境里安装的任何包都会被放进去不会影响系统 Python也不会影响其他虚拟环境。有人可能觉得项目之间切换时把依赖导出来再重装不就行了吗实际操作过就知道这不现实。复杂项目的依赖动辄几十个每个包还有自己的间接依赖靠手工反复重装既慢又容易出错。环境隔离不是锦上添花而是 Jetson Nano 这种单板设备上开发的基本操作。1.3 virtualenv 和同类方案的取舍在 Python 环境管理上市面上其实有几种选择。Python 标准库自带的venv第三方工具virtualenv还有更重的集成发行版以及容器级隔离方案。Python 自带的venv在 Jetson Nano 上能用但存在一些限制。一是它默认依赖系统里的 Python 版本如果你想在同一个项目里跑不同版本的 Python它用起来不够灵活二是在部分 Ubuntu 系统上venv模块本身还没有预装需要额外安装python3-venv包三是它默认创建的环境里没有pip或者说需要额外引导遇到网络不好的时候反而更折腾。virtualenv作为更成熟的老牌工具支持指定 Python 解释器、支持更细的控制参数在 Jetson Nano 这种受限环境里操作起来更顺手。集成发行版方案我身边也有人用过但体积太大。Jetson Nano 如果用的是 16GB 的 eMMC 或者 32GB SD 卡装完系统之后剩余空间本来就不多再塞一个几 GB 的集成环境还没开始写代码存储先告急了。容器方案隔离最彻底但需要在 Jetson 上处理设备映射、内核带 CUDA 相关设置门槛偏高对多数做项目开发的场景来说有点重。所以我的结论很明确在 Jetson Nano 上跑多个 Python 项目virtualenv是性价比最高的选择。轻量、直观、迁移方便而且不占用太多磁盘和内存。2. 安装前的系统检查与准备2.1 确认你的 Jetson Nano 系统状态我建议先别急着敲安装命令先花两分钟把系统基本情况搞清楚。Jetson Nano 的系统和普通电脑不完全一样尤其是 Python 版本和 CPU 架构直接决定了后面装包的方式。uname -m lsb_release -a python3 --version python3 -m pip --version我在自己的板子上执行的结果大概是aarch64 Ubuntu 18.04.6 LTS Python 3.6.9 pip 20.x from /usr/lib/python3/dist-packages/pip (python 3.6)这里最值得关注的是aarch64。这是 ARM 64 位架构意味着你在网上看到的很多下载好的 Python 包预编译版本都不能直接用因为它们往往是 x86 架构的。Jetson Nano 上装包通常有两条路一是找到专门为 ARM64 编译好的 wheel 包二是从源码编译。后者对系统依赖要求很高耗时也长所以后面所有操作都要围绕 ARM64 这个前提来做。如果系统提示没有pip3说明基础环境还不完整先安装基础依赖这个放到后面的步骤里。确认完系统版本之后我会顺手记录一下当前 Python 解释器的绝对路径比如/usr/bin/python3.6因为创建虚拟环境时可以直接指定这个路径避免系统默认有多套 Python 时选错。2.2 给 pip 换源否则安装能等哭你Jetson Nano 出厂自带的 pip 默认用的是官方源。实际用过的朋友应该都知道在国内网络环境下直接从官方源下载大一点的包速度非常不稳定经常下载到一半就超时然后整个安装失败。有的包反复重试好几次甚至一晚上都装不完这是很多新手第一次被劝退的地方。解决办法是给 pip 配置一个速度更快、连接更稳定的镜像源。这个操作不会影响系统安全只是把下载地址替换成你区域里访问效果更好的镜像站点。具体方法是pip3 config set global.index-url https://你的镜像站点地址/simple执行完之后可以检查一下配置是否生效pip3 config list如果你有多个源地址想作为备选可以用extra-index-url再指定一个备用源。这样主源下载失败时pip 会尝试备用源。配置一次之后以后所有用 pip 的安装都会走这个源包括后面虚拟环境里的操作。不过提醒一句给 pip 换源只影响 Python 包下载。如果你的板子整体系统更新慢那是另一个层次的问题需要对系统的软件源列表做调整这里先不展开。2.3 更新系统包时别踩的坑在装 virtualenv 之前系统基础环境最好完整一点。我通常执行下面的命令补上开发常用的基础包sudo apt update sudo apt install -y python3-pip python3-dev build-essentialpython3-dev很重要。Jetson Nano 上很多 Python 包从源码编译时需要用到 Python 的头文件不装这个包编译时大概率会报“找不到 Python.h”之类的错误。build-essential则提供了编译需要的 gcc、make 等基础工具。但这里有个大坑不要随手执行sudo apt upgrade把整个系统全部升级一遍。Jetson 的系统里有很多底层驱动和硬件支持包是定制过的全量升级很容易把硬件支持和系统组件之间的平衡打破。我就见过有人把系统升级之后原本能用的摄像头模块识别不到了最后只能重新刷系统。所以我的原则是apt update用来刷新索引没问题升级 PT 包时看清楚不相关的包一律不动。如果系统的 pip 版本确实太老我建议先不要全局升级 pip。Jetson 系统里全局 pip 变动同样可能影响系统组件。更好的做法是留个能用的初始 pip 就行真正升级 pip 的动作放到创建虚拟环境之后再去做。这个细节看起来不起眼但能省掉不少麻烦。3. virtualenv 安装与基础使用完整实操3.1 安装 virtualenv 本体virtualenv 是环境管理工具它本身属于开发工具所以在系统层面安装是合理的。我自己更推荐通过 pip 安装到当前用户目录这样能获得较新的版本也方便管理。执行命令python3 -m pip install --user virtualenv这里写python3 -m pip而不是直接写pip是怕系统里存在多个 Python 版本时选错 pip。安装完成后验证一下python3 -m virtualenv --version如果不出意外会输出类似virtualenv 20.x.x的版本号。这时你可能会发现单独敲virtualenv命令还是提示找不到这多半是因为用户安装目录没有加入 PATH。执行这条命令查看情况echo $HOME/.local/bin如果该目录不在环境变量里把下面这行追加到~/.bashrc的末尾然后重新加载echo export PATH$HOME/.local/bin:$PATH ~/.bashrc source ~/.bashrc之后virtualenv --version就能直接用了。如果你的系统也通过 apt 装了旧版 virtualenv我建议优先用 pip 安装的这个新版因为新版对 Python 3.6 到更新版本的支持都更好创建环境的速度也更快。3.2 创建第一个虚拟环境现在用一个实际例子演示创建过程。假设我准备在~/projects/hello_jetpack目录下做一个新项目mkdir -p ~/projects/hello_jetpack cd ~/projects/hello_jetpack virtualenv -p python3 venv命令里的-p python3表示指定使用系统里的 Python 3 解释器来创建环境。如果你系统里装了多个 Python 版本也可以明确指定路径比如virtualenv -p /usr/bin/python3.6 venv这样更保险。创建完成后venv目录下会生成一套完整的虚拟环境结构核心包括venv/ ├── bin/ │ ├── python │ ├── pip │ └── activate └── lib/ └── python3.6/ └── site-packages/其中bin/python是指向系统解释器的符号链接bin/pip是虚拟环境专用 pip 脚本。以后在这个环境里安装的所有包都会进入lib/python3.6/site-packages和系统环境完全隔离。在 Jetson Nano 上我还想提一个非常实用的选项--system-site-packages。它的意思是虚拟环境继承系统 Python 里已经装好的包。因为 Jetson 系统里已经预装了某些大型科学计算库如果你不想在虚拟环境里重复安装一遍可以用这个参数创建环境virtualenv -p python3 --system-site-packages venv但它也有个缺点虚拟环境里 pip 安装新包时可能会和系统自带包发生版本冲突而且你不好排查。所以我个人建议是默认情况下不用这个参数除非你确实明确知道要复用系统里的某些预编译库。3.3 激活、退出与日常操作虚拟环境创建好了还需要“激活”才能使用。激活的方式是source venv/bin/activate激活之后命令行提示符前面会出现(venv)字样比如(venv) 用户主机名:~/projects/hello_jetpack$然后你可以用which python确认当前解释器已经指向虚拟环境which python # 输出/home/用户名/projects/hello_jetpack/venv/bin/python这个验证步骤非常关键因为很多人以为自己激活了实际没生效。激活的本质是修改当前终端会话的 PATH 变量把venv/bin放到最前面。所以终端里执行python、pip时都会先找到虚拟环境里的版本。日常操作就是在激活状态下安装包pip install requests pip list不需要用了就退出deactivate退出之后which python会重新回到系统路径。还有一个常见误区有人觉得虚拟环境要和“某个目录”绑定必须cd到环境目录里才能用。其实不用只要保持终端 session 处于激活状态你在任何路径下都可以使用这个虚拟环境的 Python。只是项目中新建的依赖文件一般会放在项目目录里所以通常在项目根目录下激活。3.4 用 requirements.txt 锁定依赖环境建好、包装好之后一定要养成记录依赖列表的习惯。最直接的做法是pip freeze requirements.txt这个命令会把当前虚拟环境里所有包的名字和版本号写入requirements.txt。之后在其他板子上或者环境被误删时可以用下面的命令恢复pip install -r requirements.txt不过我在 Jetson Nano 上使用时有两点心得。第一pip freeze会把所有间接依赖都列进去看起来可能很长。如果你只想记录自己主动装过的顶层包可以这样pip list --not-required --formatfreeze requirements.txt第二Jetson 上有些包不是简单 pip 安装的可能是从源码编译或者来自某个特殊源。这类包在requirements.txt里可能记录成类似file:///...的路径换环境后直接-r恢复往往会失败。我自己的习惯是requirements.txt 仍然保留但另外再用注释写清楚特殊包的安装方式比如需要从源码编译、需要提前装某个系统库。恢复依赖时也不要一次pip install -r requirements.txt一把梭。在 Jetson 上很多包体积很大同时下载安装容易超时或内存不足。先装几个核心大包再让它们把依赖带上来最后再补剩余的小包这样更稳。4. 在 Jetson Nano 上使用 virtualenv 的高阶技巧4.1 让代码编辑器正确识别虚拟环境很多人在终端里使用虚拟环境没问题但打开编辑器之后右下角显示的解释器还是系统 Python结果编辑器里运行代码和终端里运行代码用的不是同一个环境折腾半天找不到问题。解决办法很简单在编辑器的 Python 解释器设置里把路径指向当前项目的venv/bin/python。以常见 IDE 为例打开项目设置找到 Python Interpreter 或解释器路径选项手动填入/home/用户名/projects/hello_jetpack/venv/bin/python需要说明的是不要填成相对路径venv/bin/python因为在不同的工作路径下相对路径可能失效。用绝对路径最稳妥。如果你用的是远程开发方式还需要在远程端激活虚拟环境后再启动编辑服务或者在远程设置里明确指定这个解释器路径。还有一个容易被忽略的地方如果你在编辑器里新建了一个终端窗口它的环境变量不一定会自动继承你之前手动激活的虚拟环境。这时候要看终端提示符里有没有(venv)标注。没有的话就再执行一次source venv/bin/activate。4.2 路径与软链接的小陷阱Jetson Nano 上使用 virtualenv有一个坑很容易踩移动虚拟环境目录。虚拟环境里的python本质上是软链接指向创建时的系统解释器而bin/pip、bin/activate等脚本里的 shebang 或配置写死了虚拟环境的绝对路径。创建时路径是什么后面就必须在什么路径下运行。如果你通过重命名、移动目录把整个venv换了个位置激活脚本可能还能用但pip脚本会直接报错因为它第一行写的还是旧的解释器路径。所以我的建议是创建虚拟环境时就想好一个明确的项目目录不要为了方便临时建在某个临时路径下之后再挪。如果确实需要迁移最省事的做法是重建环境# 记录依赖 pip freeze requirements.txt # 删掉旧环境目录 deactivate rm -rf venv # 在新目录重新创建 virtualenv -p python3 venv source venv/bin/activate pip install -r requirements.txt这个过程不复杂但一旦你分不清哪些包是手动装的、哪些是间接依赖重建时会手忙脚乱。所以前面说的requirements.txt习惯关键时刻是真的救命。还有一个坑是系统 Python 版本被升级。比如你用某个方式把系统的 Python 3.6 替换成了 Python 3.8旧的虚拟环境可能无法正常运行。因为它的符号链接失效了。遇到这种情况不要试图修修补补直接重建虚拟环境最干净。4.3 内存紧张时的安装策略Jetson Nano 的运存有限很多时候卡在 pip 安装编译阶段其实不是网络问题而是内存爆了。尤其是一些大型科学计算包在 ARM64 上没有现成的预编译 wheel 时pip 会自动从源码编译编译过程会并行开多个进程内存瞬间吃满然后系统直接 OOM。解决思路分几层。第一层先检查有没有 ARM64 的预编译 wheel优先装预编译版。第二层如果必须从源码编译我会创建一个 swap 文件给系统增加虚拟内存sudo fallocate -l 4G /var/swapfile sudo chmod 600 /var/swapfile sudo mkswap /var/swapfile sudo swapon /var/swapfile如果想开机自动启用把这个文件加入系统挂载配置echo /var/swapfile none swap sw 0 0 | sudo tee -a /etc/fstab加了 swap 之后系统可以撑过编译高峰但别忘了这是用 SD 卡或硬盘空间换来的频繁写入对存储介质寿命有一定影响。所以我的原则是能用预编译包就用预编译包实在不行才开 swap装完大包之后如果不需要 swap 了可以关掉。第三层限制编译并行数。源码编译时可以通过环境变量限制 CPU 并行编译任务数export MAKEFLAGS-j2这样编译进程不会一次性把内存打满。实测下来虽然编译时间会变长但至少不会中途被系统杀掉。5. 常见问题排查实录5.1 快速排查速查表先给一张我实际用过很多次的排查表遇到问题可以先对照看看。问题现象常见原因解决办法提示virtualenv: command not foundvirtualenv 未安装或安装目录不在 PATH重新执行安装命令并确认~/.local/bin已加入 PATH提示No module named virtualenv当前 Python 解释器里没有这个包用python3 -m pip install --user virtualenv安装创建虚拟环境时提示找不到ensurepip系统缺少python3-venv执行sudo apt install python3-venv虚拟环境激活后pip还是指向全局当前 shell 没有重新读取 PATH执行hash -r或重开终端安装包时提示网络超时pip 源速度慢配置镜像源或增加超时时间--timeout 60编译时报错找不到 Python 头文件缺少python3-dev执行sudo apt install python3-dev移动虚拟环境后无法运行环境脚本写死绝对路径不要移动虚拟环境重建环境并恢复依赖5.2 几个最常见的错误实录我在 Jetson Nano 上遇到得最多的错误之一是创建虚拟环境时指定了-p python3但系统根本没有名为python的命令只有python3。这时 virtualenv 会报一种含糊的找不到解释器错误。解决办法是使用绝对路径指定解释器virtualenv -p /usr/bin/python3 venv或者在创建之前先确认系统里有哪些可行的 Python 路径。第二个高频问题出在 pip 源。Jetson 系统自带的网络环境本身就不稳定如果你忘记了换源下载一个稍大的包很容易出现无限重试或者Read timed out。我的处理方式是先执行一次无操作下载命令测试连通性python3 -m pip install --timeout 20 -i 你的镜像站点/simple 一个很小的包小包能装成功说明源配置没问题如果还超时再检查网络。不要反复重试默认源浪费时间。第三个问题是虚拟环境里居然装不了系统相关的包。比如你已经在venv里激活了环境但安装某个包时还是提示权限不足或者包被装到了系统目录。这往往是因为你用了sudo pip install而sudo会切到 root 用户的 PATH导致它完全忽略了虚拟环境。这是一个新手很容易踩的坑虚拟环境激活之后安装包不需要加 sudo也千万不要加 sudo。否则你明明以为自己装在虚拟环境里实际却污染了系统环境。第四个问题是一个比较隐蔽的现象安装了包之后在 Python 里import还是失败。我排查过几次基本都出在编辑器或终端的 Python 解释器没有切换。单独在终端里which python看的是虚拟环境但编辑器里运行的 Python 是系统解释器这两者不一致就会造成上述现象。碰到这类问题先不要怀疑包的安装过程先检查解释器路径。5.3 大型库安装的特殊处理在 Jetson Nano 上某些大型科学计算库的安装方式和普通电脑不太一样。它们往往涉及底层 BLAS、CUDA 等组件安装失败的概率很高。我的处理流程是先在虚拟环境里尝试正常安装如果 pip 开始编译源码了并且编译过程卡住或无响应立刻按 CtrlC 中止换用更合适的安装策略。最好的情况是能找到适配 ARM64 且匹配当前系统版本的预编译包直接安装避免源码编译。如果必须编译源码我建议先把编译依赖补齐进去否则编译到一半会缺这个缺那个sudo apt install -y build-essential cmake libopenblas-dev liblapack-dev另外某些和硬件加速相关的库在运行时需要 CUDA 环境变量。Jetson 上 CUDA 通常安装在/usr/local/cuda目录下。为了让虚拟环境每次激活时自动加载好这些路径我习惯把下面几行追加到venv/bin/activate文件末尾export PATH/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH export CUDA_HOME/usr/local/cuda这样每次source venv/bin/activate时环境变量就会自动准备好不需要每次手动敲。如果在安装或调用时发现库文件找不到优先检查LD_LIBRARY_PATH是否包含/usr/local/cuda/lib64。还有一个值得说的经验尽量不要在一个虚拟环境里同时维护太多大型库不同用途的包分开放。比如图像识别项目单独一个虚拟环境控制板卡 GPIO 的项目单独一个。这样即使某个环境坏了也不会影响另一个项目重建成本也低。6. 最后再分享一套我目前用的工作习惯Jetson Nano 上跑项目环境问题占用的开发时间往往比代码本身还多。为了少踩坑我现在基本固定了一套流程拿到新板子、开始新项目时先创建目录再立刻创建虚拟环境然后才装第一个包。目录结构通常是~/jetson_projects/ ├── project_a/ │ ├── venv/ │ └── src/ └── project_b/ │ ├── venv/ │ └── src/每个项目一个虚拟环境互不干扰。每次装完新包我会立即把requirements.txt更新一下。不要等到项目做完了再一次性导出因为到那时候你很可能已经忘了哪些包是哪个项目用的。还有一个小技巧激活环境之后我会在终端里先跑一下which python和python --version确认无误再开始装包。这一步看着很傻但真的能避免很多低级的错误。尤其是当你同时开了好几个终端窗口时每个终端激活的环境可能都不一样。遇到环境莫名奇妙出问题的时候我也不会急着去分析包版本而是先看两样东西当前终端有没有(venv)标识以及which python指向哪里。很多时候问题根本不是包坏了而是解释器用错了。这套方法在 Jetson Nano 上陪我完成了不少项目到现在为止我再也没有因为环境冲突重新刷过系统。如果你也在这块板子上被 Python 环境折磨过不妨从安装 virtualenv 开始把环境管理这件事理顺了后面的开发效率会明显上一个台阶。
阅读完成 · 觉得有帮助?