如何复用已有casadi::Function成本函数实现CasADI优化?
复用CasADI成本/梯度函数实现CasADI优化的两种方案
方案一:用casadi::Callback封装现有函数
你可以自定义一个Callback类,把已有的final_result_fun包装进去,适配nlpsol对目标函数(含梯度)的输入输出要求:
- 定义Callback子类
class CostGradientCallback : public casadi::Callback { public: // 构造函数传入已有的成本梯度计算函数 CostGradientCallback(casadi::Function fun) : fun_(std::move(fun)) {} // 重写eval方法,处理输入输出转换 void eval(const std::vector<casadi::DM>& arg, std::vector<casadi::DM>& res) override { // 构造final_result_fun的完整输入:优化变量u + 提前准备好的固定参数 std::vector<casadi::DM> fun_input = { arg[0], // 优化变量u init_x_val, // 预存的init_x数值 obj_list_1_val, // 预存的obj_list_1数值 obj_list_2_val, // 预存的obj_list_2数值 dt_val, // 预存的dt数值 cost_val // 预存的cost数值 }; // 调用现有函数计算成本和梯度 auto fun_output = fun_(fun_input); // 输出结果:成本+梯度 res.resize(2); res[0] = fun_output[0]; res[1] = fun_output[1]; } // 声明输入输出数量 casadi::casadi_int n_in() override { return 1; } // 仅输入优化变量u casadi::casadi_int n_out() override { return 2; } // 输出成本和梯度 private: casadi::Function fun_; // 预存固定参数的DM值(需从业务逻辑中获取实际值) casadi::DM init_x_val; casadi::DM obj_list_1_val; casadi::DM obj_list_2_val; casadi::DM dt_val; casadi::DM cost_val; };
- 实例化Callback并构建求解器
// 准备固定参数的实际数值 casadi::DM init_x_val = ...; casadi::DM obj_list_1_val = ...; casadi::DM obj_list_2_val = ...; casadi::DM dt_val = ...; casadi::DM cost_val = ...; // 创建Callback实例并绑定参数 CostGradientCallback cost_grad_cb(final_result_fun); cost_grad_cb.init_x_val = init_x_val; cost_grad_cb.obj_list_1_val = obj_list_1_val; cost_grad_cb.obj_list_2_val = obj_list_2_val; cost_grad_cb.dt_val = dt_val; cost_grad_cb.cost_val = cost_val; // 定义优化变量符号 casadi::MX u = casadi::MX::sym("u", u_sym.size1(), u_sym.size2()); // 用Callback构建目标函数(含梯度) casadi::MX f = cost_grad_cb(u)[0]; casadi::MX grad_f = cost_grad_cb(u)[1]; // 构建NLP问题(无约束示例,有约束可添加"g"和"h"字段) casadi::MXDict nlp = {{"x", u}, {"f", f}, {"grad_f", grad_f}}; // 创建nlpsol求解器(以IPOPT为例,可替换为sqpmethod等) casadi::Function solver = casadi::nlpsol("solver", "ipopt", nlp, { {"ipopt.print_level", 0}, {"print_time", false} }); // 设置初始猜测值 casadi::DM u0 = ...; // 求解优化问题 casadi::DMDict sol = solver({{"x0", u0}}); // 获取最优解 casadi::DM u_opt = sol["x"]; casadi::DM f_opt = sol["f"];
方案二:直接用nlpsol整合符号化成本与梯度
如果final_result_fun基于符号变量构建,可直接将符号化的成本和梯度整合到NLP定义中,无需Callback:
- 提取符号化的成本与梯度
// 定义优化变量和固定参数的符号 casadi::MX u = u_sym; casadi::MX init_x = init_x_sym; casadi::MX obj_list_1 = obj_list_1_sym; casadi::MX obj_list_2 = obj_list_2_sym; casadi::MX dt = dt_sym; casadi::MX cost = cost_sym; // 调用函数获取符号化的成本和梯度 casadi::MXArray output = final_result_fun({u, init_x, obj_list_1, obj_list_2, dt, cost}); casadi::MX f = output[0]; casadi::MX grad_f = output[1]; // 将固定参数替换为实际数值(可选,也可作为NLP参数传入) // init_x = casadi::DM(...);
- 构建NLP并求解
// 构建NLP字典 casadi::MXDict nlp = { {"x", u}, {"f", f}, {"grad_f", grad_f} // 手动提供梯度,避免自动微分 }; // 创建求解器 casadi::Function solver = casadi::nlpsol("solver", "ipopt", nlp, { {"ipopt.max_iter", 100}, {"print_time", true} }); // 求解 casadi::DMDict sol = solver({{"x0", u0}}); casadi::DM u_opt = sol["x"];
关键注意事项
- 固定参数需提前转为
casadi::DM类型,可直接替换符号变量,或通过NLP的"p"字段作为参数传入求解器。 - 选择求解器时,根据问题类型(凸/非凸)选合适后端(如IPOPT适合非凸,OSQP适合凸二次规划)。
- 手动提供梯度可保证和NLopt逻辑一致,确保性能对比的公平性。
内容的提问来源于stack exchange,提问作者Rémi.T
相关产品推荐
相关产品推荐

