news 2026/10/4 1:17:53

NeRF三维重建实战:从手机拍摄到模型导出的全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NeRF三维重建实战:从手机拍摄到模型导出的全流程解析

简介:面向三维重建与神经辐射场方向的学习者,这份Python源码项目将NeRF算法与手机拍摄的物体照片相结合,完整覆盖从多视角图像输入、特征点匹配、相机位姿估计,到神经辐射场训练、新视角渲染的全流程,可用于毕业设计、课程项目或算法入门实践。压缩包共31个文件,包含21个Python脚本、5个缓存文件、2个文本说明、1份Markdown文档、1张示例图片及1个演示动图,整套资源仅5.37MB,轻量且便于快速部署。源码采用模块化设计,训练与渲染、网络结构、损失函数、数据加载、路径生成与可视化等职责分离,同时提供COLMAP工具链及配套真实拍摄数据集,内置测试配置和球面路径渲染示例,解压后即可按说明文档复现重建结果。目前已有181人学习,项目经过本地编译验证,评审得分95分以上,助教审定通过,难度适中,对希望动手实现NeRF三维重建的读者具有直接参考价值。

1. 手机拍一圈图,NeRF 就能出三维模型:这项目值得你亲手跑一遍

用手机围着物体拍一圈照片,交给 NeRF 训练,就能得到一个可以自由旋转、任意角度查看的三维模型——这是近两年三维重建领域热度最高的落地方向之一,也是很多高校课程设计和工程岗面试的常客。标题里的这个 python 源码项目,本质就是一条“手机图片 + NeRF + 三维重建”的完整数据流:你负责拍,COLMAP 负责算相机位姿,NeRF 负责把照片蒸馏成隐式场景权重,最后导出网格放进 Blender 或 Unity 用。适合三类人:想低成本做商品展示的开发者、做三维视觉课题的学生,以及刚入门找不到完整数据流的小白。它的难点不在算法理论,而在数据采集和参数调试——数据集质量直接决定重建上限,下面按这条主线拆。

2. 先搞懂 NeRF 在做什么:隐式场景表示与手机输入的适配点

2.1 NeRF 的三维重建本质:从多视角照片回归连续辐射场

NeRF 全称 Neural Radiance Fields,核心思想是用一个多层感知机(MLP)直接表示整个场景。输入是三维空间点坐标 p=(x,y,z) 和观察方向 d=(θ,φ),输出是颜色 c=(r,g,b) 和体密度 σ。同一个点从不同方向看颜色应该不同,否则高光和视角相关的反光没法表达,所以观察方向必须作为输入;而密度只和位置有关,和观察方向无关。把大量照片喂进去训练后,网络权重本身就成了“场景的数字孪生”,不需要再额外存储模型和贴图。

真正让 NeRF 成立的关键是把“这个点是否可见”转换成体渲染。每个像素对应一条从相机中心射出的光线,沿光线均匀采样 N 个点,把每个点的颜色和密度都查出来,从近到远累积颜色。越靠前的点遮挡权重越大,后面的点被前面密度挡住后贡献迅速衰减。这个物理模型直接决定了重建几何的精度上限,也是后续所有参数调节的根本依据。

def render_ray(net, ray_o, ray_d, t_near, t_far, n_samples): # 在 [t_near, t_far] 深度范围内做均匀采样 t_vals = torch.linspace(t_near, t_far, n_samples) # 采样点坐标 = 光线起点 + 深度 * 方向 pts = ray_o[:, None] + t_vals[None, :] * ray_d[:, None] # 网络对每个采样点输出颜色和体密度 rgb, sigma = net(pts, ray_d.expand_as(pts)) # 相邻采样点的间距,用于把密度转成遮挡率 delta = t_vals[1:] - t_vals[:-1] alpha = 1 - torch.exp(-sigma * delta) # 不透明度 trans = torch.cumprod(1 - alpha, dim=0) # 累积透射率 weights = trans * alpha # 每个点的颜色贡献权重 color = (rgb * weights[:, None]).sum(dim=0) # 体渲染合成颜色 return color, weights

这里的 net 就是训练好的 MLP。t_vals 采样越密,几何细节越细腻,但显存和耗时同步上涨。weights 从近到远递减,正是体渲染区别于普通 Alpha Blending 的地方;alpha 表示该点对光线的“拦截率”,cumprod 累乘表示前面所有点都没挡住时,光线还能到达当前点的概率。很多初学者直接把这里改成等间距线性采样跑出来效果差,就是因为没有分层采样导致近景过采样、远景欠采样。

还有一个小细节容易被忽略:直接把 x,y,z 喂给 MLP,高频几何细节出不来,源码里一般会用 positional encoding 把输入升维到高频空间。这部分代码看起来像“先做三角函数变换再拼接”,但它决定了杯口边缘、布料褶皱这类细节能不能被重建出来。对手机拍摄的物体尤其重要,因为物体近、纹理尺度小,高频信息全靠这个编码和足够的采样密度兜住。

2.2 为什么手机拍摄的图片能做输入:COLMAP 位姿估计与尺度问题

NeRF 的训练样本是“图片 + 对应的相机位姿”。手机照片只有像素,没有位姿,怎么喂?常见做法是先做运动恢复结构(SfM),用 COLMAP 跑一遍稀疏重建:它对所有照片提取特征并做匹配,当两张图有足够多的匹配点时,就能算出相对旋转和平移;把整组照片连接起来后,再用光束法平差优化出每一张照片的外参,同时估计相机内参。这个过程的输出不仅仅是相机位姿,还包括一个稀疏点云。

这就带来一个有点反直觉的结论:手机拍摄能不能做 NeRF,看的不是算法能力,而是照片之间有没有足够多的共视特征。纹理多的表面(木纹、绒布、键盘、包装盒上的字)会让匹配非常好做;纯白模具、镜面不锈钢、透明玻璃,就算拍几百张,COLMAP 也可能只有十几张能注册,剩下的照片没有位姿可用。

另一个容易忽略的点是尺度。SfM 恢复出来的场景是任意尺度的,没有物理上的厘米关系,所以 NeRF 的输出尺寸只有相对值。拿真实物体做重建,导出网格后想 3D 打印或测量尺寸,必须自己拿实物量一个基准长度去缩放。这是很多小白第一次导出模型后问“尺寸怎么不对”的原因,不是项目的问题,是流程里本来就需要这一步。

2.3 源码包结构:解压后先看哪三个文件

拿到一个标着“高分项目”的源码包,不要急着全量训练。这类 NeRF 项目的源码结构通常很相似,先看 README、configs 下的配置文件、以及 datasets 目录下的数据加载器即可。你不需要一次看完所有代码,只需要找三个问题的答案:数据格式是什么,训练入口在哪,超参在哪个文件。

nerf-phone-3d/ ├── configs/ │ └── phone.yaml # 训练超参:分辨率、采样数、迭代次数 ├── datasets/ │ └── colmap_loader.py # 数据加载器,决定数据目录结构 ├── models/ │ └── nerf.py # NeRF 网络结构 ├── tools/ │ ├── imgs2poses.py # 调 COLMAP 生成相机位姿 │ └── export_mesh.py # 导出 OBJ/PLY ├── run_train.py # 训练入口 └── data/ # 数据集:通常包含 images/ 和 sparse/

这个结构在 NeRF 相关开源项目里几乎是标准布局。我习惯先看 phone.yaml 里的 image_size、n_iters、batch_size,再看 colmap_loader.py 有没有对图片文件名强依赖;最后用一个 20 张图的 mini 场景验证端到端链路,而不是上来就全量训练。这个习惯帮我避开了很多“训练很久之后才发现 pose 对齐错了”的翻车现场,因为数据链路的问题越早暴露成本越低。

3. 用手机拍出合格的数据集:采集规范与校验步骤

3.1 手机采集的七条硬规矩

手机拍的图能不能用,直接决定 NeRF 训练是从生到熟还是从生到死。先说七条我踩过坑之后固定下来的规矩。第一条,物体不动、手机绕物体转,保持同一高度围绕一圈,拍 150 到 300 张;把物体放在转台上拍通常是行不通的,原因在后面的避坑章节展开。第二条,锁定曝光和对焦,iPhone 长按屏幕锁 AE/AF,Android 在专业模式里同样锁,允许画面变暗也不能让连续两帧曝光跳变。

第三条,光源尽量均匀,避开硬光直射产生的镜面反光和高光。第四条,物体表面要有纹理,纯色、无图案、光滑反光的东西对 COLMAP 是灾难。第五条,镜头离物体尽量近,同时确保物体完整出现在每张画面里,不裁切。第六条,手持拍摄可以,但按快门时停顿一下防止运动模糊,快门速度尽量不低于 1/100 秒。第七条,背景别太杂乱也别太单调,有清晰纹理的墙面最好,纯白墙会让背景区域的位姿估计产生歧义。

注意:不是所有手机都适合直接拍摄。开启超分、夜景、美颜等增强模式会改变图像的像素关系,导致特征匹配偏移,建议用默认相机模式关闭所有 AI 增强。

3.2 从视频抽帧还是连拍:两种方式的取舍

手机拍一圈近景,人手很难稳定匀速地走完 200 张,所以我更推荐拍一段一到两分钟的视频再抽帧。优点有两个:帧间位置连续,位姿估计不会出现莫名跳变;快门时间固定,曝光变化小。缺点也很明显,视频帧通常有轻微运动模糊,尤其手抖时明显。连拍则每张都是独立对焦和曝光,清晰度好,但 200 张连续按下来手指和稳定度都是折磨,动态范围也容易跳。

采集方式优点缺点适用场景
视频抽帧位姿平滑、曝光稳定、效率高容易有运动模糊手持绕拍、物体静止
连拍画质高、对焦准曝光易跳变、效率低有转台或固定轨道时

我一般用手机 4K 或 1080p 的 60fps 拍一圈 60 到 90 秒,回来抽帧取 200 到 300 张。注意抽帧时不要把相邻帧全抽出来,两个视角太近对 SfM 没有增益,反而增加匹配时间。

# 把手机视频每 0.5 秒抽一帧,按 4 位数字编号保存到 frames/ ffmpeg -i IMG_2035.MOV -vf "fps=2" -q:v 2 frames/%04d.jpg

fps=2 表示每秒抽 2 帧,90 秒视频大约抽 180 张,这个密度对一般物体足够。q:v 2 是 jpg 压缩质量,数值越小质量越高,2 到 3 足够 NeRF 使用。输出文件名 %04d.jpg 会让 ffmpeg 自动补零命名,方便后续按顺序加载。抽完后先挑出模糊帧删掉,经过压缩的视频帧稍微抖动就容易糊,懒这一步会直接拉低整个重建的锐度。

3.3 用 COLMAP 跑稀疏重建:拿到位姿与相机参数

图片准备好后,下一步是让 COLMAP 输出每张照片的相机位姿。源码包里一般都会带调用脚本,但我建议手动跑一遍,因为脚本报错时你得知道是哪一步失败。整个过程分成特征提取、特征匹配、增量重建三个部分。

colmap feature_extractor \ --database_path data/phone_obj/db.db \ --image_path data/phone_obj/images colmap exhaustive_matcher \ --database_path data/phone_obj/db.db colmap mapper \ --database_path data/phone_obj/db.db \ --image_path data/phone_obj/images \ --output_path data/phone_obj/sparse colmap model_converter \ --input_path data/phone_obj/sparse/0 \ --output_path data/phone_obj/sparse/0/ \ --output_type .txt

feature_extractor 提取 SIFT 特征;exhaustive_matcher 做两两匹配,几百张图的中小场景完全跑得动;mapper 是真正的增量重建,输出到 sparse 目录;model_converter 把二进制模型转成 images.txt、cameras.txt 这种文本格式,很多 NeRF 源码的位姿解析器只认文本格式。跑完立刻看终端里的注册图像数量,如果注册率低于六成,别急着训练,回去补拍或者调整拍摄角度才更省时间。

确认位姿质量还有一个更直接的检查方法:把 sparse/0 里的 images.txt 打开,每行对应一张图。一张正常绕拍的数据集,相机平移向量应该平滑变化;如果出现大角度跳变,说明照片排序有误或采集轨迹有断点。重投影误差也可以参考,COLMAP 默认会输出平均误差,一般小于 1 像素就可以接受,大于 2 像素说明有部分图片没对齐,需要删除后重新 mapper。

4. 跑通训练流程:配置环境、解析位姿到导出模型

4.1 依赖环境搭建:先看 requirements.txt 再装

NeRF 训练要吃 GPU,环境配置是新手最容易被卡住的地方。原则是先读源码包里的 requirements.txt 或 environment.yml,再按里面锁定的版本装。PyTorch 和 CUDA 的版本必须和显卡驱动匹配,盲目装最新版反而容易踩坑。

conda create -n nerf python=3.10 -y conda activate nerf pip install -r requirements.txt

requirements 里一般会有 numpy、opencv-python、tqdm、torch 等基础项。如果源码包依赖 NVIDIA 的 tiny-cuda-nn,那个扩展需要从源码编译,Windows 下尤其容易缺 Visual Studio 生成工具。没有编译环境时,优先找项目 README 里指定的预编译 wheel,或者在 Linux 机器上跑。环境问题在 NeRF 项目里占掉新手七成的时间并不夸张,那个安装报错经常要折腾一下午,但它和算法本身没关系,耐心逐步排查即可。

4.2 数据组织:把 COLMAP 位姿转成项目要求的格式

训练入口只关心一件事:某张照片对应哪个相机位姿。不同项目有三种主流格式:LLFF 的 poses_bounds.npy、Instant-NGP 的 transforms.json、原版 NeRF 的单个 txt。这里以 transforms.json 为例,它把每张照片的位姿矩阵、内参焦距和文件路径放在一个 JSON 字典里。源码包里一般有转换脚本,但需要自己拼时,核心逻辑是把 COLMAP 的 cameras.txt 和 images.txt 读出来后统一组合。

import json import numpy as np frames = [] for line in images_txt: # COLMAP images.txt 每张照片一行: # 相机编号 四元数(x y z w) 平移(x y z) 文件名 parts = line.split() if line.startswith("#") or len(parts) != 10: continue _, qx, qy, qz, qw, tx, ty, tz, name, _ = parts q = np.array([float(qw), float(qx), float(qy), float(qz)]) t = np.array([float(tx), float(ty), float(tz)]) rot = quaternion_to_matrix(q) # 四元数转旋转矩阵 c2w = np.eye(4) c2w[:3, :3] = rot c2w[:3, 3] = t frames.append({ "file_path": "images/" + name, "transform_matrix": c2w.tolist() }) with open("transforms.json", "w") as f: json.dump({"frames": frames}, f, indent=2)

这段代码最容易错的是四元数顺序:COLMAP 的 images.txt 里顺序是 qx qy qz qw,很多转换脚本写成 qw qx qy qz,导致位姿旋转错 90 度,训练出的新视角满屏重影。transform_matrix 是相机到世界坐标系的变换,NeRF 的加载器默认读它直接做光线采样。转换完抽查第一张、中间一张、最后一张的 c2w 平移向量,应该构成一段绕着物体转的弧线,而不是乱跳。

4.3 启动训练:超参数要看这五个

数据就绪后训练就很无脑了。项目一般提供一个配置文件,执行入口通常长这样:

python run_train.py --config configs/phone_obj.yaml

训练期间盯住五个参数:n_iters 迭代步数、batch_size 随机采样的光线数、image_size 训练分辨率、lr 初始学习率、n_samples 每条光线采样点数。手机照片场景我常用的配置如下:

image_size: 800 n_iters: 30000 batch_size: 1024 lr: 5e-4 n_samples: 128

image_size 不是越大越好,手机原图往往 4000 像素,NeRF 全分辨率训练显存直接爆掉。工程做法是先用 800 到 1000 分辨率跑通端到端流程,确认没问题后用原图做微调。n_iters 30000 在单张 3090 上大约 20 到 40 分钟。训练十来分钟后 loss 降到不再降但 PSNR 还在 20dB 左右徘徊,多半是位姿没对齐,不是训练不到位。插一句:NeRF 的超参有不少“玄学”成分,跑通后再调参比一开始就追求最优配置明智得多。

4.4 网格导出:用 marching cubes 提取表面并输出 OBJ

训练完的 NeRF 是隐式场,没法直接放进三维软件,最后通常要导出网格。常见做法是在场景包围盒内均匀采样一个 N×N×N 密度网格,调用 marching cubes 算法在等值面处抽取三角面片。

from skimage.measure import marching_cubes import trimesh # 在 [-1, 1] 立方体里采样 256^3 个点的密度值 density_grid = sample_density(net, N=256, bbox=(-1, 1)) verts, faces, normals, values = marching_cubes( density_grid, level=10.0, spacing=(2 / 256,) * 3 ) mesh = trimesh.Trimesh( vertices=verts, faces=faces, vertex_normals=normals ) mesh.export("mesh.obj")

sample_density 表示调用训练好的网络,把空间点坐标输入并取密度通道输出。marching_cubes 的 level 是密度阈值:设大了表面收缩、物体变瘦,设小了会把背景和训练误差带进来的杂散密度一起包进网格。我习惯导出三到四个 level 值的网格对比,选边缘最干净的那个。导出前把包围盒根据数据集的 near、far 设得紧凑一些,能显著减少背景拉花。

5. 避坑指南:手机 NeRF 重建里最常见的 5 个翻车现场

5.1 现象:重建出来是糊成一团的“雾”

这是刚入门时最常遇到的结果。训练日志显示 loss 在降,但渲染图曝光偏白、结构完全看不出来。原因几乎可以锁定为 COLMAP 注册率太低或位姿分布太差:手机围绕物体只转了半圈,新视角没有足够信息去推断,NeRF 只能给所有空间点糊上均匀的低密度雾。解决分两步:先用 model_converter 的输出确认 sparse/0 里注册的图像数量,注册比例不到七成就回去补拍;如果注册率高但训练完还是雾,把近远裁剪面 near、far 收紧,我早期用宽松的 near 也翻过车。雾状输出还有一个来源是背景区域曝光过度,导致密度场在背景处积压,适当裁剪图片再训练会好很多。

5.2 现象:训练很快但渲染视角完全错乱

日志里 PSNR 看起来正常,但渲染视角一帧对不上、图像撕裂。这多半不是网络问题,而是数据加载器拿到的位姿和照片文件名顺序不匹配。最常见的原因就是四元数顺序写错,或者用脚本整理照片时重命名了文件,而 transforms.json 里的 file_path 还是旧名。排查方式是用代码检查位姿轨迹是否平滑:

import json import numpy as np data = json.load(open("transforms.json")) tracks = [] for f in data["frames"]: m = np.array(f["transform_matrix"]) tracks.append(m[:3, 3]) # 相机中心 tracks = np.array(tracks) diff = np.linalg.norm(tracks[1:] - tracks[:-1], axis=1) print("max diff:", diff.max(), "mean diff:", diff.mean())

相邻帧平移量应该接近。max 值若超过 0.5 而 mean 在 0.05 左右,就是某一帧的数据张冠李戴。解决方法是把文件名按拍摄顺序重新排序,保证 transforms.json 的帧顺序和图像一致。这个坑压死了最多新手,因为报错不会直接出现,只会表现为渲染结果诡异。

5.3 现象:物体浮在半空或背景被拉花

很多第一次用手机拍摄的数据集,物体周围会生成一圈半透明的“假背景”,看起来像物体浮在雾里。原因是背景区域在每张图里的透视变化都不同,NeRF 无法把它们拟合成实心几何,只能用半透明密度“糊弄”过去。另一个叠加原因是旋转台:把物体放在转台上而相机不动,COLMAP 会把静止的桌子、背景算成主场景,物体反而因为旋转产生非刚性位移配不上,重建出来就是一团乱麻。解决方法是固定物体、移动手机,背景与物体的距离拉开一些,让背景区域的视差变化尽量平缓。如果物体是纯色塑料,放一张有纹理的纸或标签贴在上面,领养完成后撕掉即可,这个技巧在商品没有纹理时特别管用。

5.4 现象:显存不够直接 OOM

8GB 显存跑一张 3000 像素的照片,几乎必挂。NeRF 训练是“每条光线、每个采样点都要算一次网络前向”的体量,分辨率越高、batch_size 越大、采样点越多,显存占用线性上升。解决优先级:先把 image_size 缩到 640 到 800,这一步收益最大;再把 batch_size 从 1024 降到 512;最后看 n_samples 是否真的需要 128,降为 64 能省近三分之一的激活显存。

# 常见救急组合:低分辨率 + 小批量 + 少采样点 python run_train.py --config configs/phone_obj.yaml \ --image_size 640 \ --batch_size 512 \ --n_samples 64

跑通后再逐步调回去看显存占用。注意不同源码包的参数名不完全一致,传入的键名以 configs 里实际定义为准。梯度、优化器状态同样吃显存,torch.cuda.empty_cache() 只能清理碎片,别指望它救爆显存。

5.5 现象:Windows 下运行源码包里的脚本报编码错误

这类源码大多是开发者在 Linux 下写的,压缩包解压到 Windows 上一跑就报 UnicodeDecodeError,或者路径带中文直接找不到文件。原因是 Python 默认以系统编码读文件,而源码和数据集标注是 UTF-8。解决有两个办法:一是把文件路径全部改成纯英文,二是给程序入口加两行兜底设置:

import sys sys.stdout.reconfigure(encoding="utf-8")

还有一个小细节:用 VSCode 打开源码时把默认编码改成 UTF-8,避免明明数据路径没错、却因为 JSON 解析时丢掉非 ASCII 字符导致错位。这类问题不会明确指向真实原因,只有出了乱码再排查,属于典型的三小时血泪经验。

6. 验收与进阶:怎么判断重建质量,以及从 NeRF 迁到 3D Gaussian Splatting

6.1 指标怎么判读:PSNR、SSIM、LPIPS

训练完不要只看训练集效果,要留出一部分照片不参与训练,固定一套验证视角来对比渲染图和真实照片。这样测出来的指标才有意义。手机场景的参考值大致如下:

指标手机场景参考值说明
PSNR22 ~ 30 dB高于 30 说明像素级很接近
SSIM0.85 ~ 0.95衡量结构相似度,低于 0.8 检查位姿
LPIPS0.05 ~ 0.20越低越好,更接近人眼感知

我习惯在训练前就把 5% 到 10% 的图片移到 val 目录,训练脚本通常支持这种验证模式。如果训练集 PSNR 很高而验证集很低,说明过拟合,要补数据而不是加网络层数。

6.2 进阶方向:数据直接导入 3D Gaussian Splatting

跑通手机 NeRF 后,下一步大概率会接触“3D高斯三维重建”这个方向。3D Gaussian Splatting 用显式高斯椭球代替 MLP 隐式场,渲染速度快了几个数量级,训练时间从几小时压缩到十几分钟,重建的细节和锐度通常也比 NeRF 高。最关键的是它的输入数据格式几乎和 NeRF 一致:COLMAP 的稀疏模型转成相机参数文件。你已经做完了采集、COLMAP 校验和位姿转换,直接复用 images/ 和 sparse/ 目录,省掉最容易踩的数据采集环节,迁移成本很低。

落到具体习惯上:我现在拿到手机物体三维重建任务,会先拍一小段 30 秒视频抽 60 帧跑通 COLMAP 注册率,再决定要不要全量拍摄。这个“先试拍再全量”的习惯帮我省了很多白训几小时的后悔药。另外训练时先低分辨率跑通再升分辨率,导出网格时多试几个 density 阈值,这些动作看起来琐碎,却是让重建效果稳定的真正关键。希望这篇能让第一次试跑的你,避开我当年踩过的那些坑,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 1:16:11

FDTD Solutions自学笔记:网格、边界、光源与材料拟合的避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:15:18

Python+OpenCV车牌识别实战:从图像处理到GUI界面完整链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:14:18

STM32串口不定长接收:空闲中断+DMA实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:13:59

点云欧式聚类实战:KDTree调优与PCL工业级参数配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:13:58

MRAM+AVR工业级非易失存储系统设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:13:30

树莓派4B搭建Ubuntu20.04+ROS Noetic+VNC远程桌面全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华