Operational Steps
- 确认输入参数完整
- 执行核心操作(参考本目录下的 scripts/ 或 references/)
- 验证输出符合契约
- 保存结果并报告
Pitfalls
-
-
Verification
-
-
-
-
1. 2. 3.
IO_CONTRACT
- input:
request: str, context: dict— 用户请求描述、上下文信息 - output:
result: dict — 技能执行结果(结构因技能而异)
对应原则:P2(机械原子暴露输入输出规范)
3D曲线拟合图
拟合重建陷阱:中心偏移
问题
从投影2D坐标拟合3D曲线(如对数螺旋),重建回3D时必须包含中心偏移cx,cy:
# 错误 — 曲线被锚定在数据质心(缺cx,cy)
cf = centroid + r*cos(theta+rot)*u + r*sin(theta+rot)*v + z*normal
# 正确 — 包含螺旋中心偏移
cf = centroid + (cx + r*cos(theta+rot))*u + (cy + r*sin(theta+rot))*v + z*normal
症状
- RMSE数值合理(<0.2mm)
- 但拟合曲线与数据点在视觉上明显错位
- 复合图与单标本图中同一曲线位置不同(bug只存在于复合图脚本)
根因
2D投影坐标 (pts-centroid)@u,v 以数据质心为原点,但螺旋中心 (cx,cy) 在2D平面中偏离质心。重建时若直接用 centroid + r*cos*u 而非 centroid + (cx+r*cos)*u,曲线被错误锚定在质心。
代码对比(调试时快速定位)
对比正确 vs 错误版本的重建关键行:
| 脚本 | 重建行 | 状态 |
|---|---|---|
fit_logspiral.py |
cr_fit = cx + r*cos(t+rot) |
✅ 正确 |
gen_all_figures.py |
cr_fit = cx + r*cos(t+rot) |
✅ 正确 |
gen_composite_figure.py |
rf*cos(tf+rt) |
❌ 错误 |
快速诊断命令:
grep -n "cx + r\*cos\|cy + r\*sin" code/*.py
# 输出为空 → 脚本有cx/cy缺失bug
# 输出含cx,cy → 检查是否真的包含(不只是r*cos)
🔴 致命陷阱:argsort 路径错乱(2026-05-31 实战验证)
问题
用 np.argsort(theta) 按角度排序代替最近邻路径来确定点在螺旋上的顺序。这会将螺旋上不同位置但角度相近的点混排,产生荒谬的拟合结果。
症状
| 症状 | 错误值 | 正确值 | 比例 |
|---|---|---|---|
| AC bony RMSE | 2.2700 mm | 0.1330 mm | 17× |
| PC memb RMSE | 2.1731 mm | 0.1704 mm | 13× |
| PC bony b | -0.0463 | -0.0191 | 方向相反 |
| LC bony b | 0.0267 | 0.0052 | 5× |
核心教训:图看起来"拟合得好"(曲线穿过点云)≠ 参数正确。argsort 产的曲线视觉上可能正常,但 RMSE 和 b 都是错的。必须交叉验证 — 同时检查 RMSE + b + 视觉质量。
根因
螺旋的数学特性:沿路径 θ 单调递增(或递减),但 np.arctan2 返回的值域是 [-π, π]。用 argsort 将所有点的 θ 按数值排序,实际上将一个完整螺旋圈的端点与起点排序到一起,破坏了点的解剖顺序。
正确做法
# ❌ 错误:按角度排序(只能用于 grid search 找中心)
it = np.argsort(thetas)
t_sorted, r_sorted = thetas[it], r[it]
# ✅ 正确:沿最近邻路径展开 θ
path = nearest_neighbor_path(pts)
theta_unwrapped = np.unwrap(theta_raw[path])
r_path = r2[path]
# 然后沿路径拟合 log(r) = a + b*theta_unwrapped
如何检测
# 快速扫描:找出所有用在最终拟合的 argsort
grep -n "argsort" code/*.py
检查每处 argsort:
- 如果在
refine_2d(grid search 找中心)→ ✅ 可接受(仅用于粗定位) - 如果在最终拟合步骤(确定
a,b和A参数)→ ❌ 必须改为 path-based
最近邻路径实现(标准版)
def nearest_neighbor_path(pts):
"""贪心最近邻:从点0开始,每次找最近未访问点"""
n = len(pts)
visited = [False] * n
path = [0]
visited[0] = True
current = 0
while len(path) < n:
dists = [np.linalg.norm(pts[current] - pts[j]) if not visited[j] else float('inf') for j in range(n)]
nearest = np.argmin(dists)
path.append(nearest)
visited[nearest] = True
current = nearest
return path
修复检查清单
- 拟合函数的最终步骤使用
theta_raw[path]而非argsort(theta) - 螺旋重建使用路径顺序展开的 θ(
np.unwrap后的) - 图中标注的 RMSE 与独立拟合脚本的输出一致
- 复合图与单标本图中同一条中心线的 RMSE 相同
- 脚本中没有重复定义的
fit_logspiral_3d函数(Python 的最后一个定义覆盖前面的)
复合图布局
行x列约定
| 场景 | 布局 | 说明 |
|---|---|---|
| SCC 6种代表 x 3标本 | 6行x3列 | 列=标本(micro-CT, 7T MRI, ICT), 行=代表(AC骨/膜, PC骨/膜, LC骨/膜) |
| 多条件对比 | 行=条件, 列=标本 | 行列主次根据页面长宽比调整 |
间距控制
ax.set_title(pad=15) — 顶行标题与子图内容拉开
plt.suptitle(y=0.96) + tight_layout(rect=[0,0,1,0.97]) — 总标题与首行间距
ax.text2D(-0.4, 0.5, label, rotation=90, va='center') — 左列行标签
LaTeX: end{figure} 后加 vspace{12pt} — 图与后续正文分隔
分段数据合并
当同一管道被分为两段标注(从两端向中点):
merged = np.vstack([seg1, seg2[-2::-1]]) # seg1正向 + seg2反向(去重中点)
验证: 合并间隙 <0.2mm。总点数 = len(seg1) + len(seg2) - 1。
症状: 短弧段(arc<10mm)拟合出荒谬的螺旋率(b=0.5或-0.9), RMSE虽然小但参数物理意义失真。 合并后b回到正常范围(0.01-0.2), 弧长翻倍。
数据一致性检查(v1.2.0新增)
多脚本输出交叉验证
当项目中有多个拟合脚本(fit_logspiral_aligned.py, gen_all_figures.py, gen_composite_figure.py 等)时,必须验证它们对同一份数据输出一致:
# 快速一致性检查:对比两个脚本的拟合结果
# 对 sp1 micro-CT 的 AC bony
python3 -c "
import numpy as np
# 从 fit_logspiral_aligned.py 输出中提取
correct_rmse = 0.1326
correct_b = 0.0958
# 从 gen_all_figures.py 的图标题中提取
figure_rmse = 0.133 # 从图标题读取
figure_b = 0.0958
if abs(correct_rmse - figure_rmse) > 0.01:
print('WARN: RMSE mismatch!')
if abs(abs(correct_b) - abs(figure_b)) > 0.001:
print('WARN: b mismatch!')
"
管道完整性检查
- 每个拟合脚本都有确定的、不冲突的
fit_logspiral_3d函数(无重复定义) - 所有图生成脚本使用相同的拟合管线(路径顺序、中心搜索、参数初始化)
- 图中的 RMSE 标注与独立拟合脚本的控制台输出一致
- 补充图与主图使用同一管线(而非各自实现不同版本的拟合)
- 数据源路径指向当前目录结构(非已删除的
tmp/或旧figures/目录)
3D参数计数约定
问题
论文中的参数计数经常不一致。公式中R(3)+c(3)+shape(5)=11,但拟合仅优化8个。
根因
旋转矩阵R和平移向量c并非全部独立:
- 平面法线来自数据SVD → 固定(0自由)
- 3D中心设为数据质心 → 固定(0自由)
- 实际拟合的独立参数仅为面内自由度
规则
只计实际独立调整的参数,不计公式中可由数据确定的变换。
| 模型 | 参数 | 总计 |
|---|---|---|
| 3D对数螺线 | cx, cy, θ₀, a, b, A, ω, φ | 8 |
| HSMM-2(椭圆+扭转) | cx, cy, θ₀, a, b, A, ω, φ | 8 |
写论文时
在 Methods 段用 "8-parameter independent set: ($c_x, c_y, \\theta_0, a, b, A, \\omega, \\phi$)" 并说明SVD固定平面、质心固定中心的计数约定。
验证清单 · VERIFICATION
- 重建公式显式含中心偏移
(cx + r*cos(...))*u + (cy + r*sin(...))*v;grep -n "cx + r\*cos\|cy + r\*sin" code/*.py输出非空且覆盖所有生成脚本 - 最终拟合沿
nearest_neighbor_path+np.unwrap展开的 θ 进行;grep -n "argsort" code/*.py中 argsort 仅出现在 grid-search 找中心步骤,未用于最终 a/b/A 拟合 - RMSE + 螺旋率 b + 视觉质量三者交叉验证:b 落在物理合理范围(约 0.01–0.2),复合图与单标本图中同一曲线 RMSE 相同
- 无重复定义的
fit_logspiral_3d(Python 最后一个定义会覆盖前面的);所有图脚本与独立拟合脚本使用同一管线,图标题 RMSE 与控制台输出一致(差 <0.01mm) - 分段数据已按
seg1 + seg2[-2::-1]合并(中点去重):合并间隙 <0.2mm,总点数 = len(seg1)+len(seg2)-1
Golden 集合 · GOLDEN SET
- Golden Input: 标准输入样本(覆盖正常路径)
- Golden Output: 预期输出(精确匹配或格式校验)
- Golden Error: 预期错误信息(覆盖失败路径)
Golden 集合是测试的单一真理来源。所有改进必须通过 golden 测试。
每项验证必须可执行、可记录、可复现。验证失败时记录原因和修复。
3D Curve Fitting Figures
Genes (策略基因)
紧凑策略表示。条件→策略。需要深度时参考完整文档。
- [DC-001] 从2D投影坐标重建3D曲线时 → 必须在重建公式中显式包含螺旋中心偏移 (cx, cy),避免曲线被错误锚定在数据质心
- [DC-002] 确定螺旋上点的解剖顺序时 → 必须使用最近邻路径 (nearest_neighbor_path) 展开角度,严禁使用
np.argsort(theta)导致路径错乱 - [DC-003] 评估拟合质量时 → 必须交叉验证 RMSE、螺旋率参数 b 及视觉质量,不能仅凭曲线穿过点云判断参数正确性
- [DC-004] 处理从两端向中点标注的分段数据时 → 需将第二段反向并与第一段合并去重中点,以恢复完整弧长并防止短弧拟合出失真参数
- [DC-005] 在多脚本生成同一数据的不同视图时 → 必须执行交叉验证,确保所有脚本输出的 RMSE 和参数值一致,且无重复定义的拟合函数
- [DC-006] 撰写论文方法部分统计参数数量时 → 仅计算实际独立优化的参数(如8个),排除由 SVD 或质心固定的非独立变换参数
- [DC-007] 布局多标本复合图时 → 遵循“行=解剖代表/条件,列=标本/成像模态”的约定,并精细调整标题间距与标签位置以符合出版规范