在配置 MNE 环境的过程中,遇到了一些技术难题。虽然最终找到了有效的解决方案,但整个过程满了挑战。
为此,总结了几条经验与解决思路,旨在为遇到类似问题的人提供一些参考和帮助。
第一个问题:版本不兼容
当运行 MNE 时,控制台输出了以下错误信息:
libGL error: MESA-LOADER: failed to open swrast: /data/anaconda3/envs/mne/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /lib/x86_64-linux-gnu/libLLVM-15.so.1) (search paths /usr/lib/x86_64-linux-gnu/dri:\$${ORIGIN}/dri:/usr/lib/dri, suffix _dri)
libGL error: failed to load driver: swrast
2024-12-23 17:08:47.630 (0.385s) [731BF04A7740] vtkXOpenGLRenderWindow.:651 ERR| vtkXOpenGLRenderWindow (0x5e868dbf4cf0): Cannot create GLX context. Aborting.
ERROR:root:Cannot create GLX context. Aborting.
从错误信息来看,问题出在 OpenGL 相关的库文件,特别是 libstdc++.so.6
版本不兼容,导致无法加载图形驱动,从而无法创建图形上下文。
解决方案
1. 删除 Anaconda 环境中的旧版 libstdc++.so.6
首先,需要删除 Anaconda 环境中自带的较旧版本的 libstdc++.so.6
文件:
rm /home/xx/anaconda3/bin/../lib/libstdc++.so.6
原因:Anaconda 自带的 libstdc++.so.6
版本较旧,缺少支持 GLIBCXX_3.4.30
的符号。删除这个旧版本是为了避免它影响其他库的加载。
风险提示:如果操作不当,或后续替换库版本出现问题,可能会导致 Anaconda 环境中的其他问题。
2. 从系统中复制新版 libstdc++.so.6
到 Anaconda 环境
接着,从系统中的 libstdc++.so.6.0.29
文件复制到了 Anaconda 环境中:
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.29 /home/xx/anaconda3/bin/../lib
原因:系统中的新版 libstdc++.so.6.0.29
支持较新的 GLIBCXX
版本(如 GLIBCXX_3.4.30
),这是 MNE 所需要的版本。通过将其复制到 Anaconda 环境中,可以使 Anaconda 正常使用新版库。
3. 创建符号链接
然后,创建了一个符号链接,让 Anaconda 环境指向正确的版本:
ln -s /home/xx/anaconda3/bin/../lib/libstdc++.so.6.0.29 /home/xx/anaconda3/bin/../lib/libstdc++.so.6
原因:动态链接器通常查找 libstdc++.so.6
这样的符号链接,而不是查找具体版本的文件(如 libstdc++.so.6.0.29
)。创建符号链接后,动态链接器就能够正确找到新版库。
第二个问题:可视化结果闪退
在解决了第一个问题后,还遇到了第二个问题:MNE 的可视化窗口在打开时闪退,但没有报错。
原始简化代码:
import mne
from mne import read_source_estimate
from mne.datasets import sample
# 打印文档
print(__doc__)
# 获取示例数据路径
sample_dir_raw = sample.data_path()
sample_dir = sample_dir_raw / "MEG" / "sample"
subjects_dir = sample_dir_raw / "subjects"
fname_stc = sample_dir / "sample_audvis-meg"
# 加载 SourceEstimate 数据
stc = read_source_estimate(fname_stc, subject="sample")
# 定义绘图参数
surfer_kwargs = dict(
hemi="lh", # 左半球
subjects_dir=subjects_dir,
clim=dict(kind="value", lims=[8, 12, 15]), # 颜色限制
views="lateral", # 横向视图
initial_time=0.09, # 初始时间
time_unit="s", # 时间单位为秒
size=(800, 800), # 窗口大小
smoothing_steps=5, # 平滑步骤
)
# 绘制表面图像
brain = stc.plot(**surfer_kwargs)
# 添加标题
brain.add_text(0.1, 0.9, "SourceEstimate", "title", font_size=16)
# 获取峰值顶点和时间点
peak_vertex, peak_time = stc.get_peak(hemi="lh", vert_as_index=True, time_as_index=True)
# 找到峰值顶点对应的表面位置
peak_vertex_surf = stc.lh_vertno[peak_vertex]
# 添加峰值点(使用蓝色标记)
brain.add_foci(peak_vertex_surf, coords_as_verts=True, hemi="lh", color="blue")
# 添加峰值信息为标题
# brain.add_text(0.1, 0.9, f"Peak coordinate: {peak_vertex_surf}, Amplitude: {stc.lh_data[peak_vertex, peak_time]:.2f}", "title", font_size=14)
解决方案:启动事件循环
MNE 的可视化功能依赖于 PyQt 等 GUI 库,而这些库需要事件循环来保持图形界面的交互与刷新。若没有启动事件循环,图形界面将无法响应用户操作,可能导致闪退或无响应。为了确保可视化窗口正常显示并保持响应,我在代码中加入了启动事件循环的逻辑,代码修改如下:
import mne
from mne import read_source_estimate
from mne.datasets import sample
from PyQt5.QtWidgets import QApplication
# 打印文档
print(__doc__)
# 获取示例数据路径
sample_dir_raw = sample.data_path()
sample_dir = sample_dir_raw / "MEG" / "sample"
subjects_dir = sample_dir_raw / "subjects"
fname_stc = sample_dir / "sample_audvis-meg"
# 加载 SourceEstimate 数据
stc = read_source_estimate(fname_stc, subject="sample")
# 定义绘图参数
surfer_kwargs = dict(
hemi="lh", # 左半球
subjects_dir=subjects_dir,
clim=dict(kind="value", lims=[8, 12, 15]), # 颜色限制
views="lateral", # 横向视图
initial_time=0.09, # 初始时间
time_unit="s", # 时间单位为秒
size=(800, 800), # 窗口大小
smoothing_steps=5, # 平滑步骤
)
# 启动 PyQt 应用程序
app = QApplication([]) # 创建应用程序实例
brain = stc.plot(**surfer_kwargs) # 绘制脑图
# 添加标题
brain.add_text(0.1, 0.9, "SourceEstimate", "title", font_size=16)
# 获取峰值顶点和时间点
peak_vertex, peak_time = stc.get_peak(hemi="lh", vert_as_index=True, time_as_index=True)
# 找到峰值顶点对应的表面位置
peak_vertex_surf = stc.lh_vertno[peak_vertex]
# 添加峰值点(使用蓝色标记)
brain.add_foci(peak_vertex_surf, coords_as_verts=True, hemi="lh", color="blue")
# 启动事件循环,等待用户交互
app.exec_() # 启动Qt事件循环,直到用户关闭图形界面
通过添加 app.exec_()
,程序开始进入事件循环,直到用户关闭图形界面,问题顺利解决。
原理总结
问题 1:libstdc++.so.6
版本不匹配导致的 OpenGL 错误
问题是什么?
你遇到的错误提示显示,MNE 在运行时因为一个叫 libstdc++.so.6
的文件版本不对,导致 OpenGL 驱动加载失败,进而无法显示图形界面。libstdc++.so.6
其实是一个 C++ 标准库的文件,它负责一些基本的计算和数据处理。你的环境里有个较旧的版本,而 OpenGL(图形绘制库)需要的是一个更新的版本。
简单来说:就是因为你的环境里用了一个比较旧的库,它没有一些新的功能,导致无法加载图形界面。
解决方法的原理是什么?
- 删除旧版
libstdc++.so.6
: 这个文件是一个标准的 C++ 库文件,Anaconda 自带了一个版本,但这个版本可能不支持一些新的功能,导致出错。所以第一步是删除掉它,避免它影响其他的库。 - 从系统中复制一个新版的
libstdc++.so.6
: 系统(不是 Anaconda 环境)中有一个较新的版本,支持更多的新功能。你把这个版本复制到 Anaconda 环境里,替换掉原来过时的版本,这样就能解决兼容性问题。 - 创建符号链接: 这里的“符号链接”其实是个快捷方式,类似于电脑中的快捷方式。因为 Anaconda 环境需要的文件是
libstdc++.so.6
,但这个文件的真实名字可能是libstdc++.so.6.0.29
。通过创建符号链接,程序就能找到正确的文件版本,从而正常工作。
总结:通过用新版的库替换掉旧版库,解决了不兼容的问题,让 OpenGL 能够加载,图形界面才能显示出来。
问题 2:MNE 可视化闪退(代码无报错)
问题是什么?
当你运行 MNE 进行图形可视化时,虽然代码没有报错,但显示的图形界面窗口一打开就闪退了。这种情况通常发生在使用图形界面库(比如 PyQt)时,程序没有正确地进入“事件循环”状态。
简单来说:程序没能正确进入等待用户操作的状态,所以图形界面窗口很快就关闭了。
解决方法的原理是什么?
-
事件循环的作用: 现代的图形界面程序通常需要一个“事件循环”,它是一个持续运行的过程,负责监听用户的输入(比如点击按钮、关闭窗口等),并根据输入做出相应的反应。没有事件循环,程序会直接执行完毕,窗口很快就会关闭。
-
app.exec_()
的作用: 在 PyQt 中,QApplication
对象负责管理整个应用的事件循环。通过调用app.exec_()
,程序会进入一个循环状态,持续等待用户的操作,直到你关闭窗口为止。你可以把这个过程想象成:你打开一个音乐播放器,点击播放,播放器会一直在后台“播放”音乐,直到你点击“暂停”或“关闭”。如果没有事件循环,播放器就不会开始播放,窗口也不会显示出来。
-
解决方案: 只需要在代码中加上
app.exec_()
,程序就会进入事件循环,图形界面窗口就能够正常显示,并且等待你进行交互(点击、关闭等)。
总结:事件循环就像是一个“守门员”,它确保程序能够一直运行,等待你进行交互。通过加上 app.exec_()
,程序就能进入这个“等待”状态,避免了闪退的问题。
解决思路
- 确认问题和错误信息
首先,要清楚问题的症状是什么。比如,遇到无法加载图形库、版本不匹配或依赖问题时,错误信息通常会给出提示。弄清楚是哪一部分出错,是驱动、库版本还是环境配置。
- 收集信息和寻求初步帮助
当自己无法解决时,先通过 社区资源(如 GitHub 讨论、官方文档、论坛等)或工具(如 GPT)寻找常见的解决方案。社区的集体智慧往往能提供较多思路。此时不要跳过任何可能的基础方案,比如驱动更新、库的版本兼容等。
- 环境隔离与逐步排查
如果问题比较复杂,可以通过 创建多个虚拟环境 进行隔离测试。这样做有两个好处:
- 可以确保不会有其他环境中的干扰,做到"干净"调试;
- 逐步排查不同库版本的兼容性问题,确定哪一部分是罪魁祸首。
- 文档与官方配置核对
在初步尝试无果后,对照官方文档和 requirements.txt
进行细致检查,确保环境配置没有遗漏。很多时候,问题可能是因为某些小的版本或配置差异引起的,所以要严格按照官方推荐的方式设置。
- 深度搜索与定位具体解决方案
如果常规方法都没有效果,可以通过 深入搜索 查找更专业的解决方案。通过 GitHub、Stack Overflow 或特定讨论平台,查找别人是否遇到过类似问题,看看有没有更具体的解决方法。
标签:libstd,遇到,配置,c++,sample,peak,so.6,MNE,dir From: https://blog.csdn.net/weixin_45638136/article/details/145013897