Scipy least_squares添加Jacobian后相机标定优化不收敛问题排查
问题根源分析
你的代码存在两个核心错误,导致自定义Jacobian无法适配least_squares的优化逻辑:
1. 残差函数返回平方后结果,与Jacobian推导不匹配
Scipy的least_squares要求残差函数返回原始残差(预测值-观测值),它内部会自动计算残差平方和作为优化目标。但你的residuals函数返回的是平方后的残差:
return ((projected_points - points_2D.T)**2).flatten()
而你实现的jacobian是针对原始残差的导数,这直接导致优化过程中使用的Jacobian与实际残差的导数完全不一致,算法无法正确更新参数。
2. Jacobian矩阵的y方向导数符号错误
在project函数中,y的投影公式为:
y = -fy * Y / Z + cy
因此y对fy的偏导数应为-Y[i]/Z[i],但你在Jacobian中写的是Y[i]/Z[i],符号错误会导致参数更新方向完全相反。
修正后的代码
import numpy as np from scipy.optimize import least_squares # Camera projection function def project(params, points_3D): """投影3D点到2D平面""" fx, fy, cx, cy = params X, Y, Z = points_3D x = fx * X / Z + cx y = -fy * Y / Z + cy return np.array([x, y]) # Jacobian function def jacobian(params, points_2D, points_3D): fx, fy, cx, cy = params X, Y, Z = points_3D nb_pts = points_3D.shape[1] J = np.zeros((2*nb_pts, 4)) for i in range(nb_pts): # 修正y对fy的导数符号,适配原始残差的导数 J[2*i:2*i+2, :] = np.array([ [X[i]/Z[i], 0, 1, 0], [0, -Y[i]/Z[i], 0, 1] ]) return J # Residual function for least_squares def residuals(params, points_2D, points_3D): """返回原始残差(预测2D点 - 观测2D点)""" projected_points = project(params, points_3D) return (projected_points - points_2D.T).flatten() # Initial guess for the camera parameters params0 = np.array([1050, 1000, 320, 240]) # 最优参数是[1000, 1000, 320, 240] # Example 3D points points_3D = np.array([ [0, 0, 3], [20/100, 0, 2], [0, -1/100, 1] ]) # Example 2D points points_2D = np.array([ [320, 240], [420, 240], [320, 250] ]) # Perform optimization result = least_squares(residuals, params0, jac=jacobian, args=(points_2D, points_3D.T), verbose=1) # Print the optimized camera parameters print("优化后的参数:", result.x) print("优化后残差:", residuals(result.x, points_2D, points_3D.T))
修正说明
- 调整残差函数:返回原始残差而非平方后的结果,匹配
least_squares的预期输入逻辑 - 修正Jacobian中y方向对fy的导数符号,确保导数计算与投影公式一致
- 简化
project函数参数(移除无用的points_2D参数)
运行修正后的代码,优化过程会正常收敛到预期的最优参数[1000, 1000, 320, 240]。
内容的提问来源于stack exchange,提问作者AlixL
相关产品推荐
相关产品推荐

