序列二次规划法SQP解非线性优化问题 序列二次规划法SQP求解非线性优化问题的自编MATLAB程序,支持等式约束,不等式约束以及混合约束。 目标函数与约束函数均可以是非线性函数,但要求目标函数和约束函数的一阶偏导数连续。 买家修改附图标注的5处地方即可用于求解自己的优化问题。 应用示例见末图。

前几天在实验室折腾机械臂轨迹优化,碰上个非线性约束的硬骨头。师兄甩过来一句"用SQP啊",结果全网搜到的SQP代码全是黑箱调用。得,干脆自己撸个能改约束的MATLAB版SQP,没想到还真跑通了。

先看核心架构——整个程序分成三个模块:目标函数池、约束函数池、主迭代器。重点在于怎么把使用者的目标函数和约束条件喂给算法。来看这个自编的mySQP函数入口:

function [x_opt, fval] = mySQP(fun, x0, cons, ceq_flag)
    % fun: 目标函数句柄
    % x0: 初始猜测
    % cons: 约束结构体(含函数、梯度)
    % ceq_flag: 等式约束标记数组
    ...
end

需要用户动手改的五个地方,全藏在函数参数里。比如目标函数梯度计算,这里用了自动差分但建议手动提供:

% 修改点1:目标函数梯度(建议手动计算)
if nargout(fun) > 1
    [f, grad] = fun(x);
else
    grad = finiteDiff(fun, x); % 内置有限差分备用
end

处理约束时有个巧妙的设计——用0/1标记等式不等式约束。比如当约束条件同时包含x²≤3(不等式)和sin(x)=0(等式)时:

% 修改点2:约束类型标记
ceq_flag = [0; 1]; % 第一个约束是不等式,第二个是等式

迭代核心是QP子问题。这里调用MATLAB自带的quadprog求解二次规划,但Hessian矩阵用BFGS法更新才是精髓:

% 修改点3:Hessian更新策略
H = BFGS_update(H, delta_x, delta_grad);
qp_options = optimset('Display', 'off');
[d, ~, exitflag] = quadprog(H, grad, Aineq, bineq, Aeq, beq, [], [], [], qp_options);

实战测试时,拿经典的Hock-Schittkowski问题开刀。原问题要求最小化(x₁-2)² + (x₂-1)²,满足x₁² - x₂ ≤0 和 x₁ + x₂ ≤2。在程序中只需修改两处:

% 修改点4:目标函数定义
function [f, grad] = hockSchittkowski(x)
    f = (x(1)-2)^2 + (x(2)-1)^2;
    if nargout > 1
        grad = [2*(x(1)-2); 2*(x(2)-1)];
    end
end

% 修改点5:约束函数定义
cons(1).func = @(x) x(1)^2 - x(2);
cons(1).grad = @(x) [2*x(1); -1];
cons(2).func = @(x) x(1) + x(2) - 2;
cons(2).grad = @(x) [1; 1];

跑起来之后观察迭代过程特别有趣:初始点(0,0)处QP子问题解出的搜索方向会先撞到二次约束边界,接着在后续迭代中像打水漂一样逐步逼近最优点(1,1)。整个过程梯度投影和积极集的变化,在命令行打印的迭代信息里看得清清楚楚。

序列二次规划法SQP解非线性优化问题 序列二次规划法SQP求解非线性优化问题的自编MATLAB程序,支持等式约束,不等式约束以及混合约束。 目标函数与约束函数均可以是非线性函数,但要求目标函数和约束函数的一阶偏导数连续。 买家修改附图标注的5处地方即可用于求解自己的优化问题。 应用示例见末图。

这个自研SQP最实用的地方在于约束处理的透明度。之前用fmincon优化电机参数时,老怀疑约束没生效。现在把每个约束的违反量打印出来,哪条约束在起作用一目了然。特别是处理不等式约束时,积极集策略让无效约束根本不影响计算量。

不过要提醒几点:1)手动计算梯度虽然麻烦,但比自动差分稳定得多,特别是约束函数存在陡峭区域时;2)初始点别太放飞自我,至少得满足不等式约束;3)遇到震荡不收敛时,调小步长因子beta管用。

完整代码已打包成开箱即用的工具包,要换成自己的问题,基本上就是改改那五个标记点的事。当然,如果遇到目标函数二阶不光滑的妖孽问题,可能还得上更复杂的Hessian修正策略——但那就是另一个故事了。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐