
简介GPOPS2是一款基于高斯伪谱法和序列二次规划的MATLAB数值优化工具主要针对多阶段动态优化问题在飞行器轨迹规划、航天器轨道设计、机器人路径规划等领域应用广泛能够处理复杂约束下的最优控制问题。压缩包共259个文件含136个P代码文件、116个M函数文件、1个PDF手册以及MEX编译文件等包体约8.89MB。该资源已有8077人学习下载。软件内置高斯伪谱法离散化和SQP求解器可通过高斯节点将连续最优控制问题转化为非线性规划再迭代求解最优轨迹用户能借助MATLAB接口定义运动方程、约束条件与性能指标并利用内置示例脚本如rlvAscentMain、launchMain快速上手。PDF使用手册详细讲解安装步骤、语法规则、参数调整方法并提供与Simulink集成的案例帮助工程师和研究人员解决实际动态优化难题。 搞最优控制的人多少都听过GPOPS2的大名。它是普渡大学Anil Rao课题组发布的一套MATLAB工具箱核心解决的是非线性最优控制问题的数值求解用的方法叫高斯伪谱法。简单说就是把你手头从导弹弹道规划、卫星轨道机动到机器人关节轨迹优化这类“寻找满足微分方程约束的最优轨迹”问题转化成一个大规模稀疏非线性规划问题再交给SNOPT或IPOPT这类NLP求解器去算。和传统的直接打靶法、间接法相比它最大的好处是不需要你手动推导伴随方程也不用太担心初值猜得不好就发散网格还能自适应加密非常省心。不过话说回来GPOPS2虽然好用安装和上手却劝退了不少人。软件包的目录结构、NLP求解器的选择、mex编译、路径设置每一步都有不少隐性坑。网上资料要么是官方wiki的英文说明要么就是零散的论坛提问帖很少有把“从下载安装到跑通第一个例子再到写自己的问题”串起来的完整过程。这篇博文我就把自己实际折腾GPOPS2的经验整理出来包括软件包获取、安装方式、环境配置、常见报错排查以及如何用它实现一个完整的最优控制问题希望能让后面的人少走点弯路。1. GPOPS2到底是什么高斯伪谱法工具箱的核心价值1.1 伪谱法的基本思路和GPOPS2的定位伪谱法本质上是在时间区间上选取一组插值节点比如Legendre-Gauss点、Legendre-Gauss-Radau点把连续的状态和控制变量用全局插值多项式近似然后在节点上强制满足动力学方程。这样原来的微分方程约束就变成了代数约束一个连续时间的最优控制问题就变成了一个带稀疏结构的非线性规划问题。GPOPS2用的是LG点Legendre-Gauss点所以叫“高斯伪谱法”。GPOPS2这个“2”不是简单升级而是实现了一次架构重构。第一代GPOPS需要用户自己提供梯度信息二阶导数也要额外处理GPOPS2内部集成了自动微分ADiGator可以自动计算目标函数和约束条件对决策变量的一阶、二阶导数再结合稀疏雅可比和海森矩阵的构造方法让大规模NLP求解器的效率提升非常明显。另一个大变化是增加了hp自适应网格细化能力通俗说就是求解时不仅自动加细时间网格还能根据误差分布自动决定“在哪一段多加配点”和“把哪一段拆成更多小段”这是老版本做不到的。1.2 它能解决的问题类型与典型应用场景从数学形式上说GPOPS2能够处理多阶段multi-phase最优控制问题。每个阶段有独立的时间区间、动力学方程、路径约束、事件约束和目标函数中的积分项阶段之间还可以有耦合约束。这个能力非常关键因为它让很多工程问题建模变得自然。我实际见过的应用里出现频率最高的几类航天领域的轨迹优化典型如月球软着陆、小推力转移轨道、再入飞行器轨迹设计。这类问题强非线性、强约束对求解器容错能力要求很高。机器人领域的运动规划例如欠驱动机械臂的时间最优轨迹、腿足机器人落脚点规划。自动控制中的边界最优控制问题比如最小燃料、最小能量的LQR类问题变体。动力学参数估计、最优实验设计这类更边缘的用法也有人拿GPOPS2在做。如果你面对的是一个“给定初始状态、想要终端状态同时满足动力学和过程约束还要优化某个性能指标”的问题GPOPS2基本就是一个非常合适的现成工具。它最漂亮的地方在于你不需要关心内部NLP迭代的细节只需要把问题“翻译”成它要求的函数接口。2. 安装前置工作环境评估、组件选型与软件包获取2.1 MATLAB版本与C编译器兼容性检查很多人一上来就下载软件包然后盲目添加路径结果运行示例时报一堆红字。我建议在安装GPOPS2之前先花十分钟检查两件事MATLAB版本和可用的C编译器。GPOPS2的开发时间比较早官方明确支持的是R2010b到R2018a左右。但并不意味着新版本MATLAB不能用R2020a、R2023a我都实测过只要mex文件重新编译、个别兼容性补丁处理掉照样跑得很稳。关键是别用太旧的版本比如R2014a以下因为GPOPS2的一些代码依赖较新的函数特性太旧反而容易出问题。编译器方面如果你打算使用SNOPT那就很省心因为SNOPT官方提供的mex文件一般已经编译好只要平台一致就能直接用。但如果你想用IPOPT这个开源求解器就需要自己编译mex接口。这就牵扯到C编译器和MATLAB内置编译器版本的匹配问题。在Windows上我建议装一个与MATLAB版本匹配的MinGW-w64MATLAB官方插件或者Microsoft Visual C Build Tools。在Linux上则是gcc版本要对得上MATLAB内置的API版本建议先用mex -setup确认当前默认编译器再去编译IPOPT。2.2 NLP求解器SNOPT/IPOPT/fmincon怎么选GPOPS2本身不负责解NLP它只负责把最优控制问题离散化、生成导数信息最后一步把NLP问题交给外部求解器。所以你必须至少准备好一个NLP求解器。三种常见选择的取舍如下求解器开源/商业安装难度求解性能适用场景SNOPT商业需授权低多为现成mex强尤其适合稀疏大问题科研、工程生产环境条件允许首选IPOPT开源较高需要编译强配置好全性能不差学生、预算受限乐于折腾fminconMATLAB自带零安装一般中小规模还行快速验证问题模型临时暂用我的个人建议是如果只是学习、验证算法先用fmincon把手头问题跑通确认建模没问题再换IPOPT或SNOPT做正式计算。如果一开始就卡在求解器安装上很容易把“GPOPS2不好使”误判成“我的问题无解”两头受气。2.3 从下载到解压软件包目录与路径规范GPOPS2的软件包托管在SourceForge上直接搜“GPOPS2 SourceForge”就能找到。下载下来是一个压缩包名称大致是GPOPSII.zip或者GPOPSII_XXX.zip。此外GitHub上也有一些社区维护的版本修复了新MATLAB兼容性问题如果原版在运行示例时频繁报错可以试试GitHub上的更新分支。解压这个步骤看似简单但有两个坑我见过太多次了第一是解压路径不能带中文或空格。很多人的用户名是中文默认的解压目录就成了C:\用户\张三\Downloads\GPOPS2MATLAB的老版本在处理这类路径时会出现诡异的错误而且定位起来特别费劲。建议解压到一个纯英文、无空格的目录比如D:\Tools\GPOPSII。第二是目录层级不要搞错。压缩包解压后最外层应该是一个包含Main、Utilities、examples、setup等子文件夹的目录。有些压缩包解压后外层还会多包一层同名目录需要看清楚确定好路径再添加。另外提醒一句GPOPS2是MATLAB工具箱不是Linux发行版里的那种“软件包”不需要用系统包管理器安装也别用“选中未选择的软件包”这种方式去装——它本质上就是一批.m、.mex和.mat文件的集合在MATLAB里设置路径即可。3. 一步一步把GPOPS2装进你的MATLAB3.1 路径设置的正确姿势把GPOPS2解压好之后第一步就是告诉MATLAB上哪找这些函数。最稳妥的方式是addpath(genpath(D:\Tools\GPOPSII)); savepath;genpath会把GPOPSII目录下所有子目录都加入搜索路径这样GPOPS2.m、setup函数、示例脚本就都能找到了。这里我特别强调两点第一savepath很重要。如果你只是addpath一旦重启MATLAB就失效了下次又得重新加。保存路径时如果有文件夹只读会报警告把整个GPOPSII目录设为可写即可。第二尽量不要把GPOPS2放在MATLAB的安装目录里比如D:\MATLAB\R2023a\toolbox\local虽然也能用但后续升级、卸载都麻烦。独立一个工具目录删掉也干净。3.2 编译IPOPT的完整过程与参数说明如果你手头没有SNOPT授权又想用高性能开源求解器那就得编译IPOPT。我用的是Windows MATLAB R2023a的实测流程Linux上也类似。首先去IPOPT官网下载源码包一般叫Ipopt-3.12.13.tgz或类似版本。解压后用CMake配置工程开启BUILD_MEX选项或者使用mex接口的源码目录。这里最坑的是底层依赖库IPOPT需要BLAS/LAPACK可选HSL或MUMPS线性求解器。初次尝试的人常常卡在这一步。我的建议是直接使用MATLAB环境中自带的BLAS库不额外装LAPACK操作上就是CMake配置时指定IMATH_LIB等参数为matlab的方式。还有一种更省事的办法直接用社区已经编译好的mex文件网上有不少人分享或者把IPOPT安装任务交给coinbrew脚本自动处理。不过从零编译一次虽然烦但好处是你对求解器内部的库依赖会有更清晰的理解后面排查问题会从容很多。编译完成后把生成的ipopt.mexw64或ipopt.mexa64放到一个固定目录然后在MATLAB里同样加进路径。在GPOPS2的setup.m结构中将NLP求解器指定为ipopt即可。3.3 验证安装用最速降线示例做冒烟测试装好之后不要急着写自己的问题先用GPOPS2自带的示例验证环境。官方包里最经典的入门示例是MonoLunarRocket、Brachistochrone最速降线和BrysonDenham。以最速降线为例打开examples下的Brachistochrone文件夹运行里面的主脚本观察输出。如果顺利你会看到网格细化迭代过程最后打印出最优时间和状态曲线。一旦这一步通了说明路径、求解器、自动微分链路全部正常。如果运行报错先别慌从最后一条错误信息往上翻看是出在GPOPS2.m调用NLP求解器的那一步还是出在用户函数求值的那一步。前者多半是求解器配置问题后者多半是你修改示例代码时破坏了函数签名。3.4 在Linux类系统上安装的额外注意事项Linux下安装GPOPS2大体一致但有三个细节值得单独说。一是mex编译需要gcc版本与MATLAB兼容。不同MATLAB版本对gcc版本要求差异很大R2020a要求gcc 6.3R2023a要求gcc 11.x。如果系统自带gcc版本过新要么装旧版本要么在mex -setup时手动指定编译器路径。二是权限和依赖。有些Linux发行版的软件包管理逻辑比较特殊安装第三方库时容易出现依赖冲突比如“正在读取数据库”类卡顿或者“软件包似乎无效”这类提示。GPOPS2本身不涉及系统软件包但如果编译IPOPT时用系统的BLAS库要确保libblas-dev等包正确安装否则编译过程会报找不到库文件。三是LD_LIBRARY_PATH。如果mex文件依赖某些动态库而系统找不到MATLAB会在启动时报libxxx.so: cannot open shared object file。解决办法是把动态库所在目录导出到LD_LIBRARY_PATH或者在启动脚本里写死别在命令行临时设置省得每次重启MATLAB都要重新配。4. 使用说明一个最优控制问题从建模到求解的完整流程4.1 理解GPOPS2的三层结构problem、setup、solutionGPOPS2使用起来有一个清晰的“三层结构”理解了它一切示例代码都大同小异。problem结构体描述你的最优控制问题包括时间区间、状态变量初值、控制变量、目标函数、动力学方程、路径约束、网格初值猜想等。setup结构体描述求解配置包括网格细化方法、NLP求解器、容差、自动尺度化开关、导数计算方式等由GPOPS2自带的setup函数生成。solution结构体求解结果在调用GPOPS2(problem, setup)后返回里面有各阶段的time、state、control、integral以及网格细化信息。写代码的时候通常是先定义setup setup();然后再修改其中字段比如setup.mesh.method hp、setup.nlp.solver ipopt。而problem则是一笔一画填出来的。两者分离好处是同一个问题可以快速切换不同求解配置方便做对比实验。4.2 连续函数文件与端点函数文件的写法GPOPS2通过函数句柄回调你的物理模型最核心的是两类函数连续函数continuous function和端点函数endpoint function。连续函数文件形如function output myContinuous(input) t input.phase.time; x input.phase.state; u input.phase.control; % 动力学 output.dynamics [x(2); u(1)]; % 路径约束 output.path [x(1) - 1]; end端点函数文件形如function output myEndpoint(input) t0 input.phase.initialtime; tf input.phase.finaltime; x0 input.phase.initialstate; xf input.phase.finalstate; % 目标函数Mayer项 积分项 output.objective xf(2) input.phase.integral; % 事件约束边值条件 output.eventgroup(1).event [x0(1), x0(2)]; end这里要特别注意input.phase.integral的使用如果你在连续函数里定义了output.integrand这个积分值会被自动算出来并出现在端点函数的input.phase.integral里。这是处理最短时间、最小燃料这类积分型指标的标准通道。4.3 边界条件、路径约束与目标函数的写法细节边界条件写在端点函数的事件组里。可以定义多个事件组eventgroup这一点很重要因为在多阶段问题中阶段之间的连接约束就是通过把前一个阶段的末端状态和后一个阶段的初始状态放进同一个事件组来实现的。路径约束写在连续函数里比较典型的是控制约束和控制变化率约束。举个例子如果控制量需要满足|u| 1那就写成output.path u * u - 1;让GPOPS2理解为不等式约束output.path 0。目标函数的部分我经常看到新手把Mayer项和积分项写混。Mayer项是只跟端点状态有关的项比如终端速度积分项是拉格朗日型性能指标比如全程燃料消耗。GPOPS2支持两者同时存在只要在端点函数里把output.objective设置成两者的加权和即可。加权本身也经常是个调试手段如果求解不收敛先把终端惩罚项权重调小跑通后再逐渐加大。4.4 初值猜想不理想时怎么办初值猜想guess是所有直接法工具里的头号痛点。GPOPS2对初值的要求已经比打靶法宽松很多但也不能随便猜。我的经验是分三步第一步用时间线性插值猜状态控制量取一个“物理上合理”的常数或小扰动第二步先用最粗的网格、较宽松的容差跑一次不一定收敛到高精度解但要能给出一个合理的轨迹第三步把这次结果当作新的guess再细化网格、收紧容差。这招在处理强非线性问题时特别管用相当于人为制造了一个“热启动”过程。另外GPOPS2自带的初始网格是setup.mesh.initial里定义的默认一般是10个区间、每个区间3个配点。如果初值非常粗糙可以故意把网格配点数增多一点虽然单次迭代慢但能给NLP求解器更宽容的可行域。4.5 结果提取与mesh细化信息解读调用GPOPS2后返回的solution结构体里最常用的是t solution.phase.time; x solution.phase.state; u solution.phase.control; obj solution.result.objective;如果你需要输出控制变量曲线、状态曲线直接plot(t, x)就行。不过我更推荐多看一眼solution.info里的mesh迭代历史。GPOPS2的hp自适应网格会输出每一次迭代的最大相对误差error和网格区间数、配点数。如果误差连续两次没有明显下降基本可以判断问题本身有奇异性或约束冲突而不是网格不够细。solution.result.nlpinfo也很关键它返回NLP求解器的退出状态和迭代次数。比如用IPOPT时nlpinfo.exitflag对应IPOPT的返回值把退出码对照IPOPT文档比看MATLAB包装后的报错要准确得多。5. 踩坑实录让新用户崩溃的常见报错与排查思路5.1 路径没设置导致的“Undefined function”GPOPS2刚装完最常见也最原始的报错是Undefined function or variable GPOPS2.原因十个里有九个是路径没加对。注意addpath(genpath(...))和addpath(...)的差别genpath是递归子目录的少了它主函数里的setup子函数就找不到。另外务必确认当前工作目录不在GPOPS2的某个子文件夹里因为MATLAB有一类“shadow”问题当前目录下的同名脚本会覆盖工具箱函数。5.2 mex编译失败编译器版本与底层库不匹配用IPOPT时最容易翻车的是mex编译。报错形式千奇百怪有的是Error using mex有的是找不到头文件还有一堆undefined reference to链接错误。我调试时最有效的排查顺序是先在MATLAB里跑mex -setup确认编译器和API版本。单独编译IPOPT的mex源码不通过GPOPS2看能不能生成.mexw64。确认BLAS/LAPACK库路径无误在MATLAB里用[LAPACK_lib, BLAS_lib] lapacklib()检查当前MATLAB内部库。如果你实在不想折腾直接在GPOPS2的setup.nlp.solver改成fmincon快速验证问题建模是否正确也是完全合理的临时方案。5.3 “软件包似乎无效”与解压异常类问题有些用户喜欢在Linux上通过unzip解压GPOPS2压缩包偶尔遇到压缩包损坏或下载不完整。系统会提示类似“压缩文件损坏”或“软件包似乎无效”。这个时候先重新下载断点续传导致的文件损坏非常常见。最好用sha256sum校验一下下载文件的哈希值官方页面通常会提供校验值。如果你在Windows上解压注意不要用系统自带的“打开方式”直接打开压缩包内部文件而要用右键“全部解压”。我看到过有人直接在压缩包预览窗口里把几个.m文件拖出来放到自定义目录结果缺少了子目录里的关键文件后面运行报错怎么都查不出来。5.4 求解器没选对导致收敛失败的真实案例之前帮一个朋友排查卫星轨道转移问题他用的fmincon结果反复迭代都停在同一个不可行点上。查了代码动力学、约束、目标都没有问题问题出在setup.nlp.solver默认还是snopt而他的机器没有SNOPT授权导致GPOPS2实际根本没调用他预设的fmincon。这类“配置覆盖失败”的问题非常隐蔽因为GPOPS2不会每次都打印“你设置的求解器没用上”。排查方法很简单在求解之前打印一下setup结构体确认setup.nlp.solver的最终值到底是什么。还有fmincon解法器需要明确的optimoptions如果没设置也会有奇怪的收敛行为。5.5 常见报错对照表报错现象最常见原因解决思路Undefined function or variable GPOPS2路径未使用genpath递归重新addpath(genpath(...))SNOPT license errorSNOPT未正确授权换IPOPT或fminconipopt.mexw64 cannot be foundmex文件未加入路径将mex所在目录加入路径Error using mex at compile编译器版本不匹配切换MinGW或匹配版本gccfmincon stopped because it exceeded FunctionTolerance初值太差或容差设置太严放宽容差、转热启动Index exceeds matrix dimensions状态或控制维度与guess不匹配检查guess.state与bound.initial状态数量6. 从跑通Demo到解决自己的问题工程化经验6.1 如何把自带示例改造成自己的问题完全从零写一个GPOPS2问题其实不难但更快的路径是“复制一个自带示例然后逐步替换”。我会选一个和自己问题最像的示例比如做轨迹优化的选MonoLunarRocket做一般机械系统的最小时间问题选Brachistochrone。改造的步骤一般是复制整个示例文件夹改成自己的名字。打开主脚本修改problem.bounds里的时间区间、状态上下界、控制上下界。修改连续函数文件和端点函数文件换成自己的动力学和约束。修改初值猜想建议先在时间轴上等间距采样状态初值用边界上下限的中间值。这一步最需要注意的是状态和控制变量的维度一致性。很多报错都来自state有四维但guess里只写了三维。建议在代码里加一句断言assert(size(problem.guess.state, 2) numel(problem.bounds.initial.state.lower));6.2 变量尺度化与数值稳定性的判断GPOPS2有自动尺度化机制在setup.scales.method里可配置为automatic或automatic-bounds。我实测下来对大多数问题开启自动尺度化就够了。但如果你发现NLP求解器迭代缓慢、目标函数变化很小很可能是某个变量量级差异太大。比如轨道半径是百万公里量级而质量是千千克量级动力学方程里的数值会差到十几个数量级。这时需要手动给状态或控制设置缩放因子。在problem.bounds里可以把上下界设置为实际物理量的1.0倍或0.1倍然后配合setup.scales把后续量级拉回[-1,1]附近。这个技巧看着简单但往往是求解器性能的最后一公里。6.3 提高求解成功率和速度的几条经验第一对非线性度较高的路径约束比如姿态指向约束、路径曲率约束起步时可以先不加或者放宽等动力学轨迹基本合理了再收紧。第二尽量把状态数量降到最低。有时候为了写约束方便新手会引入冗余状态但每多一个状态NLP的维度就上升一截求解速度下降不少。第三多阶段建模能显著提高数值稳定性。比如地面滑跑、爬升、巡航分三个阶段建模比一个阶段塞全部动力学要稳得多收敛概率也高得多。最后再说一个我自己的习惯拿到任何GPOPS2结果都会顺手做一次“半程验证”——把解出的控制量带回原始动力学方程用ode45前向积分看看终端状态是否和目标一致。这一步能帮你筛掉一大部分由于伪谱法配点误差导致的“看起来最优但实际上不可行”的解。毕竟求解器再强大它也是一个数值工具我们始终要对物理结果保持判断力。本文还有配套的精品资源点击获取