MPC源码解读及路径跟踪demo

MPC源码解读及路径跟踪demo

附赠自动驾驶最全的学习资料和量产经验:链接

三、MPC 源码解读

需要注意:理论推导和代码实现存在较大的差异性

1、算法实现中是将预测过程得到的 中间状态量+控制量 全部放在一个大矩阵中进行计算求解的;

2、理论推导中控制量使用的是 速度和方向盘角,代码实现中使用的是 加速度和方向盘角(更符合实际情况);

3、理论推到中使用的是误差模型,代码实现中使用的是 路径相对位置(y轴偏差和朝向偏差),所以存在坐标系的转换;

4、代码实现中使用 IPOPT求解器,需要进行设置(代码注释中有解释)。

// MPC.HPP文件

/**
 * @file mpc.hpp
 * @brief  mpc控制器
 * @author annotation by author mce
 * @date 2024-5-25
 */
#ifndef MPC_H
#define MPC_H
#define HAVE_CSTDDEF
#include <cppad/cppad.hpp>
#include <cppad/ipopt/solve.hpp>
#undef HAVE_CSTDDEF
#include <vector>

#include "../src/Eigen-3.3/Eigen/Core"

// CppAD::vector<double>
typedef CPPAD_TESTVECTOR(double) Dvector;

// 预测步长
const int N = 50;
// 控制周期
const double dt = 0.02;
// 轴距
const double Lf = 2.5;
// 最大速度
const double VELOCITY_MAX = 100.0;
// 状态量的数量( px, py, psi朝向, v速度, cte横向偏差, epsi角度偏差 )
const int NUMBER_OF_STATES = 6;
// 控制量个数( steering angle 方向盘角度, acceleration 加速度)
const int NUMBER_OF_ACTUATIONS = 2;
// 未来一段时间状态量总个数 + 未来一段时间控制量总个数
const int NX = N * NUMBER_OF_STATES + (N - 1) * NUMBER_OF_ACTUATIONS;
// 约束个数
const int NG = N * NUMBER_OF_STATES;

// 状态量
const int ID_FIRST_px = 0;
const int ID_FIRST_py = ID_FIRST_px + N;
const int ID_FIRST_psi = ID_FIRST_py + N;
const int ID_FIRST_v = ID_FIRST_psi + N;
const int ID_FIRST_cte = ID_FIRST_v + N;
const int ID_FIRST_epsi = ID_FIRST_cte + N;
// 控制量
const int ID_FIRST_delta = ID_FIRST_epsi + N;  // 方向盘角度
const int ID_FIRST_a = ID_FIRST_delta + N - 1; // 加速度
// 状态量权重
const double W_cte = 1500.0;  // y轴偏差 1500
const double W_epsi = 1500.0; // 朝向偏差 1500
const double W_v = 1.0;       // 速度
// 控制量权重
const double W_delta = 10.0; // 方向盘角度 10
const double W_a = 10.0;     // 加速度  10
// 控制量变化量的变化率
const double W_ddelta = 150.0; // 150 weight cost for high difference between consecutive steering actuations
const double W_da = 15.0;      // 15 weight cost for high difference between consecutive acceleration actuations

class MPC {
public:
    double steer;    // 反向盘角度
    double throttle; // 油门(加速度)

    // 存放所有的状态和驱动变量
    Dvector x;
    // x变量的上下限
    Dvector x_lowerbound; // lower limit for each corresponding variable in x
    Dvector x_upperbound; // upper limit for each corresponding variable in x
    //
    Dvector g_lowerbound; // value constraint for each corresponding constraint expression
    Dvector g_upperbound; // value constraint for each corresponding constraint expression

    // 预测状态的X、Y
    std::vector<double> future_xs;
    std::vector<double> future_ys;

    MPC();
    virtual ~MPC();
    // MPC 求解器
    void solve(Eigen::VectorXd state, Eigen::VectorXd K);
};
#endif /* MPC_H */

// MPC.CPP文件

#include "../include/MPC.hpp"
using CppAD::AD;
using namespace std;

class FG_eval {
public:
    // 传入的轨迹
    Eigen::VectorXd K;

    FG_eval(Eigen::VectorXd Kin) : K(Kin) {}

    typedef CPPAD_TESTVECTOR(AD<double>) ADvector;
    // fg 包含代价和所有约束的向量
    // x 包含所有状态量和控制量
    void operator()(ADvector &fg, const ADvector &x) {
        //*********************************************************
        //* COST DEFINED HERE 状态量+控制量 代价
        //*********************************************************
        fg[0] = 0.0;
        for (int i = 0; i < N; ++i) {
            // y轴偏差
            const auto cte = x[ID_FIRST_cte + i];
            // 朝向偏差
            const auto epsi = x[ID_FIRST_epsi + i];
            // 速度
            const auto v = x[ID_FIRST_v + i] - VELOCITY_MAX;
            fg[0] += (W_cte * cte * cte + W_epsi * epsi * epsi + W_v * v * v);
        }

        for (int i = 0; i < N - 1; ++i) {
            // 方向盘角度
            const auto delta = x[ID_FIRST_delta + i];
            // 加速度
            const auto a = x[ID_FIRST_a + i];
            fg[0] += (W_delta * delta * delta + W_a * a * a);
        }

        for (int i = 0; i < N - 2; ++i) {
            // 方向盘角度变化速度
            const auto ddelta = x[ID_FIRST_delta + i + 1] - x[ID_FIRST_delta + i];
            // 加速度变化速度
            const auto da = x[ID_FIRST_a + i + 1] - x[ID_FIRST_a + i];
            fg[0] += (W_ddelta * ddelta * ddelta + W_da * da * da);
        }

        //*********************************************************
        //* CONSTRAINTS DEFINED HERE  约束
        //*********************************************************

        // given state does not vary 是不是延时?
        fg[ID_FIRST_px + 1] = x[ID_FIRST_px];
        fg[ID_FIRST_py + 1] = x[ID_FIRST_py];
        fg[ID_FIRST_psi + 1] = x[ID_FIRST_psi];
        fg[ID_FIRST_v + 1] = x[ID_FIRST_v];
        fg[ID_FIRST_cte + 1] = x[ID_FIRST_cte];
        fg[ID_FIRST_epsi + 1] = x[ID_FIRST_epsi];

        // constraints based on our kinematic model
        for (int i = 0; i < N - 1; ++i) {
            // where the current state variables of interest are stored
            // stored for readability
            const int ID_CURRENT_px = ID_FIRST_px + i;
            const int ID_CURRENT_py = ID_FIRST_py + i;
            const int ID_CURRENT_psi = ID_FIRST_psi + i;
            const int ID_CURRENT_v = ID_FIRST_v + i;
            const int ID_CURRENT_cte = ID_FIRST_cte + i;
            const int ID_CURRENT_epsi = ID_FIRST_epsi + i;
            const int ID_CURRENT_delta = ID_FIRST_delta + i;
            const int ID_CURRENT_a = ID_FIRST_a + i;

            // current state and actuations
            const auto px0 = x[ID_CURRENT_px];
            const auto py0 = x[ID_CURRENT_py];
            const auto psi0 = x[ID_CURRENT_psi];
            const auto v0 = x[ID_CURRENT_v];
            const auto cte0 = x[ID_CURRENT_cte];
            const auto epsi0 = x[ID_CURRENT_epsi];
            const auto delta0 = x[ID_CURRENT_delta];
            const auto a0 = x[ID_CURRENT_a];

            // next state
            const auto px1 = x[ID_CURRENT_px + 1];
            const auto py1 = x[ID_CURRENT_py + 1];
            const auto psi1 = x[ID_CURRENT_psi + 1];
            const auto v1 = x[ID_CURRENT_v + 1];
            const auto cte1 = x[ID_CURRENT_cte + 1];
            const auto epsi1 = x[ID_CURRENT_epsi + 1];

            // desired py and psi
            const auto py_desired = K[3] * px0 * px0 * px0 + K[2] * px0 * px0 + K[1] * px0 + K[0];
            const auto psi_desired = CppAD::atan(3.0 * K[3] * px0 * px0 + 2.0 * K[2] * px0 + K[1]);

            // 推导下一时刻车辆的状态
            const auto px1_f = px0 + v0 * CppAD::cos(psi0) * dt;
            const auto py1_f = py0 + v0 * CppAD::sin(psi0) * dt;
            const auto psi1_f = psi0 + v0 * tan(delta0) / Lf * dt;
            const auto v1_f = v0 + a0 * dt;
            const auto cte1_f = py_desired - py0 + v0 * CppAD::sin(epsi0) * dt;
            const auto epsi1_f = psi0 - psi_desired + v0 * tan(delta0) / Lf * dt;

            // store the constraint expression of two consecutive states
            fg[ID_CURRENT_px + 2] = px1 - px1_f;
            fg[ID_CURRENT_py + 2] = py1 - py1_f;
            fg[ID_CURRENT_psi + 2] = psi1 - psi1_f;
            fg[ID_CURRENT_v + 2] = v1 - v1_f;
            fg[ID_CURRENT_cte + 2] = cte1 - cte1_f;
            fg[ID_CURRENT_epsi + 2] = epsi1 - epsi1_f;
        }
    }
};

MPC::MPC() {
    //**************************************************************
    //* SET INITIAL VALUES OF VARIABLES  初始化变量
    //**************************************************************
    this->x.resize(NX);
    // all states except the ID_FIRST are set to zero the aformentioned states will be initialized when solve() is called
    for (int i = 0; i < NX; ++i) {
        this->x[i] = 0.0;
    }

    //**************************************************************
    //* SET UPPER AND LOWER LIMITS OF VARIABLES 设置变量的约束条件
    //**************************************************************
    this->x_lowerbound.resize(NX);
    this->x_upperbound.resize(NX);

    // all other values large values the computer can handle
    for (int i = 0; i < ID_FIRST_delta; ++i) {
        this->x_lowerbound[i] = -1.0e10;
        this->x_upperbound[i] = 1.0e10;
    }

    // all actuation inputs (steering, acceleration) should have values between [-1, 1]
    // 方向盘转向角约束 大概是40°左右
    for (int i = ID_FIRST_delta; i < ID_FIRST_a; ++i) {
        this->x_lowerbound[i] = -0.70;
        this->x_upperbound[i] = 0.70;
    }

    // 加速度约束 -3~5
    for (int i = ID_FIRST_a; i < NX; ++i) {
        this->x_lowerbound[i] = -3.0;
        this->x_upperbound[i] = 5.0;
    }

    //**************************************************************
    //* SET UPPER AND LOWER LIMITS OF CONSTRAINTS 设置约束
    //**************************************************************
    this->g_lowerbound.resize(NG);
    this->g_upperbound.resize(NG);

    // the first constraint for each state veriable
    // refer to the initial state conditions
    // this will be initialized when solve() is called
    // the succeeding constraints refer to the relationship
    // between succeeding states based on our kinematic model of the system

    for (int i = 0; i < NG; ++i) {
        this->g_lowerbound[i] = 0.0;
        this->g_upperbound[i] = 0.0;
    }
}

MPC::~MPC() {}

void MPC::solve(Eigen::VectorXd state, Eigen::VectorXd K) {
    // 机器人初始状态
    const double px = state[0];   // x
    const double py = state[1];   // y
    const double psi = state[2];  // 朝向角
    const double v = state[3];    // 速度
    const double cte = state[4];  // y轴偏差(横向偏差)
    const double epsi = state[5]; // 朝向角偏差(角度偏差)

    this->x[ID_FIRST_px] = px;
    this->x[ID_FIRST_py] = py;
    this->x[ID_FIRST_psi] = psi;
    this->x[ID_FIRST_v] = v;
    this->x[ID_FIRST_cte] = cte;
    this->x[ID_FIRST_epsi] = epsi;

    this->g_lowerbound[ID_FIRST_px] = px;
    this->g_lowerbound[ID_FIRST_py] = py;
    this->g_lowerbound[ID_FIRST_psi] = psi;
    this->g_lowerbound[ID_FIRST_v] = v;
    this->g_lowerbound[ID_FIRST_cte] = cte;
    this->g_lowerbound[ID_FIRST_epsi] = epsi;

    this->g_upperbound[ID_FIRST_px] = px;
    this->g_upperbound[ID_FIRST_py] = py;
    this->g_upperbound[ID_FIRST_psi] = psi;
    this->g_upperbound[ID_FIRST_v] = v;
    this->g_upperbound[ID_FIRST_cte] = cte;
    this->g_upperbound[ID_FIRST_epsi] = epsi;

    //**************************************************************
    //* SOLVE
    //**************************************************************

    // IPOPT 优化求解器
    std::string options;
    // Uncomment this if you'd like more print information 打印开关
    options += "Integer print_level  0\n";
    // NOTE: Setting sparse to true allows the solver to take advantage of sparse routines, this makes the computation MUCH FASTER.
    // 将稀疏设置为 "true "可让求解器利用稀疏例程,这将大大加快计算速度
    options += "Sparse  true        forward\n";
    options += "Sparse  true        reverse\n";
    // NOTE: Currently the solver has a maximum time limit of 0.5 seconds.
    // 容许求解器最大的计算时间(实际上应该设置为一个控制周期时间)
    options += "Numeric max_cpu_time          0.5\n";

    // 优化求解结果
    CppAD::ipopt::solve_result<Dvector> solution;
    // 包含代价函数和约束条件
    FG_eval fg_eval(K);

    // solve the problem
    CppAD::ipopt::solve<Dvector, FG_eval>(options, x, x_lowerbound, x_upperbound, g_lowerbound, g_upperbound, fg_eval, solution);

    // 判断优化器是否计算成功
    auto cost = solution.obj_value;
    bool ok = solution.status == CppAD::ipopt::solve_result<Dvector>::success;
    if (ok) {
        // std::cout << "OK! Cost:" << cost << std::endl;
    } else {
        std::cout << "SOMETHING IS WRONG!" << cost << std::endl;
    }

    //**************************************************************
    //* STORE RELEVANT INFORMATION FROM SOLUTION
    //**************************************************************

    this->steer = solution.x[ID_FIRST_delta];
    this->throttle = solution.x[ID_FIRST_a];

    this->future_xs = {};
    this->future_ys = {};
    for (int i = 0; i < N; ++i) {
        const double px = solution.x[ID_FIRST_px + i];
        const double py = solution.x[ID_FIRST_py + i];
        // cout << "x: " << px << ", y:" << py << endl;
        this->future_xs.emplace_back(px);
        this->future_ys.emplace_back(py);
    }
}

// MAIN.CPP文件

#include <math.h>
#include <iostream>
#include <thread>
#include <vector>
#include "../include/MPC.hpp"
#include "Eigen-3.3/Eigen/Core"
#include "Eigen-3.3/Eigen/QR"
using namespace std;

// 车辆模型机器人状态
typedef struct state {
    state(){};
    state(float _x, float _y, float _theta, float _v, float _delta, float _a) : x(_x), y(_y), theta(_theta), v(_v), delta(_delta), a(_a){};
    // ======= 状态量 ======
    float x = 0;
    float y = 0;
    float theta = 0;
    // ======= 控制量 ======
    float v = 0;     // 速度
    float delta = 0; // 转向角
    float a = 0;
} State;

// Fit a polynomial.  多项式拟合
Eigen::VectorXd polyfit(Eigen::VectorXd xvals, Eigen::VectorXd yvals, int order) {
    assert(xvals.size() == yvals.size());
    assert(order >= 1 && order <= xvals.size() - 1);
    Eigen::MatrixXd A(xvals.size(), order + 1);

    for (int i = 0; i < xvals.size(); ++i) {
        A(i, 0) = 1.0;
    }

    for (int j = 0; j < xvals.size(); ++j) {
        for (int i = 0; i < order; i++) {
            A(j, i + 1) = A(j, i) * xvals(j);
        }
    }

    auto Q = A.householderQr();
    auto result = Q.solve(yvals);
    return result;
}

// 获取矩阵K
Eigen::VectorXd getK(state state, std::vector<double> points_xs, std::vector<double> points_ys) {
    const double px = state.x;
    const double py = state.y;
    const double psi = state.theta;   // 车头朝向角度
    const double v = state.v;         // 速度
    const double delta = state.delta; // 转向角
    const double a = state.a;

    const int NUMBER_OF_WAYPOINTS = points_xs.size();
    Eigen::VectorXd waypoints_xs(NUMBER_OF_WAYPOINTS);
    Eigen::VectorXd waypoints_ys(NUMBER_OF_WAYPOINTS);

    // 变换成以车辆中心为坐标原点的路径
    for (int i = 0; i < NUMBER_OF_WAYPOINTS; ++i) {
        const double dx = points_xs[i] - px;
        const double dy = points_ys[i] - py;

        waypoints_xs[i] = dx * cos(-psi) - dy * sin(-psi);
        waypoints_ys[i] = dy * cos(-psi) + dx * sin(-psi);
        // cout << "x:" << waypoints_xs[i] << " y:" << waypoints_ys[i] << endl;
    }

    const int ORDER = 3;
    auto K = polyfit(waypoints_xs, waypoints_ys, ORDER);
    return K;
}

// 获取状态矩阵
Eigen::VectorXd getCarState(State state, Eigen::VectorXd K) {
    const double cte = K[0];         // y轴偏差
    const double epsi = -atan(K[1]); // 朝向偏差
    const double current_px = 0.0 + state.v * dt;
    const double current_py = 0.0;
    const double current_psi = 0.0 + state.v * tan(state.delta) / Lf * dt;
    const double current_v = state.v + state.a * dt;
    const double current_cte = cte + state.v * sin(epsi) * dt;
    const double current_epsi = epsi + state.v * tan(state.delta) / Lf * dt;

    Eigen::VectorXd state_eigen(NUMBER_OF_STATES);
    state_eigen << current_px, current_py, current_psi, current_v, current_cte, current_epsi;
    return state_eigen;
}

int main() {
    // 路径
    std::vector<double> points_xs = {1, 5, 5, 15};
    std::vector<double> points_ys = {1, 5, 10, 15};
    // mpc求解器
    MPC mpc;
    // 当前车辆状态()
    state state(1.f, 1.f, M_PI * 45 / 180, 0, -M_PI * 5 / 180, 0);
    auto K = getK(state, points_xs, points_ys);
    Eigen::VectorXd car_state = getCarState(state, K);
    mpc.solve(car_state, K);
    cout << "加速度:" << mpc.throttle << "m/s^2,  转向角:" << mpc.steer * 180 / M_PI << endl;
}

代码实现参考了源码: https://github.com/mithi/mpc

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值