Apollo MPC Controller

目录

[1.Apollo MPC Controller架构](#1.Apollo MPC Controller架构)

2.Mpc_Osqp类

2.1Mpc_Osqp类成员函数

2.2Mpc_Osqp类成员变量

2.3Mpc_Osqp初始化

2.4Mpc_Osqp类的执行顺序

3.MPC问题相关矩阵的构造

3.1优化变量z

3.2P矩阵的构造

3.3q矩阵的构造

3.4A矩阵的构造

3.5l和u的构造

4.MPC问题求解的实时性

4.1SolveLQRProblem

4.2SolveMPCProblem


Apollo MPC Controller中同时对横纵向控制进行求解,而本文只针对横向控制进行求解,其中会使用到OSQP求解器对MPC问题求解。

1.Apollo MPC Controller架构

下图为Apollo MPC Controller架构,本文主要关注MPC优化求解这一部分。

│ ├── MPC 优化求解

│ │ ├── 构建 QP 问题 (状态、控制、约束)

│ │ └── 调用 OSQP 求解器

2.Mpc_Osqp类

2.1Mpc_Osqp类成员函数

|-------------------------------|-----------------------------------------------------|
| 成员函数 | 作用 |
| MpcOsqp() | 构造函数 |
| Solve() | 针对MPC问题调用OSQP求解器求解 |
| CalculateKernel() | 构建 Hessian 矩阵 P (对应代价函数的二次项) |
| CalculateEqualityConstraint() | 构建等式约束(系统动态)和不等式约束(边界)的矩阵 A (OSQP 中统一处理为不等式 l≤Ax≤u) |
| CalculateGradient() | 计算线性项 q (即 gradient_),通常来自参考轨迹和代价矩阵 |
| CalculateConstraintVectors() | 构建约束上下界向量 lowerBound_ 和 upperBound_,对应 OSQP 的 l和u |
| Settings() | OSQP 的配置(如最大迭代、精度) |
| Data() | OSQP 的问题数据(P, q, A, l, u) |
| FreeData() | 释放 OSQP 数据结构的内存 |
| CopyData() | 将 std::vector 转换为 C 风格数组 |

2.2Mpc_Osqp类成员变量

|-------------------|-----------------------------------------------------------------|-------------------|
| 成员函数 | 维度 | 作用 |
| matrix_a_ | state_dim_ × state_dim_ | 状态矩阵A离散化 |
| matrix_b_ | state_dim_ × control_dim_ | 控制矩阵B离散化 |
| matrix_q_ | state_dim_ × state_dim_ | 状态权重矩阵Q |
| matrix_r_ | control_dim_ × control_dim_ | 控制权重矩阵R |
| matrix_initial_x_ | state_dim_ × 1 | 当前状态量 |
| matrix_u_lower_ | control_dim_ × horizon_ | 控制量下界 |
| matrix_u_upper_ | control_dim_ × horizon_ | 控制量上界 |
| matrix_x_lower_ | state_dim_ × (horizon_ + 1) | 状态量下界 |
| matrix_x_upper_ | state_dim_ × (horizon_ + 1) | 状态量上界 |
| matrix_x_ref_ | state_dim_ × (horizon_ + 1) | 参考状态量 |
| max_iteration_ | | 最大迭代次数 |
| horizon_ | | 预测步长 |
| eps_abs_ | | 求解精度 |
| state_dim_ | | 状态量维度 |
| control_dim_ | | 控制量维度 |
| num_param_ | state_dim_ * (N + 1) + control_dim_ * N | 总优化变量数 |
| num_constraint_ | state_dim_ * N(等式) + (control_dim_ + state_dim_) * N(不等式,若启用) | 总约束数量= 等式约束+不等式约束 |
| gradient_ | 同num_param_ | QP 目标函数线性项q |
| lowerBound_ | 同num_constraint_ | 所有约束的下界向量l |
| upperBound_ | 同num_constraint_ | 所有约束的上界向量u |

2.3Mpc_Osqp初始化

cpp 复制代码
MpcOsqp::MpcOsqp(const Eigen::MatrixXd &matrix_a,
                 const Eigen::MatrixXd &matrix_b,
                 const Eigen::MatrixXd &matrix_q,
                 const Eigen::MatrixXd &matrix_r,
                 const Eigen::MatrixXd &matrix_initial_x,
                 const Eigen::MatrixXd &matrix_u_lower,
                 const Eigen::MatrixXd &matrix_u_upper,
                 const Eigen::MatrixXd &matrix_x_lower,
                 const Eigen::MatrixXd &matrix_x_upper,
                 const Eigen::MatrixXd &matrix_x_ref, const int max_iter,
                 const int horizon, const double eps_abs)
    : matrix_a_(matrix_a),
      matrix_b_(matrix_b),
      matrix_q_(matrix_q),
      matrix_r_(matrix_r),
      matrix_initial_x_(matrix_initial_x),
      matrix_u_lower_(matrix_u_lower),
      matrix_u_upper_(matrix_u_upper),
      matrix_x_lower_(matrix_x_lower),
      matrix_x_upper_(matrix_x_upper),
      matrix_x_ref_(matrix_x_ref),
      max_iteration_(max_iter),
      horizon_(horizon),
      eps_abs_(eps_abs) {
  state_dim_ = matrix_b.rows();
  control_dim_ = matrix_b.cols();
  ADEBUG << "state_dim" << state_dim_;
  ADEBUG << "control_dim_" << control_dim_;
  num_param_ = state_dim_ * (horizon_ + 1) + control_dim_ * horizon_;
}

|--------------------|----------------------------------|
| 参数 | 含义 |
| matrix_ad_ | 状态矩阵A离散化 |
| matrix_bd_ | 控制矩阵B离散化 |
| matrix_q_updated_ | 状态权重矩阵 |
| matrix_r_updated_ | 控制权重矩阵 |
| matrix_state_ | 当前状态量[ed,dot_ed,efai,dot_efai] |
| lower_bound | 控制量下界 |
| upper_bound | 控制量上界 |
| lower_state_bound | 状态量下界 |
| upper_state_bound | 状态量上界 |
| reference_state | 参考状态量 |
| mpc_max_iteration_ | 最大迭代次数 |
| horizon_ | 预测步长 |
| mpc_eps_ | 求解精度 |

2.4Mpc_Osqp类的执行顺序

3.MPC问题相关矩阵的构造

将MPC问题转换为如下二次规划形式:

3.1优化变量z

优化变量从u变为z,维度增加,但是矩阵变得稀疏之后求解速度可以变得更快,还能对控制变量和状态变量进行约束。

Apollo 中将状态量x和控制量u一起作为优化变量z,是为了将 MPC 问题转化为一个具有高度稀疏结构的 QP 问题,从而能被 OSQP 等求解器高效、实时地求解。虽然变量数量增加了,但稀疏性带来的计算效率提升远远超过变量增加的开销,这是现代 MPC 实现(尤其在自动驾驶、机器人领域)的标准做法。

本质是:用更多变量 + 更多约束,换取矩阵的稀疏性,从而获得更快的求解速度

3.2P矩阵的构造

P矩阵是horizon+1个Q矩阵和horizon个R矩阵组合而成的对角矩阵,需要将其转换为CSC格式。

|-----------|-------------------------------------------------------------------------------|
| 数组 | 含义 |
| P_data | 所有非零元素的值,按列优先(column-major)顺序存储 |
| P_indices | 每个非零元素对应的行号 |
| P_indptr | 长度为 n + 1 的数组,col_ptr[j] 表示第 j 列的第一个非零元在 P_data中的索引; col_ptr[n] = 总非零元个数。 |

例如P矩阵为

1 0 0 0

2 5 0 0

3 0 6 0

4 0 7 8

P_data:[1, 2, 3, 4, 5, 6, 7, 8]

P_indices:[0, 1, 2, 3, 1, 2, 3, 3]

P_indptr:[0, 4, 5, 7, 8]

3.3q矩阵的构造

实际上作为参考值,在Apollo中都设置为0了。

cpp 复制代码
Eigen::MatrixXd reference_state = Eigen::MatrixXd::Zero(basic_state_size_, 1);

3.4A矩阵的构造

等式约束也统一写成不等式约束:

所有约束如下所示:

写成矩阵形式,可以得到A、l 、u矩阵的构造:

3.5l和u的构造

见3.4节。

4.MPC问题求解的实时性

对比LQR和MPC求解所用的时间,其中LQR中Riccati方程的求解采用的是迭代法。

4.1SolveLQRProblem

cpp 复制代码
void Mpc_Test::SolveLQRProblem() {
	Eigen::MatrixXd &A = matrixAd_;
	Eigen::MatrixXd &B = matrixBd_;
	Eigen::MatrixXd &Q = matrixQ_updated_;
	Eigen::MatrixXd &R = matrixR_;

	double tolerance = eps_;
	int max_num_iteration = max_iteration_;
	Eigen::MatrixXd *ptr_K = &matrixK_;

	if (A.rows() != A.cols() || B.rows() != A.rows() || Q.rows() != Q.cols()
			|| Q.rows() != A.rows() || R.rows() != R.cols()
			|| R.rows() != B.cols()) {
		return;
	}

	Eigen::MatrixXd AT = A.transpose();
	Eigen::MatrixXd BT = B.transpose();

// Solves a discrete-time Algebraic Riccati equation (DARE)
// Calculate Matrix Difference Riccati Equation, initialize P and Q
	Eigen::MatrixXd P = Q;
	int num_iteration = 0;
	float diff = std::numeric_limits<float>::max();
	Eigen::MatrixXd P_next = P;
	Eigen::MatrixXd P_next_diff = P;

    auto start = std::chrono::steady_clock::now();
	if (riccati_equation_solve_method_ == 1) {
		// 解析法求解
		// P = DiscreteAlgebraicRiccatiEquation(A, B, Q, R);
	} else if (riccati_equation_solve_method_ == 2) {
		// 迭代法求解
		while (num_iteration++ < max_num_iteration && diff > tolerance) {
			P_next = AT * P * A
					- AT * P * B * (R + BT * P * B).inverse() * BT * P * A + Q;
			P_next_diff = P_next - P;
			diff = fabs(P_next_diff.maxCoeff());
			P = P_next;
		}
	}
	*ptr_K = (R + BT * P * B).inverse() * BT * P * A;
    auto end = std::chrono::steady_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

    std::cout << "LQR solve time: " << ms << " ms" << std::endl;
}

4.2SolveMPCProblem

cpp 复制代码
void Mpc_Test::SolveMPCProblem() {
    auto start = std::chrono::steady_clock::now();
    // 存储osqp求解后的控制量
    Eigen::MatrixXd control_matrix = Eigen::MatrixXd::Zero(controls_, 1);
    std::vector<Eigen::MatrixXd> control(horizon_, control_matrix);

    // Eigen::MatrixXd control_gain_matrix = Eigen::MatrixXd::Zero(controls_, basic_state_size_);
    // std::vector<Eigen::MatrixXd> control_gain(horizon_, control_gain_matrix);

    // Eigen::MatrixXd addition_gain_matrix = Eigen::MatrixXd::Zero(controls_, 1);
    // std::vector<Eigen::MatrixXd> addition_gain(horizon_, addition_gain_matrix);

    // 构造相应的矩阵,用于MpcOsqp mpc_osqp初始化
    // lower_bound, upper_bound, lower_state_bound, upper_state_bound, reference_state,
    Eigen::MatrixXd reference_state = Eigen::MatrixXd::Zero(basic_state_size_, 1);
    std::vector<Eigen::MatrixXd> reference(horizon_, reference_state);

    Eigen::MatrixXd lower_bound(controls_, 1);
    lower_bound << -wheel_single_direction_max_degree_;

    Eigen::MatrixXd upper_bound(controls_, 1);
    upper_bound << wheel_single_direction_max_degree_;

    const double max = 1.0;
    Eigen::MatrixXd lower_state_bound(basic_state_size_, 1);
    Eigen::MatrixXd upper_state_bound(basic_state_size_, 1);

    // lateral_error, lateral_error_rate, heading_error, heading_error_rate
    // station_error, station_error_rate
    lower_state_bound << -1.0 * max, -1.0 * max, -1.0 * 3.1415926, -1.0 * max;
    upper_state_bound << max, max, 3.1415926, max;
    // mpc_osqp.Solve(&control_cmd)求解的结果
    std::vector<double> control_cmd(controls_, 0);
    // MpcOsqp mpc_osqp初始化
    MpcOsqp mpc_osqp(
        matrixAd_, matrixBd_, matrixQ_mpc_, matrixR_,
        matrixState_, lower_bound, upper_bound, lower_state_bound,
        upper_state_bound, reference_state, mpc_max_iteration_, horizon_,
        mpc_eps_);
    if (!mpc_osqp.Solve(&control_cmd)) {
        //std::cout << "MPC OSQP solver failed" << std::endl;
    } else {
        //std::cout << "MPC OSQP problem solved! " << std::endl;
        control[0](0, 0) = control_cmd.at(0);
        mpc_steer_angle_feedback_ = control[0](0, 0);
    }

    auto end = std::chrono::steady_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    // 打印MPC问题的求解时间
    std::cout << "MPC solve time: " << ms << " ms" << std::endl;
}
相关推荐
小王C语言3 小时前
【线程概念与控制】:线程封装
jvm·c++·算法
学习,学习,在学习3 小时前
Qt工控仪器程序框架设计详解(工控多仪器控制版本)
开发语言·c++·qt
信竞星球_少儿编程题库4 小时前
2026年全国信息素养大赛算法应用主题赛 丝路新城 C++ 模拟卷(三)
开发语言·c++
Zhang~Ling4 小时前
深入解析C++list:从0到1实现一个完整的链表类
c++·链表·list
longerVR5 小时前
自动驾驶(FSD/Autopilot)的数据采集-特斯拉纯视觉方案
人工智能·机器学习·自动驾驶
运维帮手大橙子5 小时前
自动驾驶各模块协作与本质
人工智能·机器学习·自动驾驶
王老师青少年编程5 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串综合】:[NOIP 2015 提高组] 子串
c++·字符串·csp·高频考点·子串·信奥赛
June`5 小时前
redis项目之命令解析器
数据库·c++·redis
rGzywSmDg5 小时前
如何在Dev-C++中设置TDM-GCC为默认编译器
开发语言·c++