1. 先把问题看明白:Py6S 和那个报错到底是什么
这几年做遥感数据处理的人,多少都绕不开大气校正这件事。Py6S 作为 6S 辐射传输模型的 Python 封装库,确实帮我们省掉了不少手动组织输入文件、解析输出文件的功夫。你也只需要写几行 Python 代码,就能调用 6S 模型完成大气校正参数模拟。但很多人在安装 Py6S 后,第一次运行就栽在一个非常统一的报错上:“6S executable not found”。而且这个报错出现的位置、触发时机、解决思路,和普通 Python 包安装失败完全不一样,光靠pip install重装是解决不了的。
这里先把核心逻辑说清楚:Py6S 本质上只是一个“遥控器”,真正干活的“电视机”是 6S 模型本身。6S 模型是用 Fortran 写的独立程序,需要先被编译成可执行文件,Py6S 在运行时再去调用它。所以当你看到“6S executable not found”,并不是说 Py6S 没装上,而是说它没找到那个真正计算辐射传输的 6S 可执行文件。这篇文章就是围绕这个问题,从原因分析、环境准备、编译配置、踩坑排查几个方面,把整个流程完整走一遍,适合刚接触 Py6S 的遥感方向学生,也适合已经被这个报错卡住、想彻底解决的从业者。
1.1 Py6S 不是装完 pip 包就能直接用的工具
我遇到过不少同学,装 Py6S 之前完全不知道 6S 是一个独立的 Fortran 程序。他们通常的流程是:pip install py6s,然后导入库,写脚本,运行,报错。紧接着去搜索,发现网上教程说法五花八门,有的让下载源码,有的让设置环境变量,最后越搞越乱。要理解这个报错,你得先接受一个事实:Py6S 和 6S 是两个东西,而 Py6S 的正确安装流程其实是“Python 包安装 + 6S 可执行文件编译”两步走。
我用一个生活化的类比来解释:Py6S 就像你买回的智能遥控器,6S 才是客厅里那台需要通电的电视。遥控器本身做工再精致,电视没开机、没通电,你按任何按钮都不会有画面。对应到技术上,6S 可执行文件就是这个“电视”,它是一段经过编译的、可直接运行的二进制程序,负责实际的大气辐射传输计算。Py6S 只是帮你把输入参数整理成 6S 能识别的格式,再把 6S 算完的结果解析回 Python 对象。缺了 6S 可执行文件,Py6S 就只是一个空壳。
所以当你遇到“6S executable not found”时,第一反应不应该是去重装 Py6S,而应该去确认两件事:第一,系统中是否已经存在编译好的 6S 可执行文件;第二,Py6S 运行时能否在约定的路径里找到它。这两件事分别对应“有没有”和“找不找得到”的问题,排查顺序不能反。
1.2 这个报错到底在哪个环节触发
“6S executable not found”并不是在 Py6S 导入时就出现的。你执行from Py6S import SixS时,一切都很正常,因为这句话只是加载 Python 模块,不会立即调用外部程序。真正的报错通常发生在你创建SixS对象并调用run()方法之后,Py6S 才去搜索 6S 可执行文件。这一点很关键,因为它决定了你定位问题的方向:如果导入没问题,说明 Python 包本身安装成功,问题出在外部依赖配置环节。
从 Py6S 源码的逻辑来看,运行时它会按照预设的搜索顺序去找 6S 可执行文件。常见的查找路径包括:当前工作目录、系统 PATH 环境变量、以及少数版本里写死的默认路径。如果这些位置都没有找到名字匹配的可执行文件,就会抛出类似SixSExecutableNotFoundError("6S executable not found")的异常。注意,这个异常的名称和提示文本在不同版本里可能略有出入,但定位思路完全一致。
从这个触发机制可以反向推导出三种解决路线:一是把编译好的 6S 放到 Py6S 默认查找的路径下;二是把 6S 所在目录加入 PATH;三是直接修改 Py6S 源码中关于可执行文件路径的配置。第三条听起来很粗暴,但确实是很多老用户在没有 PATH 配置权限时的兜底方案,后面我会详细说。
2. 动手之前,先检查这几个关键前提
现在你已经知道了问题的本质,但先别着急下载源码、执行编译。我见过太多人一上来就make,结果编译出一堆莫名其妙的错误,最后才发现是自己的操作系统缺少 Fortran 编译器。准备工作做得越充分,后面越少踩坑。我把安装 6S 之前需要确认的事项整理成了几类,每一项都很基础,但每一项都有人栽跟头。
2.1 操作系统和编译工具链要匹配
6S 源码是用 Fortran 77 编写的,虽然非常古老,但它需要的编译器并不复杂,多数 Linux 发行版都支持得很好。在 Linux 环境下,我通常使用gfortran配合make完成编译,这两个工具可以通过包管理器一键安装。在 Ubuntu/Debian 系统上,执行下面的命令就可以完成准备:
sudo apt update sudo apt install -y gfortran make如果你用的是 CentOS、RHEL 这类使用 yum 的系统,对应命令则是:
sudo yum install -y gcc-gfortran make这里有一个细节容易被忽略:6S 源码虽然是 Fortran 77 写的老代码,但现代编译器对它仍然有不错的兼容性。不过有个别行代码的长度比较特殊,可能需要在编译参数里加上-ffixed-line-length-132这类参数,否则某些编译器版本会报“line too long”之类的错误。这个问题我在后面的编译章节会单独展开。总之,在开始之前,先确认gfortran --version和make --version能正常输出,这一步就值回票价了。
2.2 macOS 和 Windows 用户的额外注意事项
如果你用的是 macOS,情况会稍微复杂一点。macOS 自带的 clang 并不包含 Fortran 编译器,所以你需要额外安装 gfortran。最简单的方式是通过 Homebrew 安装:
brew install gfortran但注意,新版 macOS 的架构切换(从 Intel 到 Apple Silicon)带来了一些编译兼容性问题。我实测下来,Apple Silicon 上编译 6S 通常没有太大问题,但个别依赖外部数学库的版本可能会报错。实在编不过去的时候,不用死磕编译,后面我会介绍 Docker 方案,那是更省心的选择。
Windows 用户遇到这个问题就比较头疼了。因为 6S 的老代码默认面向 Unix 环境,在 Windows 上直接编译需要折腾 MinGW 或 Cygwin,配置成本很高。我的建议是不要直接在 Windows 上编译 6S,而是使用 WSL(Windows Subsystem for Linux)来搭建环境。在 WSL 里按 Linux 的流程操作,报错概率会大幅下降。你把 WSL 理解为 Windows 里一个轻量 Linux 虚拟机就好,遥感方向的人大多已经装了 WSL,如果没有,微软官方文档写得很清楚,搜索“安装 WSL”跟着做就行。哪怕只是为了跑 Py6S,这一步也值得。
2.3 确认 Python 环境和 Py6S 版本兼容
在动手编译 6S 之前,先用pip show py6s或pip list | grep -i py6s确认一下 Py6S 是否真的装好了,以及装的是哪个版本。Py6S 对 Python 版本的兼容性在不同阶段有变化,过老的 Python 版本可能装不上最新 Py6S,而太新的 Python 也可能因为依赖包没跟上而出现问题。我目前用的 Python 3.10 搭配 Py6S 1.1.0,运行很稳定;Python 3.11 之后我没遇到过明显问题,但如果你发现安装阶段就报错,可以先考虑换到 Python 3.9 或 3.10 的虚拟环境再试。
另外,Py6S 的运行依赖 numpy、matplotlib、scipy 这些常见科学计算库。如果之前没安装过,建议直接用下面的命令一次性补全:
pip install numpy scipy matplotlib py6s先确认这些基础依赖都正常,再去处理 6S 可执行文件,思路更清晰。注意,我见过有人在 conda 环境里装的 Py6S 是直接从 pip 拉进来的,导致和 conda 的其他包存在 ABI 兼容问题,运行 Py6S 时出现一些莫名其妙的底层层面错误(比如 numpy 报错)。如果你用的是 conda,建议优先用 conda install 搜索有没有 py6s 包;如果没有,再使用 pip 安装,装完之后保持环境稳定,不要再频繁混装其他渠道的包。
2.4 准备一份可信的 6S 源码
6S 模型的源码可以在官方网站或相关学术机构的公开资源里获取。下载前先检查文件哈希或大小,确认下载文件完整。有些镜像站点提供的压缩包不完整,解压时会报“unexpected end of file”,这类错误浪费时间的程度远超你的想象。我通常会先解压到一个独立目录,比如~/6s/,然后再开始编译,这样后面排查路径问题时思路更清晰。尽量不要把源码解压到含有中文或空格的路径里,6S 这种老代码对路径字符的处理能力非常有限,用全英文路径能省掉很多潜在麻烦。
3. 一步步配置,真的把 6S 跑起来
准备工作做完了,下面进入正题。这一节我会给出两种最实用的方案:源码编译和系统包管理器安装。源码编译适用于绝大多数环境,而且能让你对 6S 的安装位置和编译过程有绝对控制权;系统包管理器则适合懒得折腾、只想快速跑通的场景。我会把两种方案的细节都讲清楚,你再根据自己实际情况选。
3.1 方案 A:从源码编译 6S(推荐)
Step 1:下载并解压源码包。以 6S V1.1 为例,在终端里执行:
mkdir -p ~/6s && cd ~/6s wget <6S源码下载地址> tar -xzf 6S_V1.1.tar.gz cd 6S_V1.1解压之后,先别急着 make。打开目录看一看到底有哪些文件,通常会有 Makefile、src 目录、示例文件等。用ls -l确认一下源码文件权限,如果发现.f文件没有读权限,先执行chmod -R u+r .修正权限,不然后续编译会报一些奇怪的文件读取错误。
Step 2:编译。6S 的编译本质上是把一堆 Fortran 源码编译链接成一个可执行文件。在源码目录下,直接运行:
make如果一切顺利,你会在当前目录(或 Makefile 指定的目录)下看到一个名为6S的可执行文件。这里极其容易踩的坑是 Makefile 里写的是旧式编译器命令,比如f77,但你的系统只有gfortran。遇到这种情况,你需要手动修改 Makefile,把其中的f77全部替换成gfortran。我建议直接用下面的命令进行替换:
sed -i 's/f77/gfortran/g' Makefile make clean && make如果编译过程中出现 “line too long” 这种与代码行宽相关的错误,说明缺少 Fortran 固定格式扩展。此时打开 Makefile,在FFLAGS或F77FLAGS变量里加上-ffixed-line-length-132,然后重新编译。修改后的这一行看起来类似这样:
FFLAGS = -O2 -ffixed-line-length-132如果你发现自己手里的源码没有 Makefile,而是一个compile脚本或一堆.f文件,也不用慌。手动编译的思路是一样的,就是找到所有.f文件,然后用 gfortran 全部编译并链接。命令可以写成这样:
gfortran -O2 -ffixed-line-length-132 -o 6S *.f但注意,不同版本的 6S 源码里主程序文件名不一样,也可能有额外的.h或.inc头文件依赖,这会导致一条命令直接编译失败,这类问题往往需要分段编译再链接。所以如果你经验不多,优先找带 Makefile 的版本,省事得多。
Step 3:确认编译结果。编译完成后,执行以下命令确认可执行文件是否生成:
ls -la ~/6s/6S_V1.1/6S file ~/6s/6S_V1.1/6S如果第二行输出类似 “ELF 64-bit executable” 的信息,说明编译成功。如果什么也没输出,说明可执行文件在别的目录,或者编译过程中有错误被忽略了。用find ~/6s -name "6S" -type f找一下,找不到就回头仔细看编译日志里的 error 和 warning 信息。
3.2 方案 B:使用系统包管理器快速安装
如果你不想折腾编译,有些 Linux 发行版的软件源里直接带了 6S 的二进制包。比如在 Ubuntu 上可以尝试:
sudo apt install 6s执行完后直接用which 6S或which 6s查看安装位置。注意,可执行文件的大小写在不同包里可能不一样,有的叫6S,有的叫6s,后面配置 Py6S 时要根据实际情况调整。这种方式的优点是快,缺点是版本可能比较旧,而且有的发行版并没有打包这个软件,会导致 apt 报“找不到包”。如果 apt 里没有,我还是建议回到源码编译方案,那才是真正通用的路径。
3.3 把 6S 放到 Py6S 能找到的位置
源码编译或包管理器安装完成之后,只是解决了“有 6S”这个前提,接下来的核心任务,是让 Py6S 能在运行时找到它。你要是以为“文件存在”就万事大吉,那就太天真了。Py6S 查不到路径照样报“6S executable not found”。最省心、最不会出错的方法,就是把 6S 可执行文件放到/usr/local/bin下,这个目录默认在系统 PATH 里。命令如下:
sudo cp ~/6s/6S_V1.1/6S /usr/local/bin/6S sudo chmod +x /usr/local/bin/6S然后验证一下:
which 6S如果终端能输出/usr/local/bin/6S,说明现在已经可以通过 PATH 找到 6S。Py6S 在执行时如果走的是系统 PATH 搜索,这个方案就可以直接解决你的问题。
如果你不想把文件复制到系统目录,也可以选择把 6S 所在目录加入 PATH。以 bash 为例,在~/.bashrc末尾加一行:
export PATH="$HOME/6s/6S_V1.1:$PATH"然后执行source ~/.bashrc使配置生效。注意这里要保证路径里没有拼写错误,我见过很多次导出路径末尾多了一个空格,结果找半天找不到问题。
3.4 终极兜底:直接让 Py6S 源码知道 6S 在哪
某些极其特殊的场景下,比如你所在的项目环境不允许修改系统 PATH,或者用的是别人封装好的 Py6S 版本,导致默认查找逻辑不完整。这时候还有一个兜底方案:找到 Py6S 的安装目录,直接修改源码中的可执行文件路径。
先找到 Py6S 的安装路径:
python -c "import Py6S; print(Py6S.__file__)"输出类似/usr/local/lib/python3.10/site-packages/Py6S/__init__.py,对应的目录就是 Py6S 包所在目录。进入这个目录,用你熟悉的编辑器打开sixs.py(或sixs_config.py),搜索“6S”字符串,一般会看到定义可执行文件路径的变量,比如SIXS_PATH或EXE_NAME。把它改成你实际的 6S 可执行文件完整路径,保存后重新导入 Py6S。这个方法虽然不优雅,但我实测过,效果立竿见影。前提是你要有对应目录的写权限,如果系统用了严格权限限制,可能需要使用管理员权限修改或换一个用户可以写的虚拟环境。
提示:修改 site-packages 下的源码,在下次升级 Py6S 时可能会被覆盖。建议使用这个方案后记住自己的改动位置,升级完如果发现路径被重置,重新修改一次即可。
3.5 验证配置是否真正做到位
很多人在配置完成后,只是重启了 Python,没有做任何测试就宣称“解决了”,结果运行真实计算时又炸。我建议按照下面的验证脚本完整跑一遍,确认所有环节都通了再继续后续工作:
from Py6S import SixS import Py6S s = SixS() s.atmos_profile = Py6S.AtmosProfile.PredefinedType(Py6S.AtmosProfile.MidLatitudeSummer) s.ground_reflectance = 0.2 s.solar_z = 30 s.sat_z = 0 s.run() print("辐射传输计算完成") print(s.outputs.pixel_radiance) print(s.outputs.transmittance)如果这段脚本能正常输出结果,没有抛出任何异常,说明 6S 已经可以被 Py6S 正常调用了。如果仍然报错,那就进入下一节的排查流程,看看问题到底出在哪个环节。
4. 我在实际操作中遇到的坑与排查技巧
再完美的教程也挡不住现实世界的多样性。我在不同机器、不同环境下部署 Py6S 时,前后踩过不少坑,这里把最典型的几类问题整理成速查表,方便你照方抓药。
4.1 “6S executable not found” 依然出现怎么办
如果已经按照前面步骤把 6S 放进了/usr/local/bin,which 6S也能正常输出,但 Py6S 还是报错,那就要检查几件容易被忽略的小事。首先,确认文件名大小写是否和 Py6S 期待的一致。Py6S 有些版本查找的是大写的6S,有些版本却用小写的6s。如果搞混了,文件明明存在,Py6S 就是看不到。解决办法是干脆两个名字都复制一份:
sudo cp /usr/local/bin/6S /usr/local/bin/6s其次,检查可执行权限。用ls -l /usr/local/bin/6S查看权限位,如果第一列类似-rw-r--r--,说明文件没有执行权限,Py6S 即使找到文件也没法运行。执行sudo chmod +x /usr/local/bin/6S修复。
第三,要检查你的 Python 进程的环境变量。很多同学是在 IDE 里运行代码,而 IDE 的 PATH 环境可能和你终端里source ~/.bashrc之后不一样。解决办法是在运行脚本前,先打印一下 PATH 和文件是否存在:
import os print(os.environ.get("PATH")) print(os.path.exists("/usr/local/bin/6S")) print(os.access("/usr/local/bin/6S", os.X_OK))一旦发现 PATH 里没有你期望的目录,或者文件不可执行,问题就一目了然了。这一招排查效率极高,比盲目改代码强得多。
4.2 编译阶段的常见报错与应对
编译 6S 最常见的报错有两类。第一类是找不到 Fortran 编译器,比如提示make: f77: No such file or directory。这个说明 Makefile 里写死了f77,但你装的是gfortran。直接用sed -i 's/f77/gfortran/g' Makefile替换即可,之后再清理重编。第二类是刚才提过的代码行宽问题,报错信息往往包含Error: Line truncated。解决办法是给编译器加-ffixed-line-length-132参数,且这个参数需要加在编译阶段,而不是链接阶段。如果你不确定怎么改,直接在 Makefile 里搜FFLAGS,把它替换成下面这行再编译:
FFLAGS = -O2 -ffixed-line-length-132还有一种少见但确实存在的情况:编译器版本过新,对老代码的一些非标准语法会以硬错误方式拒绝。遇到这种问题,可以先尝试降低优化等级,比如把-O2改成-O0或-O1。我遇到过有些机器在-O2下编译正常、运行闪退,降到-O1反而稳定,原因猜测和浮点优化有关,但这个玄学问题不好深究,先用起来再说。
4.3 Py6S 运行时出现段错误或崩溃
如果你的 6S 编译成功了,Py6S 也不再报“executable not found”,但运行s.run()时 Python 直接崩溃或者返回 139 错误码,那是另一个层面的问题。这类段错误通常和编译器优化级别、系统库兼容性有关。解决办法在刚才提过:把编译参数从-O2降为-O1或-O0,重新编译后再试。如果仍然崩溃,可以考虑换一个版本的 gfortran,或者干脆走 Docker 方案隔离环境。
4.4 备选方案:Docker 一劳永逸
如果你做遥感数据处理,本来就在用 Docker 做环境隔离,那 Py6S 的 6S 可执行文件问题也可以通过 Docker 解决。社区里其实已经有一些现成的 Py6S 镜像,Docker Hub 搜索关键词就能找到。如果你愿意自己写,Dockerfile 的思路也很简单:基于一个 Python 镜像,安装 gfortran、make、下载 6S 源码、编译、复制可执行文件到 PATH、安装 Py6S。最后把计算目录挂载进容器即可。用 Docker 的好处是你不用在自己电脑上折腾 Fortran 编译器,也不用担心污染系统环境;坏处是第一次构建镜像需要点耐心,而且 Docker 的磁盘占用也不算小。如果你只是想在本地快速跑一个脚本验证思路,还是前面两种方案更轻量。
4.5 问题排查速查表
我把这一节遇到的主要问题整理成一张表,你可以直接对照处理。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| run() 报 6S executable not found | 6S 不在 PATH;文件名大小写不符;无执行权限 | 复制到 /usr/local/bin;同时放 6S 和 6s;chmod +x |
| make 报 f77 不存在 | 缺少 Fortran 编译器 | 安装 gfortran;sed 替换 Makefile 中的 f77 为 gfortran |
| 编译报 line truncated | 老代码行太长,未设置固定行宽 | 在 FFLAGS 中加 -ffixed-line-length-132 |
| 计算时段错误或崩溃 | 编译优化等级过高;ABI 不兼容 | 编译时降为 -O0 或 -O1;换 gfortran 版本 |
| which 6S 找不到 | 路径没加入 PATH;~/.bashrc 没生效 | 检查导出路径;重新 source;或复制到 /usr/local/bin |
| conda 环境导入 Py6S 时报 numpy 底层错误 | 混装 pip 和 conda 包 | 用虚拟环境重建依赖;避免反复混用安装源 |
5. 一些值得留意的配置和后续扩展建议
6S 可执行文件的问题解决之后,不要觉得就万事大吉了。我建议你做两件收尾的事情。第一件事,把你安装 6S 的可执行文件备份一份到项目目录或云盘,这样以后换机器、换环境时可以快速恢复,不需要重新编译一遍。第二件事,在自己的实验记录里写下你安装的 Py6S 版本、6S 源码版本、编译参数和可执行文件路径,这些小细节在后续写论文、做实验复现时非常有用,相信我,你不会记得三个月前到底用了哪些参数。
从实际使用角度,我还想提醒一个容易被忽略的细节:不同版本的 6S 源码导致的模拟结果略有差异。如果将来你在同一篇论文里用 Py6S 算了多个数据,中途升级过 6S 可执行文件,那前后数据的一致性就要打一个问号。所以综合来看,固定版本、固定环境是保证实验结果可复现的重要手段。
这之后,你还可以去了解一下 Py6S 的兄弟库 Py6S-LUT。它相当于在 Py6S 外面加了一层查找表生成逻辑,能预先计算不同条件下的参数组合,在大批量影像大气校正时明显提高效率。安装路径问题解决后,再去看它就会顺手很多。
最后再说一个我从实际操作中积累的习惯:每次在全新机器上配置 Py6S,我都会先跑一遍最极简的测试代码,确认 6S 能被调用,再进入正式计算。这个习惯看似平平无奇,但它帮我筛掉过好几台服务器上的环境问题。希望这套安装和排查思路也能帮你减少一些无谓的折腾,把时间花在真正该花的地方。