规划控制复现:Apollo LQR横向控制(C++/simulink实现)

本文在前文已经搭建好的ROS-C++规划控制验证平台中进行LQR算法的复现,理论部分详见于:

规划控制复现:Apollo LQR横向控制(算法原理推导与流程)_apollo 规划控制-优快云博客

Prescan-Carsim-ROS的仿真平台搭建详见于:

简单硬件在环搭建(ROS+Prescan+Carsim+simulink)_prescan ros-优快云博客

1.算法流程

LQR的核心在于求解里卡提方程,计算最优的反馈增益K矩阵:

其中,K矩阵为:

在计算得到K矩阵后,计算Frenet坐标系下的航向误差和横向误差,得到误差矩阵:

最终计算得到的是前轮转角:

故横向控制算法流程如下:

下面就分模块对每一个部分进行编写。

2.simulink/matlab代码编写

取自《忠厚老实的老王》,这里搬运代码并对代码作部分详解

横向控制模块如上图所示,输入为自车的状态量:x,y,Vx,Vy,航向角,以及规划出来的期望点xr,yr,θr,kappar_r(道路曲率),横向控制模块子模块实现如下:

2.1 预测模块

此模块主要用于预测一个时间步长之后的车辆状态:

数学原理如下:

matlab代码如下:

function [pred_x_ctrl,pred_y_ctrl,pred_yaw_ctrl,pred_vx_ctrl,pred_vy_ctrl,pred_yaw_dot_ctrl] = fcn(x,y,phi,vx,vy,phi_dot,ts)
    pred_x_ctrl=x+vx*ts*cos(phi)-vy*ts*sin(phi);
    pred_y_ctrl=y+vy*ts*cos(phi)+vx*ts*sin(phi);
    pred_yaw_ctrl=phi;
    pred_vx_ctrl=vx;
    pred_vy_ctrl=vy;
    pred_yaw_dot_ctrl=phi_dot;
end

2.2 LQR增益矩阵K的计算

在本算法中,为节省计算开销,采用查表的方式对速度进行插值,采样5000个点,先通过不同的输入速度Vx进行离线计算,再差值得到LQR的增益矩阵K:

离线计算算法如下,调用matlab的LQR求解器对LQR问题进行求解:

k=zeros(5000,4);
vx_break_point=zeros(1,5000);
for i=1:5000
    vx_break_point(i)=0.01*i;
    
    A=[0,1,0,0;
        0,(cf+cr)/(m*vx_break_point(i)),-(cf+cr)/m,(la*cf-lb*cr)/(m*vx_break_point(i));
        0,0,0,1;
        0,(la*cf-lb*cr)/(Iz*vx_break_point(i)),-(la*cf-lb*cr)/Iz,(la*la*cf+lb*lb*cr)/(Iz*vx_break_point(i))];
    B=[0;
        -cf/m;
        0;
        -la*cf/Iz];
LQR_Q=1*[LQR_Q1,0,0,0;
        0,LQR_Q2,0,0;
        0,0,LQR_Q3,0;
        0,0,0,LQR_Q4];
   k(i,:)=lqr(A,B,LQR_Q,LQR_R);
end
LQR_K1=k(:,1)';
LQR_K2=k(:,2)';
LQR_K3=k(:,3)';
LQR_K4=k(:,4)';

将得到的K矩阵作为查表模块得到:

当速度值过小时,采用如下模块将K矩阵计算结果判定为0:

展开得到:

function k  = fcn(k1,k2,k3,k4,vx)
    if abs(vx)<0.1
        k=[0,0,0,0];
    else
       
        k=[k1,k2,k3,k4];
    end
end

2.3 误差计算模块

此模块根据前文得到的误差公式,计算Frenet坐标系下的横向误差与航向误差:

模块展开如下:

function [err,es,s_dot] = fcn(x,y,phi,vx,vy,phi_dot,xr,yr,thetar,kappar)
    tor=[cos(thetar);sin(thetar)];
    nor=[-sin(thetar);cos(thetar)];
    d_err=[x-xr;y-yr];
    ed=nor'*d_err;
    es=tor'*d_err;
    %projection_point_thetar=thetar(dmin);%apollo
    projection_point_thetar=thetar+kappar*es;
    ed_dot=vy*cos(phi-projection_point_thetar)+vx*sin(phi-projection_point_thetar);
    %%%%%%%%%
    ephi=sin(phi-projection_point_thetar);
    %%%%%%%%%
    ss_dot=vx*cos(phi-projection_point_thetar)-vy*sin(phi-projection_point_thetar);
    s_dot=ss_dot/(1-kappar*ed);
    ephi_dot=phi_dot-kappar*s_dot;
    err=[ed;ed_dot;ephi;ephi_dot];
end

2.4 前馈计算模块

此模块用于计算前馈

展开得到:

function forword_angle = fcn(vx,a,b,m,cf,cr,k,kr)
    forword_angle=kr*(a+b-b*k(3)-(m*vx*vx/(a+b))*((b/cf)+(a/cr)*k(3)-(a/cr)));
end

2.5 输出计算模块

此模块用于将前面计算得到的反馈矩阵与误差矩阵作乘法,在前馈误差的基础上得到最终的反馈:

function angle = fcn(k,err,forword_angle)

    angle=-k*err+forword_angle;
end

3.C++/ROS环境下的算法代码编写

3.1 ROS节点代码编写

头文件Init_Ros.h:

#ifndef INIT_ROS_H
#define INIT_ROS_H
#include <ros/ros.h>
#include <geometry_msgs/Pose2D.h>
#include <geometry_msgs/Twist.h>
#include <geometry_msgs/Accel.h>
#include <std_msgs/Float64.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <cmath>
#include <limits>

class InitRos
{
public:
    //定义ROS初始化参考路径全局变量
    std::vector<double> gloabel_X;      // 全局 X 坐标轨迹值
    std::vector<double> gloabel_Y;      // 全局 Y 坐标轨迹值
    std::vector<double> globel_Angle;   // 航向角值
    std::vector<std::vector<double>> matrix; // 油门刹车查找表
    double current_speed;

    // 车辆数据
    double host_vehicle_speed_;
    double host_yaw_rate_;
    double host_heading_;
    double host_x_;
    double host_y_;
    double host_Vx_;
    double host_Vy_;
    double host_ax_;
    double host_ay_;
    double curr_time_;
    double ts=0.1;

    //输出
    double steering_;
    double throttle_;
    double brake_;

    // 读取文件的函数
    std::vector<double> readData(const std::string &filePath);
    std::vector<std::vector<double>> readMatrix(const std::string &filePath, int rows, int cols);

    //定义ROS初始化函数
    InitRos(); // 构造函数
    void run(); // 运行 ROS 循环

    //定义ROS消息类型
    geometry_msgs::Pose2D msg;;//输出控制消息
private:

    // ROS 节点句柄和订阅器
    ros::NodeHandle nh_;
    ros::Subscriber pose_sub_;
    ros::Subscriber velocity_sub_;
    ros::Subscriber Vxy_sub_;
    ros::Subscriber Axy_sub_;
    ros::Subscriber time_sub_;
    //发布节点
    ros::Publisher  control_pub_;


    // 位置回调函数
    void chatterCallback(const geometry_msgs::Pose2D& Pose_msg);
    // 速度回调函数
    void velocityCallback(const geometry_msgs::Twist::ConstPtr& msg);
    //Vxy速度回调函数
    void Vector_velocityCallback(const geometry_msgs::Twist::ConstPtr& Vxy_msg);
    //axy加速度回调函数
    void AccCallback(const geometry_msgs::Accel::ConstPtr& Axy_msg);
    //绝对时间回调函数
    void timeCallback(const geometry_msgs::Pose2D& time_msg);
    
};
#endif // VEHICLE_DATA_RECEIVER_H

在构造函数中初始化ROS节点:

#include "Init_Ros.h"
#include "EM_plan.h"
#include "LQR.h"
InitRos::InitRos()
{
    // 初始化订阅器
    velocity_sub_ = nh_.subscribe("/vel_cmd", 10, &InitRos::velocityCallback, this);
    pose_sub_     = nh_.subscribe("/simulink_pose", 10, &InitRos::chatterCallback, this);
    Vxy_sub_      = nh_.subscribe("/vxy_info", 10, &InitRos::Vector_velocityCallback, this);
    Axy_sub_      = nh_.subscribe("/Axy_info", 10, &InitRos::AccCallback, this);
    time_sub_     = nh_.subscribe("/time_info", 10, &InitRos::timeCallback, this);
    // 初始化发布器
    control_pub_ = nh_.advertise<geometry_msgs::Pose2D>("/control_cmd", 100);

    // 读取轨迹值和查找表
    std::string filePathX = "/home/jetson/ROS/R2/yahboomcar_ws/src/ROS_Prescan/prescanpp/src/gloable_X.txt";
    gloabel_X = readData(filePathX);

    std::string filePathY = "/home/jetson/ROS/R2/yahboomcar_ws/src/ROS_Prescan/prescanpp/src/gloable_Y.txt";
    gloabel_Y = readData(filePathY);

    std::string filePathAngle = "/home/jetson/ROS/R2/yahboomcar_ws/src/ROS_Prescan/prescanpp/src/globel_Angle.txt";
    globel_Angle = readData(filePathAngle);

    std::string filePathMatrix = "/home/jetson/ROS/R2/yahboomcar_ws/src/ROS_Prescan/prescanpp/src/tablebr.txt";
    matrix = readMatrix(filePathMatrix, 1001, 261);
    
}

std::vector<double> InitRos::readData(const std::string &filePath)
{
    std::ifstream file(filePath);
    std::vector<double> data;

    if (!file.is_open()) {
        ROS_ERROR("无法打开文件: %s", filePath.c_str());
        return data;
    }

    std::string line;
    while (std::getline(file, line)) {
        std::istringstream iss(line);
        double value;
        if (iss >> value) {
            data.push_back(value);
        } else {
            ROS_WARN("读取数据失败: %s", line.c_str());
        }
    }
    file.close();
    return data;
}

std::vector<std::vector<double>> InitRos::readMatrix(const std::string &filePath, int rows, int cols)
{
    std::ifstream file(filePath);
    std::vector<std::vector<double>> matrix(rows, std::vector<double>(cols));

    if (!file.is_open()) {
        ROS_ERROR("无法打开文件: %s", filePath.c_str());
        return matrix;
    }

    std::string line;
    for (int i = 0; i < rows; ++i) {
        if (!std::getline(file, line)) {
            ROS_ERROR("读取文件时出错或文件行数不足");
            return matrix;
        }
        std::istringstream iss(line);
        for (int j = 0; j < cols; ++j) {
            if (!(iss >> matrix[i][j])) {
                ROS_ERROR("读取数据时出错或文件列数不足: 行 %d 列 %d", i, j);
                return matrix;
            }
        }
    }
    file.close();
    return matrix;
}
//ROSrun主函数
void InitRos::run()
{
    ros::Rate rate(1000);
    EM_plan em_plan;
    LQRController lqrController;
    while (ros::ok())
    {
        
        // 寻找索引值
        int index = em_plan.findClosestPoint(gloabel_X, gloabel_Y, host_x_, host_y_);
        //寻找期望点
        em_plan.xr_ = gloabel_X[index]; 
        em_plan.yr_ = gloabel_Y[index];
        //航向角计算
        std::vector<double> theta_vector = em_plan.calculateHeading(gloabel_X,gloabel_Y);
        //曲率计算
        std::vector<double> kappa_vector = em_plan.calculateHeading(gloabel_X,gloabel_Y);
        //ROS_INFO("索引: %f", gloabel_Y[index]);
        //ROS_INFO("索引: %f", gloabel_X[index]);
        //预测模块更新
        lqrController.predictState(host_x_,host_y_,host_heading_,
                  host_Vx_,host_Vy_,host_yaw_rate_);
        //主计算函数
        Eigen::MatrixXd _matrix_A_bar_ = lqrController._matrix_A_bar;
        Eigen::MatrixXd _matrix_B_bar_ = lqrController._matrix_B_bar;
        Eigen::MatrixXd _matrix_Q_     = lqrController._matrix_Q; // Q矩阵
        Eigen::MatrixXd _matrix_R_     = lqrController._matrix_R; // R矩阵
        int     _iter_max_             = lqrController._iter_max; // 最大迭代次数
        double _tolerance_             = lqrController._tolerance; // 迭代精度
        lqrController.Compute_LQR(_matrix_A_bar_,_matrix_B_bar_,_matrix_Q_,_matrix_R_,_iter_max_,_tolerance_);
        //误差计算函数
        double pred_x       = lqrController.pred_x_ctrl_;
        double pred_y       = lqrController.pred_y_ctrl_;
        double pred_yaw     = lqrController.pred_yaw_ctrl_;
        double pred_vx      = lqrController.pred_vx_ctrl_;
        double pred_vy      = lqrController.pred_vy_ctrl_;
        double pred_yaw_dot = lqrController.pred_yaw_dot_ctrl_;
        double xrr          = em_plan.xr_;
        double yrr          = em_plan.yr_;
        lqrController.err_Compute(pred_x,pred_y,pred_yaw,
        pred_vx,pred_vy,pred_yaw_dot,xrr, yrr,theta_vector[index],kappa_vector[index]);
        // 更新全局的 steer
        double steer = lqrController.steer_Compute(kappa_vector[index]);

        double dt    = 0.1;
        msg.x        = throttle_; // 油门开度
        msg.y        = brake_; // 刹车压力
        steering_    = -steer*180/3.14;
        msg.theta    = steering_; // 方向盘扭矩
        control_pub_.publish(msg);
        //ROS_INFO("索引: %f", steering_);
        ros::spinOnce();
        rate.sleep();
    }
}

void InitRos::chatterCallback(const geometry_msgs::Pose2D& Pose_msg)
{
    host_x_       = Pose_msg.x;
    host_y_       = Pose_msg.y;
    host_heading_ = Pose_msg.theta;

}

void InitRos::velocityCallback(const geometry_msgs::Twist::ConstPtr& msg)
{
    host_vehicle_speed_ = msg->linear.x;
    host_yaw_rate_ = msg->angular.z;
    
}

void InitRos::Vector_velocityCallback(const geometry_msgs::Twist::ConstPtr& Vxy_msg)
{
    host_Vx_ = Vxy_msg->linear.x; // 获取车辆前进速度
    host_Vy_ = Vxy_msg->linear.y; // 获取横向速度
    //ROS_INFO("Received Vxy: Vx_ = %f, Vy_ = %f", host_Vx_, host_Vy_);
}

void InitRos::AccCallback(const geometry_msgs::Accel::ConstPtr& Axy_msg)
{
    host_ax_ = Axy_msg->linear.x; // 获取车辆前进速度
    host_ay_ = Axy_msg->linear.y; // 获取横向速度
    //ROS_INFO("Received ACC: ax_ = %f, ay_ = %f", host_ax_,host_ay_);
}
//绝对时间回调函数
void InitRos::timeCallback(const geometry_msgs::Pose2D& time_msg)
{
    curr_time_=time_msg.x;
    //ROS_INFO("Received time: time= %f", curr_time_);
}

全局路径的TXT文件获取,详见上一章节:

简单硬件在环搭建(ROS+Prescan+Carsim+simulink)_prescan ros-优快云博客

3.2 LQR主算法模块编写

算法流程依然和之前一样,只不过将matlab模块转换为了CPP函数,头文件LQR.h:

#ifndef LQR_H
#define LQR_H
#include <Eigen/Dense>

class LQRController {
public:
    //构造函数
    LQRController();
    //预测函数
    void predictState(double& host_x, double& host_y, double& host_heading,
                  double& host_Vx, double& host_Vy, double& host_yaw_rate);
    //LQR主计算函数
    bool Compute_LQR(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, 
     const Eigen::MatrixXd& Q, const Eigen::MatrixXd& R,
     const int iter_max,const double tolerance);
    //误差计算函数
    Eigen::VectorXd err_Compute(double x_e,double y_e,double phi,double vx_e,double vy_e,double yaw_e,
                                double xr, double yr, double thetar,double kappar);
    //前馈与最终输出计算函数
    double steer_Compute(double kappa);
    //设置Q矩阵

    //设置R矩阵
   
    // 预测结果
    double pred_x_ctrl_;
    double pred_y_ctrl_;
    double pred_yaw_ctrl_;
    double pred_vx_ctrl_;
    double pred_vy_ctrl_;
    double pred_yaw_dot_ctrl_;
    int    _iter_max; // 最大迭代次数
    double _tolerance; // 迭代精度
    // lqr参数
    int _matrix_size;
    Eigen::MatrixXd _matrix_A; // 状态矩阵
    Eigen::MatrixXd _matrix_A_bar; // 离散化的状态矩阵
    Eigen::MatrixXd _matrix_B; // 输入矩阵
    Eigen::MatrixXd _matrix_B_bar; // 离散化的矩阵
    Eigen::MatrixXd _matrix_K; // 反馈矩阵
    Eigen::VectorXd _matrix_err; // 误差向量
    Eigen::MatrixXd _matrix_Q; // Q矩阵
    Eigen::MatrixXd _matrix_R; // R矩阵
    double ts;
private:
    

    // 车辆参数
    double _cf; // 前轮侧偏刚度系数
    double _cr; // 后轮侧偏刚度系数
    double _m;  // 质量
    double _vx; // 沿着车身轴线的速度
    double _Iz; // 车身转动惯量
    double _a; // 质心到车前轴的距离
    double _b; // 质心到车后轴的距离
    double _steer_ratio; // 方向盘转角到轮胎转角之间的比值系数

     
};

#endif // LQR_H

主函数模块编写:

#include "Init_Ros.h"
#include "LQR.h"
//构造函数初始化
LQRController::LQRController()
{
    //车辆静态参数初始化
    _cf = -155494.663;
    _cr = -155494.663;
    _m = 1845.0;
    _a = 2.852/2.0;
    _b = 2.852/2.0;
    _Iz = _a*_a*(_m/2.0) + _b*_b*(_m/2.0);
    _steer_ratio = 16.0;
    //lqr静态矩阵初始化
    _matrix_size = 4;
    _matrix_A = Eigen::MatrixXd::Zero(_matrix_size,_matrix_size);
    _matrix_A_bar = Eigen::MatrixXd::Zero(_matrix_size,_matrix_size);
    _matrix_B = Eigen::MatrixXd::Zero(_matrix_size,1);
    _matrix_B_bar = Eigen::MatrixXd::Zero(_matrix_size,1);
    _matrix_K = Eigen::MatrixXd::Zero(1,_matrix_size);
    _matrix_err = Eigen::VectorXd::Zero(_matrix_size);
    _matrix_Q = Eigen::MatrixXd::Identity(_matrix_size,_matrix_size);
    _matrix_R = Eigen::MatrixXd::Identity(1,1);
    //A矩阵初始化
    _matrix_A(0,1) = 1.0;
    _matrix_A(1,1) = (_cf + _cr)/(_m * _vx);
    _matrix_A(1,2) = -(_cf + _cr)/(_m);
    _matrix_A(1,3) = (_a*_cf - _b*_cr)/(_m * _vx);
    _matrix_A(2,3) = 1.0;
    _matrix_A(3,1) = (_a*_cf - _b*_cr)/(_Iz * _vx);
    _matrix_A(3,2) = -(_a*_cf - _b*_cr)/(_Iz);
    _matrix_A(3,3) = (_a*_a*_cf + _b*_b*_cr)/(_Iz * _vx);
    //B矩阵初始化
    _matrix_B(1,0) = -_cf/_m;
    _matrix_B(3,0) = -_a*_cf/_Iz;
    //默认权重
    _matrix_R(0,0) = 0.1;//输入权重
    _matrix_Q(0,0) = 2.0;//横向距离误差权重
    _matrix_Q(1,1) = 0.0;//横向距离误差导数权重
    _matrix_Q(2,2) = 1.0;//横向角度误差权重
    _matrix_Q(3,3) = 0.0;//横向角度误差导数权重
    //迭代次数与容差
    _iter_max = 1500;
    _tolerance = 0.01;
    ts = 0.01;
    //前向欧拉法离散化矩阵
    auto I = Eigen::MatrixXd::Identity(_matrix_size,_matrix_size);
    _matrix_A_bar = (I + _matrix_A*ts);
    _matrix_B_bar = _matrix_B * ts;
    //初始化预测模块
    pred_x_ctrl_ = 0;
    pred_y_ctrl_ = 0;
    pred_yaw_ctrl_ = 0;
    pred_vx_ctrl_ = 0;
    pred_vy_ctrl_ = 0;
    pred_yaw_dot_ctrl_ = 0;

}
//预测函数初始化
void LQRController::predictState(double& host_x, double& host_y, double& phi,
                  double& host_Vx, double& host_Vy, double& host_yaw_rate)
{
        pred_x_ctrl_ = host_x+ host_Vx* ts * cos(phi) - host_Vy * ts * sin(phi);
        pred_y_ctrl_ = host_y + host_Vy * ts * cos(phi) + host_Vx * ts * sin(phi);
        pred_yaw_ctrl_ = phi;
        pred_vx_ctrl_ = host_Vx;
        pred_vy_ctrl_ = host_Vy;
        pred_yaw_dot_ctrl_ = host_yaw_rate;
}
//LQR主计算函数
bool LQRController::Compute_LQR(
     const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, 
     const Eigen::MatrixXd& Q, const Eigen::MatrixXd& R,
     const int iter_max,const double tolerance)
{
    
    //判断输入矩阵的维数是否正确
    if(A.rows()!=A.cols() || B.rows()!=A.rows() || Q.rows()!=Q.cols() || Q.rows()!=A.rows() || R.cols()!=B.cols())
    {
        
        return false;
    }
    auto matrix_size = A.rows();
    Eigen::MatrixXd P;
    Eigen::MatrixXd P_next;
    Eigen::MatrixXd AT;
    Eigen::MatrixXd BT;
    //初始化
    P = Eigen::MatrixXd::Zero(matrix_size,matrix_size);//P初始化成Q矩阵也可以,因为P初始化为0矩阵的话,第一次迭代P_next就是Q矩阵
    P_next = Eigen::MatrixXd::Zero(matrix_size,matrix_size);
    AT = A.transpose();
    BT = B.transpose();
    //迭代计算黎卡提方程
    for (int i = 0; i < iter_max; i++)
    {   
        P_next = Q + AT*P*A3 - AT*P*B*(R+BT*P*B).inverse()*BT*P*A;
        if (fabs((P_next-P).maxCoeff()) < tolerance)
        {
            ROS_INFO("success");
            _matrix_K = (R + BT*P*B).inverse()*(BT*P*A);
            return true;
        }
        //ROS_INFO("false");
        //第一次写忘记写
        P = P_next;
    }
    return false;
}
//误差计算函数
Eigen::VectorXd LQRController::err_Compute(double x_e,double y_e,double phi,double vx_e,double vy_e,double yaw_e,
                                             double xr, double yr, double thetar,
                                             double kappar)
{
    // 创建向量
    Eigen::Vector2d tor(cos(thetar), sin(thetar));  // 方向向量
    Eigen::Vector2d nor(-sin(thetar), cos(thetar)); // 法向量
    Eigen::Vector2d d_err(x_e - xr, y_e - yr);          // 位置误差
    // 计算误差
    double ed = nor.transpose() * d_err;            // 误差沿法向量
    double es = tor.transpose() * d_err;            // 误差沿切向量
    // 投影点
    double projection_point_thetar = thetar + kappar * es;
    // 计算误差导数
    double ed_dot   = y_e * cos(phi - projection_point_thetar) + x_e * sin(phi - projection_point_thetar);
    // 计算ephi
    double ephi     = sin(phi - projection_point_thetar);
    // 计算s_dot
    double ss_dot   = vx_e * cos(phi - projection_point_thetar) - vy_e * sin(phi - projection_point_thetar);
    double s_dot    = ss_dot / (1 - kappar * ed);
    double ephi_dot = yaw_e - kappar * s_dot;
    // 将误差组合成向量
    Eigen::VectorXd err(4); 
    // 创建一个四维向量
    err << ed, ed_dot, ephi, ephi_dot;
    return err;
}
//控制输出量计算
double LQRController::steer_Compute(double kappa)
{
    //前馈计算 
    if (! Compute_LQR(_matrix_A_bar,_matrix_B_bar,_matrix_Q,_matrix_R,_iter_max,_tolerance))
    {
        _matrix_K = Eigen::MatrixXd::Zero(1,_matrix_size);
    }
    
    double k3 = _matrix_K(0,2);
    double k_m = kappa;
    double delta_f = k_m*(_a + _b - _b*k3 - (_m*_vx*_vx/(_a+_b)) * (_b/_cf + _a*k3/_cr - _a/_cr));
    //最终控制输出量计算
    double u = (_matrix_K*_matrix_err)[0] + delta_f;
    //限制前轮转角
    double max_u = 60.0 * M_PI / 180.0;
    u = std::min(std::max(u,-max_u),max_u);
    
    double steer = -u;//前轮转角计算出负值应该是右转,所以有一个负号。
    double steer_Angel =  std::min(std::max(steer,-10.0),10.0);
    return steer_Angel;
}






3.3 用于测试的上层规划模块

由于规划模块还未集成到本项目中,故先采用离散全局路径来作为规划点,EM_Plan.h如下:

#include "LQR.h"
#include "Init_Ros.h"
class EM_plan
{
public:
    EM_plan();
    //计算距离
    double calculateDistance(double X, double Y, double X1, double Y1);
    //查找最近的索引点
    int findClosestPoint(const std::vector<double>& X,
         const std::vector<double>& Y, double currentX, double currentY);
    //计算理想曲率
    std::vector<double> calculateCurvature(const std::vector<double>& X,const std::vector<double>& Y); 
    //计算理想航向角
    std::vector<double> calculateHeading(const std::vector<double>& X,const std::vector<double>& Y);
    double xr_;
    double yr_;
    double thetar_;
    double kappar_;

private:


};

函数实现如下:

#include "EM_plan.h"
//构造函数
EM_plan::EM_plan()
{
     xr_ = 0;
     yr_ = 0;
     thetar_ = 0;
     kappar_ = 0;
}
//计算距离
double EM_plan::calculateDistance(double X, double Y, double X1, double Y1) {
        return sqrt((X - X1) * (X - X1) + (Y - Y1) * (Y - Y1));
    }
//计算最近的索引点
int EM_plan::findClosestPoint(const std::vector<double>& X, const std::vector<double>& Y, double currentX, double currentY) 
{
        int closestIndex = -1;
        double minDistance = std::numeric_limits<double>::max();
        for (size_t i = 0; i < X.size(); ++i) {
            double distance = calculateDistance(X[i], Y[i], currentX, currentY);
            if (distance < minDistance) {
                minDistance = distance;
                closestIndex = i;
            }
        }
        return closestIndex+15;
}

//计算理想曲率
std::vector<double> EM_plan::calculateCurvature(const std::vector<double>& X,const std::vector<double>& Y)
{
    std::vector<double> headings;
    for (size_t i = 1; i < X.size(); ++i) {
        double dx = X[i] - X[i - 1];
        double dy = Y[i] - Y[i - 1];
        double heading = atan2(dy, dx);
        headings.push_back(heading);
    }
    return headings;
} 
//计算理想航向角
std::vector<double> EM_plan::calculateHeading(const std::vector<double>& X,const std::vector<double>& Y)
{
    std::vector<double> curvatures;
    for (size_t i = 1; i < X.size() - 1; ++i) {
        double dx1 = X[i] - X[i - 1];
        double dy1 = Y[i] - Y[i - 1];
        double dx2 = X[i + 1] - X[i];
        double dy2 = Y[i + 1] - Y[i];

        double curvature = (dy2 / dx2 - dy1 / dx1) / (1 + (dy1 / dx1) * (dy1 / dx1));
        curvatures.push_back(curvature);
    }
    return curvatures;
}

3.4 主函数

main.cpp如下:

#include "Init_Ros.h"
#include "LQR.h"
#include "EM_plan.h"
#include <ros/ros.h>
int main(int argc, char** argv)
{
    ros::init(argc, argv, "vehicle_data_receiver_node");
    InitRos RosPrescan;
    RosPrescan.run();

    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值