WEEK 15(18.08.2025 - 22.08.2025)
- 对比一些chrono看看是不是少东西(漏了某些特性)
- LCP和CCP想办法去做一些对比
8.18
1.关于右端项b的推算过程,比较了VEROSIM在LCP建模时传入的和chrono构建的区别:一样不存在问题。
- 构造公式一样
- 但是chrono为了系统计算的统一性取了负号,但是看起来,我在构建时没问题,存疑
2.残差计算部分有问题,我没有计算RES4残差,而是用相邻迭代差的∞范数,和chrono有所不同,现在尝试更正。
8.19
已经完成了RES4部分的更改
现阶段两个实现方法的区别:
1) 运算对象:显式 A vs. 隐式 N(Schur 补)
- 你的实现:直接在求解器里保存一份稠密矩阵
A
和向量b
(带行填充),梯度用grad = A*y - b
,目标函数是f(x) = ½ xᵀ A x − bᵀ x
。 - Chrono:不显式存 N(= Dᵀ M⁻¹ D),而是通过
sysd.SchurComplementProduct(...)
隐式乘(省时间,省内存),目标函数写成f(l) = ½ lᵀ N l + rᵀ l
,其中r = b_schur
。
2) 投影(Projection)来源
- Chrono:
sysd.ConstraintsProject(...)
从系统描述器里拿所有约束类型(等式、上下界、库仑圆锥/多边形)做统一投影,细节都封装在约束对象里。 - 实现:自己实现
projectBounds + projectFriction
,通过myNub + frictionIndices
把切向块投影到‖t‖ ≤ μ n
。只要索引/μ 的约定一致,效果等价;但可覆盖的约束类型取决于你写的投影器(Chrono 的更通用一些)。
结论先说:核心 APGD 逻辑你已经把 Chrono 里的要点都落下来了(只用 RES4 收敛、回溯 + Armijo、Nesterov 加速、非单调重启、最优解 gamma_hat
追踪、用“历史最好”作为最终解等)。下面我把 Chrono 常见实现点逐条对照,哪些“已实现”、哪些“建议补齐/检查”一目了然:
- ✅ 目标函数/梯度一致:
f(x)=0.5 xᵀAx - bᵀx
,grad = A*y - b
。 - ✅ RES4 残差:
‖x - Proj(x - α∇f)‖ / α
,用的是本步的t
;alpha
下限保护1e-16
。 - ✅ 回溯线搜索 + Armijo 条件:不满足就
L_temp*=2
,t=1/L_temp
,并有限次上限(BT_MAX=20
),触顶回退到yk
。 - ✅ Nesterov 加速:
θ_{k+1} = (1+√(1+4θ_k²))/2
,β=(θ_k-1)/θ_{k+1}
,y_{k+1}=x_{k+1}+β(x_{k+1}-x_k)
。 - ✅ 非单调保护(restart):若
g·(x_{k+1}-x_k) > 0
则y ← x
且θ ← 1
。 - ✅ Lipschitz 起步估计:
L0 = ‖A(γ0-γ̂0)‖/‖γ0-γ̂0‖
,t0=1/L0
。 - ✅ 自适应 L 更新:接受后
Lk ← 0.9*L_temp
,tk ← 1/Lk
。 - ✅ Warm start:
useWarmStart
控制,冷启动置零,y0 = xprev
。 - ✅ 投影到盒约束:
projectBounds()
始终做。 - ✅ 摩擦圆锥投影(块级、L2 归一):根据
frictionIndices
聚块,按‖T‖ ≤ μ λ_n
缩放。 - ✅ “历史最好”追踪:每步用 RES4 刷新
rmin
/gamma_hat
,提前达标则退出。 - ✅ 最终解选择:用
gamma_hat
(若从未更新则回退xprev
),同步到*x
和xprev
,再计算w = A*x - b
。 - ✅ 返回值:
return (rmin < tol)
(与 Chrono 用“最佳残差是否达标”的语义一致)。
8.20
中等规模的颗粒/多体接触,CDTest6.vme
测试条件:200迭代, realtime ratio = 0
dantzig 0.27
APGD 0.45
目前已经实现了CHRONO中APGD相关代码的所有功能,现在开始围绕投影部分做出相应的检查(CHRONO),在 Project Chrono 里,“投影(projection)”不是直接写在求解器里逐个元素去裁剪的,而是放在各个约束类的 Project() 方法里