Scipy.optimize.minimize未遵循NonlinearConstraint约束的问题排查
嘿,我仔细捋了你的代码和遇到的问题,这种之前能用的约束写法突然失效的情况确实挺闹心的,咱们一步步来揪出问题所在:
1. 约束上下界与返回值的维度不匹配(核心问题!)
先看你的约束函数non_lin_cons_f的返回结构:
- 前3个元素:匹配点的位置差
mx, my, mz - 第4个元素:匹配点的速度差
match_vel_diff - 后面跟着
dv_lim,也就是每个节点的速度增量的模长,长度是n-1(因为优化变量x是(n-1)*3维,拆分后刚好是n-1个速度向量)
但你定义的约束上下界却是:
non_lin_lb = concatenate((zeros(3), full(n, -inf))) non_lin_ub = concatenate((zeros(3), full(n, max_dv)))
这里明显有个维度错误:full(n, ...)应该改成full(n-1, ...)!比如你例子里n=5,dv_lim是4个元素,但你给的上下界后面却有5个位置,这会导致约束的上下界长度和约束函数返回值长度不匹配,Scipy根本没法正确识别每个约束的边界,自然会出现约束被无视的情况。
修正后的上下界定义应该是:
# 前3位:位置差必须为0;第4位:速度差无下界,上界为max_dv;后面n-1位:每个速度增量的模长无下界,上界为max_dv non_lin_lb = concatenate((zeros(3), [-inf], full(n-1, -inf))) non_lin_ub = concatenate((zeros(3), [max_dv], full(n-1, max_dv)))
你可以先打印len(non_lin_cons_f(x0))和len(non_lin_lb)确认两者长度一致,这是约束生效的基础。
2. 匹配点速度约束的逻辑是否符合需求?
你把匹配点的速度差match_vel_diff的约束设为[-inf, max_dv],但从目标函数里也加入了这个值来看,你是不是希望这个速度差尽可能小?如果是需要强制速度差为0,那应该把这一项的上下界都设为0,和位置差的约束一样。当然这取决于你的实际需求,只是提出来供你核对。
3. 优化器的收敛容差可能太宽松
trust-constr有默认的收敛容差,比如gtol(约束满足的容差),如果约束的违反程度在默认容差范围内,优化器会认为已经收敛。你可以尝试调小这些参数,让优化器对约束满足的要求更严格:
res = opt.minimize(min_dv, x0, method="trust-constr", jac="2-point", hess=opt.SR1(), constraints=[non_lin_cons], bounds=bounds, options={"gtol": 1e-8, "xtol": 1e-8})
4. 数值求导的精度问题
你用了jac="2-point"自动数值求导,这种方式可能会因为精度问题导致优化器对约束的梯度估计不准,进而影响约束的满足。如果条件允许,手动实现约束函数的雅可比矩阵会更准确,能帮助优化器更好地处理约束。
建议你先优先修复第一个维度不匹配的问题,这应该是导致约束失效的最主要原因,之后再根据情况调整其他参数。
备注:内容来源于stack exchange,提问作者user29521723

