重建失败的 6 种典型 case 与降级路径
3DGS 重建失败的原因五花八门:输入拍得太烂、物体本身没特征、机器内存不够、重建跑超时、设备不支持、结果出来质量太差。每种失败的降级方案不一样——OOM 该清数据还是保留?超时该用部分结果还是直接放弃?结果质量差该重算还是给用户凑合看?我们踩过最坑的 case 是 OOM 降级把用户拍了 5 分钟的素材清掉了,用户骂"我白拍了"。降级不是技术问题,是体验取舍。
能力面:6 种典型失败
Spatial Recon Kit 的重建失败集中在 6 种,每种有对应错误码和检测时机:
| 失败类型 | 错误码 | 检测时机 | 是否可重试 |
|---|---|---|---|
| 输入不足 | INPUT_INSUFFICIENT | 重建开始前 | 是(引导重拍) |
| 特征缺失 | FEATURE_SPARSE | 特征提取阶段 | 是(换角度重拍) |
| OOM | OUT_OF_MEMORY | 任意阶段 | 部分(降质重试) |
| 超时 | RECON_TIMEOUT | 任意阶段 | 部分(部分结果) |
| 设备不支持 | DEVICE_NOT_SUPPORTED | session 创建时 | 否(降级方案) |
| 结果质量差 | QUALITY_LOW | 重建完成后 | 是(调参重试) |
前两种在输入门禁阶段就该挡掉。后四种是门禁过了、重建过程中出问题,是这篇的重点。
六种失败各走一条降级路,但都汇到同一个校验出口:
降级动作各异,出口只有"告知损失后可用"和"重拍"两个,别静默降级。
约束面:每种失败的降级方案与体验损失
降级方案不是"让重建成功",是"让用户不至于骂街"。每种降级都伴随体验损失,要明确告诉用户损失了什么:
| 失败类型 | 降级方案 | 体验损失 | 用户感知 |
|---|---|---|---|
| 输入不足 | 引导重拍 | 时间 | “请按引导重新拍摄” |
| 特征缺失 | 引导换角度 | 时间 | “物体纹理太少,请换角度” |
| OOM | 降质重建(减帧/降分辨率) | 重建质量 | “内存不足,已降低质量重建” |
| 超时 | 用部分结果 or 放弃 | 重建完整度 | “重建超时,已用部分结果” |
| 设备不支持 | 回退 Mesh/全景图 | 重建能力 | “当前设备不支持,已切换预览模式” |
| 结果质量差 | 调参重试 or 凑合用 | 重建质量 | “结果质量一般,是否重新计算” |
降级方案的核心是"明确告知损失",不要静默降级。用户宁可看到"质量降低了"的提示,也不愿看到不知道哪里不对的怪结果。
场景落地:6 种失败的实际触发与降级
商品展示场景跑了 500 次真实重建,统计每种失败的发生频率和降级后体验:
| 失败类型 | 发生次数 | 占比 | 降级耗时 | 降级后用户评分 | 降级后是否可用 |
|---|---|---|---|---|---|
| 输入不足 | 47 | 9.4% | 引导重拍 2-5min | 4.1/5(重拍后) | 可用 |
| 特征缺失 | 23 | 4.6% | 引导换角度 1-3min | 3.8/5 | 可用 |
| OOM | 31 | 6.2% | 降质重建 +8-15s | 3.2/5 | 勉强可用 |
| 超时 | 18 | 3.6% | 部分结果 +0s | 2.4/5 | 不可用(多数) |
| 设备不支持 | 12 | 2.4% | 回退全景图 +200ms | 3.6/5 | 可用 |
| 结果质量差 | 35 | 7.0% | 调参重试 +12-20s | 3.9/5 | 可用 |
| 无失败 | 334 | 66.8% | — | 4.5/5 | 可用 |
几个值得注意的点:超时降级用部分结果,体验评分最低 2.4/5。部分结果通常是物体一半重建出来、另一半是大洞。后来改成"放弃 + 引导减少拍摄范围重拍",评分升到 3.7/5。OOM 降质重建能用但评分不高,降质是把帧数砍半、分辨率降到 540p。结果质量差调参重试效果最好,重试 12-20s 后 80% 能拿到合格结果。设备不支持回退全景图体验意外地好,3.6/5 比 OOM 降质还高。
踩坑:OOM 降级清了用户素材
第一个版本 OOM 降级逻辑:检测到内存压力 → 销毁当前 session 释放内存 → 用降质配置重新创建 session → 重新加载素材。问题出在"销毁 session"——session 销毁会把已加载的原始帧数据也清掉。
用户拍了 5 分钟环绕视频,重建跑到 70% 时 OOM,降级触发,素材清空。两周收到 11 个投诉。
修复方式是把原始素材在 session 创建时拷一份到应用沙箱临时目录,OOM 降级时从临时目录重新加载。代价是存储多占一份(200 帧 1080p 视频约 80-120MB),但素材不丢。
// entry/src/main/ets/recon/FailureHandler.etsimport{spatialRecon,ReconSession}from'@kit.SpatialReconKit';import{fs}from'@kit.FileSystemKit';exportclassFailureHandler{// 重建失败统一入口staticasynchandle(error:ReconError,ctx:ReconContext):Promise<ReconResult>{switch(error.code){case'OUT_OF_MEMORY':returnthis.handleOOM(ctx);case'RECON_TIMEOUT':returnthis.handleTimeout(ctx);case'QUALITY_LOW':returnthis.handleLowQuality(ctx);case'DEVICE_NOT_SUPPORTED':returnthis.handleUnsupported(ctx);case'FEATURE_SPARSE':returnthis.handleSparseFeature(ctx);default:returnthis.handleUnknown(error,ctx);}}// OOM 降级:从临时目录重新加载素材,降质重建privatestaticasynchandleOOM(ctx:ReconContext):Promise<ReconResult>{// 素材已在 session 创建时备份到 ctx.backupDir,不依赖 session 内存constbackupFrames=awaitfs.listDir(ctx.backupDir);if(backupFrames.length===0)return{status:'fail',reason:'素材丢失,请重新拍摄'};// 降质:帧数砍半,分辨率降到 540pconstdegradedConfig={...ctx.originalConfig,maxFrames:Math.floor(ctx.originalConfig.maxFrames/2),targetResolution:540,};constsession=awaitspatialRecon.createReconSession(degradedConfig);awaitsession.loadFrames(backupFrames);// 从备份加载,不依赖用户重拍returnsession.reconstruct();}// 超时降级:放弃部分结果,引导减少拍摄范围重拍privatestaticasynchandleTimeout(ctx:ReconContext):Promise<ReconResult>{// 试过用部分结果,体验评分 2.4/5;改成放弃 + 引导,评分 3.7/5return{status:'fail',reason:'重建超时,建议减少拍摄范围或物体大小后重试',guide:'reduce_range'};}}这段代码的关键是OOM 降级不能依赖 session 内存里的素材,必须从独立备份重新加载。
被放弃的方案:试过 OOM 自动重试三次每次降一档。第三次降到底线质量重建出来还是垃圾,用户等了 45 秒最后看到糊成马的结果。后来改成只降一次,降到 540p 还不行就直接 fail。
踩坑:超时降级用部分结果更差
超时降级有两种选择:用部分结果或直接放弃。我们一开始选了用部分结果,理由是"用户等了那么久,总得给点东西"。
实际跑下来部分结果几乎不可用。3DGS 重建是迭代优化,跑到 60% 超时的结果,物体只有一半区域有高斯点覆盖。改成直接放弃后,UI 提示"重建超时,建议减少拍摄范围",用户反而觉得"应用诚实"。有时候放弃比凑合更好。
失败-降级映射表(最终版)
| 失败类型 | 降级动作 | 是否保留素材 | 是否重试 | 用户体验 |
|---|---|---|---|---|
| 输入不足 | 引导重拍 | 是 | — | 烦但能接受 |
| 特征缺失 | 引导换角度 | 是 | — | 烦但能接受 |
| OOM | 降质重建(540p + 帧数减半) | 是(独立备份) | 1 次 | 质量降级但能用 |
| 超时 | 放弃 + 引导减少范围 | 是 | — | 诚实告知,引导重拍 |
| 设备不支持 | 回退全景图预览 | — | — | 能力降级但能看 |
| 结果质量差 | 调参重试(增加迭代) | 是 | 1 次 | 多等 15s,80% 能修好 |
真机数据:降级前后的用户评分对比
500 次真实重建里触发降级 166 次,对比"无降级"和"走降级路径":
| 失败类型 | 无降级评分 | 走降级评分 | 评分差 | 放弃率差 |
|---|---|---|---|---|
| 输入不足 | 1.2/5(垃圾结果) | 4.1/5(重拍后) | +2.9 | -32% |
| 特征缺失 | 1.5/5 | 3.8/5 | +2.3 | -25% |
| OOM | 1.0/5(崩溃) | 3.2/5 | +2.2 | -41% |
| 超时 | 2.4/5(部分结果) | 3.7/5(放弃引导) | +1.3 | -18% |
| 设备不支持 | 1.0/5(崩溃) | 3.6/5(全景图) | +2.6 | -45% |
| 结果质量差 | 2.8/5 | 3.9/5 | +1.1 | -12% |
降级在所有 case 上都显著提升评分和降低放弃率。最明显的是设备不支持——直接崩溃评分 1.0,回退全景图评分 3.6。
几个细节
QUALITY_LOW在重建完成后用 SSIM 比对,耗时约 200ms。- OOM 降质重建只重试 1 次,再 OOM 就直接 fail。
- 降级提示要具体。"质量降低"是废话,"已降至 540p"才是有效信息。
- 设备不支持的降级要提前做,在设备门禁阶段就判。
- 素材备份清理策略要补上。一开始没清理,用户重建 10 次后沙箱占好几 GB。
总结一下
- 6 种失败要有统一处理入口
- OOM 降级必须从独立备份重新加载素材
- 超时降级直接放弃 + 引导更好
- 降级重试最多 1 次
- 降级提示要明确告知损失
- 设备不支持要在门禁阶段提前降级
- 素材备份要有清理策略
6 种失败降级路径跑通后,下一步收集线上数据看每种失败的实际占比。内测 500 次的数据线上分布可能不同——线上中端机占比更高,OOM 和超时占比可能翻倍。下一篇拆 9020 和 9030 性能差异。