GEKKO仅支持零向量终态求解,其他终态提示不可行求助
问题背景
在GEKKO中实现线性时不变系统的模型预测控制(MPC),终端状态x_f单独定义,权重矩阵Q为6×6零矩阵,R为3×3的10倍单位矩阵。当设置x_f为零向量时求解可正常收敛,但设置非零终端状态(如[0.04, 0.00, 0.00, 0.00, 0.00, 0.00])时,求解器提示局部不可行,且该问题在多组初始状态x_0与终端状态x_f组合下均出现,怀疑并非真实不可行场景,需排查原因并寻找解决办法。
GEKKO实现代码
m = GEKKO(remote = solverParams["remote"]) m.time = timeSeq x = [m.Var(value = x_0[i], fixed_initial = True) for i in range(len(x_0))] u = [m.Var(value = u_0[i], fixed_initial = False) for i in range(len(u_0))] w = m.Param(value = w) for i in range(len(x)): m.fix_final(x[i], val = x_f[i]) if stateBounds[i]["lower"] != "-Inf": x[i].lower = stateBounds[i]["lower"] if stateBounds[i]["upper"] != "+Inf": x[i].upper = stateBounds[i]["upper"] for i in range(len(u)): if forceBounds[i]["lower"] != "-Inf": u[i].lower = forceBounds[i]["lower"] if forceBounds[i]["upper"] != "+Inf": u[i].upper = forceBounds[i]["upper"] eqs = [x[i].dt() == np.matmul(A[i], x) + np.matmul(np.atleast_1d(B[i]), u) for i in range(len(x))] eqs = m.Equations(eqs) startTime = time.time() m.Minimize(np.add(np.matmul(np.subtract(x, x_f), np.atleast_1d(np.matmul(np.atleast_1d(Q), np.subtract(x, x_f)))), np.matmul( u, np.atleast_1d(np.matmul(np.atleast_1d(R), u)))) *w) m.options.IMODE = 6 m.options.SOLVER = 3 m.solve(disp = solverParams["disp"]) stopTime = time.time() x_p = [x[i].value for i in range(len(x))] u_p = [u[i].value for i in range(len(u))] x_p = np.transpose(x_p) u_p = np.transpose(u_p)
测试用例
- 初始状态:
x_0 = np.array([50.00, -25.00, 80.00, 0.00, 0.00, 0.00])
- 可行终端状态(零向量):
x_f = np.array([0.00, 0.00, 0.00, 0.00, 0.00, 0.00])
- 不可行终端状态示例:
x_f = np.array([0.04, 0.00, 0.00, 0.00, 0.00, 0.00])
问题原因分析
硬终端约束过于严格:代码中使用
m.fix_final(x[i], val=x_f[i])强制终端状态完全等于x_f,属于硬约束。若系统在给定时间序列长度、状态/控制约束下无法精确到达x_f,求解器会直接判定不可行。尤其当Q=0时,目标函数仅最小化控制输入代价,没有终端状态跟踪的软惩罚,求解器没有妥协空间。初始猜测不合理:控制输入
u的初始值u_0若离可行解过远,IPOPT(SOLVER=3)作为梯度求解器,可能陷入局部不可行区域无法跳出。求解器参数设置:默认的IPOPT迭代次数、收敛容差可能不足以让求解器找到可行解;部分求解器对硬约束的处理策略也会影响结果。
系统可达性验证不足:虽然系统是线性时不变,但需确认在给定约束(状态上下限、控制上下限)和时间范围内,
x_f是否真的可达。若存在约束限制了路径,即使理论可控,实际也无法到达。
解决办法
1. 替换硬终端约束为软约束
去掉m.fix_final硬约束,在目标函数中加入终端状态跟踪的惩罚项,给求解器妥协空间。例如:
# 移除原有硬约束 # for i in range(len(x)): # m.fix_final(x[i], val = x_f[i]) # 增加终端状态软惩罚(权重可根据需求调整,比如1e6) m.Minimize(1e6 * sum((x[i]-x_f[i])**2 for i in range(len(x))))
保留原有的控制输入代价最小化,求解器会在控制代价和终端跟踪误差之间做权衡,避免硬约束导致的不可行。
2. 优化初始猜测
- 若已知稳态控制输入(使得系统稳态为
x_f),将u的初始值设为该稳态值。 - 先通过开环仿真计算一个粗略可行的控制序列,作为
u的初始猜测。
3. 调整求解器参数
- 更换求解器:尝试使用APOPT(
m.options.SOLVER=1),它在处理不可行问题和离散优化时表现更灵活。 - 增大迭代次数:
m.options.MAX_ITER=1000(默认可能为500)。 - 调整收敛容差:
m.options.RTOL=1e-6,m.options.ATOL=1e-6,放宽容差帮助求解器收敛。
4. 验证系统可达性
- 先做开环仿真:手动给定控制输入,看是否能让系统在时间序列内到达
x_f,同时满足状态和控制约束。若开环都无法到达,说明x_f确实不可行,需调整x_f或约束。 - 检查系统可控性:通过可控性矩阵验证系统是否完全可控,确认理论上
x_f是否可达。
5. 调整时间序列长度
若时间序列过短,系统没有足够时间调整到x_f,可增加timeSeq的长度,给系统留出更多调整时间。
内容的提问来源于stack exchange,提问作者Aaron John Sabu

