做遥感反演和大气校正的人,十有八九会在Python里碰上Py6S这个库。它是6S大气辐射传输模型的一个Python封装,装上之后调参数、跑模拟、读结果,比直接跟Fortran打交道舒服太多。但也正因为它是“封装”,很多人第一次运行就会卡在同一个错误上:6S executable not found。
这个错误严格来说不是pip install阶段弹出来的,而是你第一次真正调用SixS()构造器或者执行run()的时候,程序直接甩给你的。字面意思是“找不到6S可执行文件”,但实际坑比这句话多得多——你可能压根没编译过6S,也可能编译了但路径没配对,还可能编译器本身就没装对,导致连可执行文件都没生成。我见过不少人卡在这里几天,最后发现是“6S没有被编译”这个最简单的原因。
这篇文章我不绕弯子,直接按我自己的排查思路来写:先把Py6S和6S的协作机制讲清楚,再一步步带你完成源码获取、编译器准备、可执行文件编译、路径配置和最终验证。Linux、macOS、Windows三种常见场景我都会覆盖到,尤其是Windows,我会直接给出建议。
1. 一个“找不到”背后的三层原因:先搞懂Py6S和6S到底怎么协作
1.1 Py6S只是“翻译官”,不是“执行者”
6S模型本身是用Fortran写的,它接收的输入是格式非常严格的文本文件,输出也是格式化的文本。Py6S这个Python库做的事情,是帮你把这些输入文本按6S要求的格式拼好,提交给6S程序运行,再把6S的输出解析成Python对象。
用一句话概括:Py6S是翻译官,真正干活的是那个Fortran程序。因此,Py6S装好了,并不代表6S就能跑。它还缺一个“被调用”的对象——也就是编译好的6S可执行文件。在Linux和macOS上这个文件通常叫sixs,在Windows上叫sixs.exe,它是从6S的Fortran源代码编译出来的。
我看到太多初学者以为pip install py6s之后就算万事大吉,结果第一次创建SixS实例时就被报错打懵。实际上pip只是把翻译官请进了门,真正干活的“6S程序”必须自己准备。
1.2 报错是在哪里被抛出来的
用过的Py6S版本里,源码中有这么一段逻辑:当SixS对象初始化或者调用run()时,它会去查找6S可执行文件,查找顺序大致是:环境变量SIXS_EXECUTABLE、Py6S内部维护的目录变量、当前工作目录。这些路径都没找到有效文件,它就抛出一个RuntimeError,消息内容就是6S executable not found。
这个设计本身没问题,问题在于它不会明确告诉你“你没编译”还是“路径没配对”。所以遇到这个错,第一件事不是去瞎试路径,而是先确认:机器上到底有没有一个能用的sixs程序?如果有,它是在哪个路径?如果没有,问题根源就是编译这一步还没完成。
1.3 为什么很多教程都默认你已经有了6S可执行文件
我查过不少博客和文档,发现一个很普遍的现象:大部分教程一上来就直接写:
from Py6S import SixS s = SixS() s.run()然后默认一切正常。但很少有教程会说明,6S可执行文件是需要单独下载源码、手动编译的。这些教程默认读者是“已经编译过6S”的群体,新手一踩一个准。所以我这篇文章干脆从零开始讲,把编译和配置过程完整拆开。
2. 动手之前先备齐三样东西:源码包、Fortran编译器、正确的Python环境
2.1 6S源码哪里下,版本怎么选
6S源码的官方发布渠道是6S项目主页,下载最新版本源码压缩包即可。如果官方站点访问速度不理想,GitHub上也有镜像仓库,搜索“6S”能找到。下载后解压,你会看到一堆.f、.f90、.dat文件,以及README或Makefile。
这里有个关键点:源码包里的数据文件(那一堆.dat文件)和源代码文件一样重要。编译出来的可执行文件在运行时会读取同目录下的这些数据文件。也就是说,不能只把编译出来的sixs二进制拷到别的地方,数据文件必须和它待在一起。这一步很多人忽略,后面会引发各种稀奇古怪的运行时错误。
2.2 Fortran编译器选型:选gfortran准没错
6S是Fortran 77时代的老代码,目前最通用、最不容易出问题的编译器是gfortran。它开源、免费,在Linux和macOS上安装都很方便,兼容性也比一些商业编译器更省心。
- Linux Debian/Ubuntu系列:
sudo apt install gfortran- Linux CentOS/RHEL系列:
sudo yum install gcc-gfortran- macOS:
brew install gcc注意,macOS上执行brew install gcc会带上gfortran,不需要单独装gfortran包。如果你还没装Homebrew,先去把Homebrew装了,这一步不展开。
- Windows:我的建议是别在原生环境硬折腾,优先用WSL2。在WSL2的Ubuntu里装gfortran,然后走Linux流程。如果非要原生Windows,那就装MinGW-w64提供的gfortran,但后续路径、数据文件的问题会比Linux多不少,我后面会专门讲。
6S这种老代码用gfortran而不是ifort,还有一个现实原因:ifort的许可证获取和配置更繁琐,用它编译老Fortran代码也未必比gfortran顺手。对一个科研工具来说,不划算。
2.3 Python版本和Py6S版本的搭配建议
Py6S对Python 3的支持已经很成熟,直接用pip安装即可:
pip install py6s但如果你在conda环境里工作,我强烈建议建一个干净环境再装,避免numpy、scipy等依赖和已有环境冲突:
conda create -n py6s python=3.9 conda activate py6s pip install py6sPython版本不要太新。3.12甚至之后的版本,Py6S依赖的numpy/scipy在那些环境里偶尔会有编译安装问题。我实际用的是Python 3.9,跑得很稳。如果你在安装Py6S阶段就已经遇到一堆编译报错,多半是Python版本太新或缺少开发头文件,这种问题建议直接换成3.9环境重装,别浪费时间逐个解决。
3. 真正的重头戏:把6S源码编译成可执行文件
3.1 Linux编译:看起来只有一条make命令,实际上有几个隐藏点
在Linux下,进入解压后的6S源码目录:
cd 6S_V1.1 ls如果目录里有Makefile,理论上直接:
make就能生成sixs可执行文件。但有一个常见情况:机器上有多套编译工具链,或者环境变量FC没设对,导致make报一些Fortran匹配错误。这时候可以直接指定编译器再编:
make clean make FC=gfortran如果连Makefile都没有,或者make总是失败,那就直接手动编译所有源文件:
gfortran -O2 -o sixs *.f有些版本里源码是.f90,那就把两种都编译进去:
gfortran -O2 -o sixs *.f *.f90编译完成后,先做一个简单的自测:
./sixs如果程序马上提示你输入参数,或者打印一段用法说明,说明可执行文件没问题。6S的设计是从标准输入读参数,所以直接回车几下,它大概率会报“输入行错误”之类的话,这反而是好事,说明程序真的在运行。
然后检查一下数据文件是否齐全:
ls *.dat | wc -l如果数量是0,说明你下载的源码包不完整,需要重新下载完整压缩包。
3.2 macOS编译:常见报错和解决思路
macOS比Linux多两个门槛。
第一个门槛:执行make会提示找不到命令。这时先安装Xcode Command Line Tools:
xcode-select --install第二个门槛:macOS自带的clang编译器不认Fortran,所以必须确保gfortran已经可用。用Homebrew安装:
brew install gcc装好后进入源码目录编译:
cd 6S_V1.1 make FC=gfortran如果你遇到类似ld: library not found for -lgfortran的链接错误,通常是gfortran的库路径没被编译器找到。可以用which gfortran查一下安装位置,然后设置LIBRARY_PATH,但最省事的办法其实是:
brew reinstall gcc然后重新编译,基本能过。Apple Silicon(M1/M2/M3系列)上我也实测过,只要gfortran是arm64版本,编译没问题。
3.3 Windows编译:别在原生环境硬磕,WSL2是最省心的路
在Windows原生环境编译6S,理论上可行,但会碰到很多和路径、数据文件读取相关的坑。如果你要长期做遥感数据处理,我强烈建议用WSL2,在里面创建一个Ubuntu环境,然后完全按Linux流程来。优点显而易见:编译器环境干净、Py6S的运行环境和编译环境一致、后续再装GDAL等依赖也不会拖泥带水。
如果你就是想在Windows原生环境里编译,需要装MinGW-w64,把gfortran加入PATH,然后在CMD里进入源码目录:
cd /d C:\6S_V1.1 gfortran -O2 -o sixs.exe *.f编译成功后会生成sixs.exe,同样要保证所有.dat文件就在同一个目录。但后续Py6S在Windows下调用它,可能还会遇到路径分隔符和权限问题。所以我的态度很明确:能用WSL2就用WSL2,别跟这个老Fortran程序较劲。Windows上不是不能跑,而是你花在折腾环境上的时间,足够把数据跑完好几轮了。
3.4 编译完成后的三个自查点
- 可执行文件确实生成了吗?执行
ls -l sixs,文件大小一般是几十KB到几百KB。 - 数据文件是否和可执行文件在同一个目录?所有
.dat文件都要在。 - 命令行执行
./sixs有没有“程序能跑”的反馈?哪怕报参数错误,也算活着的程序。
4. 让Py6S找到6S:三种配置方式,任选一种即可
可执行文件已经就绪,接下来要让Py6S运行时能定位到它。我按推荐程度列出三种方式。
4.1 方式一:设置环境变量SIXS_EXECUTABLE(最推荐)
Py6S默认会读取环境变量SIXS_EXECUTABLE来定位可执行文件的完整路径。这是最不容易受Py6S版本升级影响的方案,也是我最推荐的方式。
Linux/macOS临时生效:
export SIXS_EXECUTABLE=/home/user/6S_V1.1/sixsWindows临时生效:
set SIXS_EXECUTABLE=C:\6S_V1.1\sixs.exe想要永久生效,Linux/macOS下把这行写到~/.bashrc或~/.zshrc里,Windows下在系统环境变量里新建。
配置完先检查变量是否写对:
echo $SIXS_EXECUTABLE注意一个细节:如果路径里有空格,建议把可执行文件先复制到一个无空格的目录,比如/usr/local/bin/sixs。老程序对带空格的路径处理非常不友好,这个坑在后面迟早会遇到。
4.2 方式二:直接修改Py6S源码里的默认路径
如果你不想每次开终端都设置环境变量,可以直接改Py6S包内部的配置。先找到Py6S的安装目录:
python -c "import Py6S, os; print(os.path.dirname(Py6S.__file__))"然后打开里面的sixs.py或__init__.py,不同版本位置略有差异,搜索sixs_directory和sixs_executable这两个变量。它们就是控制可执行文件位置的关键:
sixs_directory = '/home/user/6S_V1.1/' sixs_executable = 'sixs'改成你自己的路径,保存后重新测试。这个方案的好处是一次配置,永久生效;缺点是一旦升级Py6S,改动可能会被覆盖,需要重新修改。所以我在自己机器上更倾向于环境变量方案。
4.3 方式三:在Python脚本里动态指定
如果你只想临时跑个脚本,不想动系统配置,可以在导入Py6S之前把环境变量放进os.environ:
import os os.environ["SIXS_EXECUTABLE"] = "/home/user/6S_V1.1/sixs" from Py6S import SixS s = SixS() s.run()这里有个非常重要的经验:设置环境变量的语句一定放在from Py6S import SixS之前。我之前一个同事死活找不到可执行文件,排查到最后发现,他把os.environ那句写在了import之后。Py6S在导入阶段就已经读取过环境变量,之后再写当然没用。
4.4 配置完怎么验证:别急着跑大模型
配置完成后的最快验证方法:
from Py6S import SixS s = SixS() print("created successfully")如果能打印出created successfully,说明Py6S已经找到6S可执行文件。接下来跑一个最小的计算流程:
s.run() print(s.outputs.pixel_radiance)这一步会真正触发6S计算。如果前面配置有问题,异常会在run()时抛出,而不是构造时。
还有一个更直接的排查方式,在Python里打印Py6S内部的查找路径,然后确认文件是否存在:
from Py6S import sixs import os path = sixs.sixs_directory + sixs.sixs_executable print(path) print(os.path.exists(path))如果结果是False,说明配置仍然没指向真实文件,回到上面三种方式重新检查。
5. 跑通之后才刚开始:最小示例和真实使用中的注意点
5.1 最小示例:算一组大气参数
配置完成后,我常用下面这段代码来验证整个环境:
from Py6S import SixS from Py6S.Params import AtmosProfile, AeroProfile, Wavelength s = SixS() # 使用热带大气剖面 s.atmos_profile = AtmosProfile.PredefinedType(AtmosProfile.Tropical) # 使用大陆型气溶胶模型,设置550nm处的AOD为0.3 s.aero_profile = AeroProfile.PredefinedType(AeroProfile.Continental) s.aot550 = 0.3 # 设置波长、太阳天顶角、卫星天顶角 s.wavelength = Wavelength(0.55) s.solar_z = 30 s.satellite_z = 10 # 运行6S s.run() # 查看结果 print(s.outputs.pixel_radiance) print(s.outputs.transmittance_atmospheric)能打印出数值,就说明整条链路是通的。
5.2 真实场景里容易忽略的事:数据文件与运行目录
前面反复强调数据文件,这里再具体说一下。6S运行时不只是需要那个可执行文件,它还需要读取很多地表和大气参数表,这些表格就是源码包里的.dat数据文件。如果你把sixs单独复制到/usr/local/bin,却把数据文件漏了,Py6S运行时会报一些看起来和路径无关的错误,比如找不到某个大气剖面、输出结果异常,甚至直接崩溃。
解决方式很土但很稳:直接在源码目录里调用它。只要SIXS_EXECUTABLE指向源码目录中的sixs,数据文件就在它旁边,整个运行过程就不会有问题。这也是为什么我习惯把6S整个源码目录固定放在~/tools/6S_V1.1,而不是只拷贝二进制。
5.3 批量处理影像时的建议
做批量遥感影像大气校正时,很多人的第一反应是循环里反复创建、销毁SixS实例。这个做法虽然可行,但每次创建都会做很多初始化工作,浪费不少时间。
我自己的习惯是:如果只是针对不同波长或不同角度循环,就先把SixS对象创建出来,循环里只改对应参数,然后调用run();如果必须用多进程加速,再考虑创建多个进程副本运行。这个小技巧不复杂,但在大量数据场景下能省下不少时间。
6. 我在实际使用中踩过的坑和排查思路
这一节专门记录高频但零散的问题,每条都是我实际碰到过或者帮别人排查过的。
6.1 明明编译出了sixs,Py6S还是报not found
先别急着重新编译,按下面顺序检查:
- 在终端执行
which sixs,如果命令能显示路径,说明可执行文件在PATH里,但Py6S不一定走PATH,它更信任环境变量或包内路径。 - 确认
SIXS_EXECUTABLE指向的是可执行文件本身,而不是所在目录。这个细节特别容易搞错,有人把路径写成目录,Py6S去拼成“目录/sixs”,自然找不到。
6.2 run()报错变成nonzero return code
如果你已经越过了not found,却在run()时看到类似return code:nonzero的报错,绝大多数情况是6S程序本身运行失败了。常见原因有三个:
- 数据文件没配对,也就是
.dat文件缺失或不在当前目录。 - 输入参数超出6S的合理范围,比如太阳天顶角为负数、波段定义不合法。
- 工作目录不可写,6S需要生成临时文件,当前目录没权限就会失败。
处理办法:手动在命令行里./sixs输入一份和Py6S类似的参数,看6S到底抱怨什么。或者写一段代码捕获6S的标准输出,把实际运行信息打出来,问题基本就显形了。
6.3 Windows路径空格和中文字符
Windows下如果把6S放在C:\Users\张三\6S Model\这种路径下,大概率会出幺蛾子,包括not found和运行时崩溃。解决方案只有一个:直接放到C:\6S\这种纯英文、无空格、层级浅的目录下。这能解决掉一大半Windows相关的问题。
6.4 升级Py6S后突然坏了
Py6S版本偶尔会调整内部调用方式,升级后有可能改变配置变量名或默认行为。遇到这种情况,先想想自己原来的配置方式是什么:
- 如果依赖环境变量
SIXS_EXECUTABLE,升级后通常最稳。 - 如果之前改了包内源码,升级后改动会被覆盖,自然回到找不到可执行文件的“出厂状态”。
处理方法是重新改一次包内配置,或者索性切到环境变量方案,一劳永逸。
6.5 快速定位问题的最小化复现法
当你被一连串报错绕晕时,我建议做一个最小化复现。只设置环境变量,写一个五行以内的脚本,跑SixS().run():
import os os.environ["SIXS_EXECUTABLE"] = "/path/to/sixs" from Py6S import SixS s = SixS() s.run()如果这个能过,说明环境是好的,问题出在你的业务代码;如果连这个都过不了,说明就是6S可执行文件或配置的问题。二分法定位,永远比瞎猜效率高。
最后再分享一个我亲测有效的习惯:刚换新电脑配Py6S时,我会把整个6S源码目录连同数据文件一起归档到固定的~/tools/6S_V1.1,然后在~/.bashrc里写死export SIXS_EXECUTABLE=~/tools/6S_V1.1/sixs。这样每次换环境、换Python版本,都不用重新记路径。真遇到问题,就从最小复现开始查。
这个错误的本质其实一点也不复杂——Py6S只是个翻译官,你要确保那个真正干活的Fortran程序存在、能运行、并且被正确指过去。把这三个问题逐一确认过,6S executable not found基本就能彻底告别了。