scipy.optimize.minimize多变量优化不收敛,求解使Board_Moments最小的参数
问题核心原因
- 目标函数定义错误:
scipy.optimize.minimize默认求解函数的最小值,你当前直接返回deltaM,算法会无限往deltaM负向增大的方向迭代,而不是寻找deltaM=0的根,这是出现lm迭代到1e23量级离谱结果的核心原因 - 未加参数物理边界约束:
board_trim(纵倾角)和lm(长度参数)都有合理物理范围,无约束下算法会遍历无意义的参数区间 - 可选:参数单位可能存在错误,比如注释里
displacement_N标注为kN,但70*9.81得到的是N单位,若实际displacement单位是吨,需要补1000的换算系数
解决步骤
1. 修正目标函数
将目标函数改为返回deltaM的平方(或绝对值),此时函数最小值对应deltaM=0的理想状态:
def Board_Moments(params): board_trim, lm = params # 若displacement单位为吨,此处改为 displacement * 1000 * 9.81 displacement_N = displacement * 9.81 lp = Lp(Vs_ms, b, lm) N = displacement_N * cos(d2r(board_trim)) deltaM = (displacement_N * LCG) - (N * lp) # 修正为返回平方,也可以用abs(deltaM) return deltaM ** 2
2. 增加参数边界约束
根据物理意义给两个参数设置合理范围,比如纵倾角board_trim范围设为030度,`lm`范围设为530(可根据你的实际场景调整范围)。
3. 调整优化参数
选择支持边界约束的优化算法,同时可以放宽迭代次数限制,完整可运行代码参考:
from scipy.optimize import minimize from math import cos, radians as d2r # 原有参数保持不变 displacement = 70 b = 6.5 deadrise = 20 LCG = 10 Vs_ms = 23.15 rho = 1025 mu = 1.19e-6 # 原有工具函数保持不变 def Cv(Vs_ms, b): cv = Vs_ms / (9.81 * b)**0.5 return cv def Lambda_(lm, b): lambda_ = lm / b return lambda_ def Lp(Vs_ms, b, lm): cv = Cv(Vs_ms, b) Lambda = Lambda_(lm, b) Cp = 0.75 - (1 / (5.21 * (cv / Lambda)**2 + 2.39)) lp = Cp * lm return lp # 修正后的目标函数 def Board_Moments(params): board_trim, lm = params displacement_N = displacement * 9.81 lp = Lp(Vs_ms, b, lm) N = displacement_N * cos(d2r(board_trim)) deltaM = (displacement_N * LCG) - (N * lp) return deltaM ** 2 # 初始值不变 x0 = [2, 17.754] # 参数边界:(board_trim最小值, 最大值), (lm最小值, 最大值),可按需调整 bounds = [(0, 30), (5, 30)] # 选择支持边界的L-BFGS-B算法,放宽迭代次数限制 Deltam = minimize(Board_Moments, x0, method='L-BFGS-B', bounds=bounds, options={'maxiter': 1000}) print(Deltam)
4. 可选优化
如果还是收敛效果不好,可以:
- 调整参数边界范围,匹配你的实际工程场景
- 更换优化算法,比如
Powell、TNC都支持边界约束 - 若你本质是求
deltaM=0的根,也可以改用scipy.optimize.root函数求解,更匹配求根场景
内容的提问来源于stack exchange,提问作者Jaume Triay
相关产品推荐
相关产品推荐

