第一次接触fmincon是在研究生时期做机械臂轨迹优化项目。当时需要让机械臂在避开障碍物的同时,以最小能耗完成指定动作,这本质上就是个带非线性约束的优化问题。试过各种方法后,导师轻飘飘地说了句:"用fmincon吧",结果困扰我两周的问题只用20行代码就解决了。
fmincon是MATLAB优化工具箱中的非线性约束优化求解器,它能处理以下典型场景:
与线性规划不同,fmincon的强大之处在于能同时处理:
matlab复制% 最简单的调用示例
options = optimoptions('fmincon','Display','iter');
[x,fval] = fmincon(@objFun,x0,A,b,Aeq,beq,lb,ub,@nonlcon,options);
实际项目中我常遇到这种情况:理论模型很完美,但一写代码就报错。后来发现,80%的报错都源于对fmincon参数理解的偏差。比如有一次把不等式约束的方向搞反了,导致求解器始终找不到可行解,调试了整整一天。
fmincon的函数签名看起来令人望而生畏:
matlab复制[x,fval,exitflag,output,lambda,grad,hessian] = fmincon(...
fun,x0,A,b,Aeq,beq,lb,ub,nonlcon,options)
核心参数实战经验:
fun:目标函数建议用匿名函数或独立函数文件。我曾比较过两种写法,匿名函数在简单场景下更直观,但复杂函数建议单独写文件便于调试。matlab复制% 匿名函数示例(推荐)
fun = @(x) x(1)^2 + 2*x(2)^2 - x(1)*x(2);
% 独立文件示例(复杂场景适用)
function f = objFun(x)
f = exp(x(1))*(4*x(1)^2 + 2*x(2)^2 + 4*x(1)*x(2) + 2*x(2) + 1);
end
A,b:线性不等式约束A*x ≤ b的编码有技巧。比如约束条件x₁ + 2x₂ ≥ 5需要转换为标准形式:-x₁ - 2x₂ ≤ -5matlab复制A = [-1 -2]; % 注意负号
b = -5; % 右侧同样取负
nonlcon:非线性约束函数必须返回两个输出:[c,ceq]。有个容易踩的坑:不等式约束c必须写成≤0的形式。曾经因为忘记这个规则,导致约束方向完全错误。matlab复制function [c,ceq] = circleConstraint(x)
% 定义圆形禁区 (x1-1)^2 + (x2-2)^2 ≥ 3
c = 3 - (x(1)-1)^2 - (x(2)-2)^2; % 转换为≤0形式
ceq = []; % 无等式约束
end
完整的输出参数包含丰富的信息:
matlab复制[x,fval,exitflag,output,lambda,grad,hessian] = fmincon(...)
exitflag:这个参数价值连城但常被忽视。曾经有个项目优化结果异常,查看exitflag发现是2(变化量小于阈值),提示需要调整优化选项。常见exitflag含义:
code复制 1 : 一阶最优性条件满足
0 : 迭代次数超过options.MaxIterations
-2 : 未找到可行解
lambda:拉格朗日乘子就像优化问题的"体检报告"。通过分析lambda.ineqnonlin,我发现某个约束实际上从未被激活,于是简化了问题。假设我们要优化6自由度机械臂的关节轨迹θ(t),要求:
目标函数和约束条件分别为:
matlab复制function J = objective(theta)
% 最小化总运动时间(假设与关节变化量正相关)
J = sum(diff(theta).^2);
end
function [c,ceq] = constraints(theta)
% 关节加速度约束 |θ''| ≤ 10 rad/s²
acc = diff(theta,2);
c = [abs(acc) - 10;
% 障碍物距离约束
sphereObstacleDistance(theta) - 0.5];
ceq = [];
end
初始实现直接调用fmincon:
matlab复制theta0 = zeros(6,100); % 初始轨迹
options = optimoptions('fmincon','Algorithm','sqp');
[theta_opt,fval] = fmincon(@objective,theta0,[],[],[],[],[],[],@constraints,options);
遇到的典型问题:
matlab复制options.UseParallel = true;
matlab复制parfor i = 1:5
theta0 = rand(6,100)*2*pi;
[theta_opt(i),fval(i)] = fmincon(...);
end
[~,idx] = min(fval);
best_theta = theta_opt(idx);
优化后必须验证:
matlab复制[c,~] = constraints(theta_opt);
assert(all(c <= 1e-6), '约束未满足!');
matlab复制figure;
subplot(2,1,1); plot(theta_opt'); title('关节角度');
subplot(2,1,2); plot(diff(theta_opt,2)'); title('关节加速度');
fmincon提供多种算法:
matlab复制'algo_names = {'interior-point', 'sqp', 'active-set', 'trust-region-reflective'};
我的实测经验:
曾经处理过2000维的有限元优化问题,interior-point比sqp快3倍,但需要16GB内存。
默认的有限差分梯度计算慢且不精确。提供解析梯度可提速5倍:
matlab复制options = optimoptions('fmincon','SpecifyObjectiveGradient',true);
function [f,g] = objWithGradient(x)
f = x(1)^2 + sin(x(2));
g = [2*x(1); cos(x(2))]; % 梯度必须与x同维度
end
海森矩阵的提供更能大幅提升收敛速度,但对编程要求较高。
当遇到exitflag为负时,我的调试清单:
x0是否满足所有约束?matlab复制x_scaled = x ./ [1000, 0.1]; % 各变量缩放系数
matlab复制options = optimoptions(options,...
'StepTolerance',1e-10,...
'ConstraintTolerance',1e-6);
将机械臂的物理约束转换为金融约束:
matlab复制function [risk,return] = portfolioOpt(weights)
% weights: 资产配置比例
covMatrix = load('covariance.mat'); % 加载历史数据
risk = sqrt(weights' * covMatrix * weights);
return = -sum(weights .* expectedReturns); % 最大化收益
end
function [c,ceq] = finConstraints(weights)
c = calculateVaR(weights) - 0.05; % VaR≤5%
ceq = sum(weights) - 1; % 总投入100%
end
关键发现:通过lambda结构体可以识别哪些资产对风险贡献最大,为调仓提供依据。
根据我处理过的50+案例,总结出以下经验:
必查清单:
性能优化技巧:
options.UseParallel = truematlab复制% 不好的写法
for i = 1:n
f = f + x(i)^2;
end
% 好的写法
f = sum(x.^2);
在最近的一个风电优化项目中,通过以上技巧将单次优化时间从45分钟缩短到3分钟。