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

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

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

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

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

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:5000vx_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.1k=[0,0,0,0];elsek=[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);%apolloprojection_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]);// 更新全局的 steerdouble 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-CSDN博客

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);// 计算ephidouble ephi     = sin(phi - projection_point_thetar);// 计算s_dotdouble 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/459010.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASO优化秘籍!

根据 App Annie 发布的报告显示&#xff0c;现全球移动设备使用时长达到新高&#xff0c;日均 4.2 小时。在这大环境下&#xff0c;App 的竞争也愈演愈烈&#xff0c;App 想让更多人看到&#xff0c;似乎越来越难。那么&#xff0c;App 可以通过哪些方式进行推广&#xff1f; …

面向对象编程中类与类之间的关系(一)

目录 1.引言 2."有一个"关系 3."是一个"关系(继承) 4.“有一个”与“是一个”的区别 5.not-a关系 6.层次结构 7.多重继承 8.混入类 1.引言 作为程序员&#xff0c;必然会遇到这样的情况&#xff1a;不同的类具有共同的特征&#xff0c;至少看起来彼…

React写关键字高亮的三个方案

1.js正则replaceAlldangerouslySetInnerHTML{{ __html: xxx }}危险属性 步骤最简单,但是是危险属性,不推荐使用,项目中实在没有头绪,可以使用它应急 通过useMemo计算得到新的状态值,赋值给dangerouslySetInnerHTML属性的__html 关键代码: const [state1, setState1] useSt…

Linux 生产者消费者模型

1. 背景概念 假设现在有多个线程&#xff0c;一部分线程负责生产任务&#xff0c;称为生产者productor&#xff0c;另一部线程负责执行任务&#xff0c;称为消费者consumer&#xff0c;他们之间是一对一一对一一对一的关系。 现在生产者productor-3有任务要派发&#xff0c;但…

PlantUML在IDEA中使用

1.打开settings,搜索PlantUML Integration并下载 2.安装并重启IDEA 3.学习相关的语法即可进行使用

Java之多线程的实现(创建)(3种实现方式)(面试高频)

目录 一、多线程的3种实现方式 &#xff08;1&#xff09;继承Thread类。 &#xff08;2&#xff09;实现Runnable接口。&#xff08;void run()&#xff1a;该方法无返回值、无法抛出异常&#xff09; &#xff08;3&#xff09;实现Callable接口。&#xff08;V call() throw…

企业如何吸引稀缺的高技能员工

高技能员工的稀缺性和招聘难度日益凸显&#xff0c;其原因主要在于技术发展迅速、人才供需失衡、企业竞争加剧。其中&#xff0c;技术发展迅速导致人才培养跟不上市场需求&#xff0c;使得高技能人才更加稀缺。以人工智能领域为例&#xff0c;新技术层出不穷&#xff0c;相关人…

【MySQL】MySQL数据库中密码加密和查询的解决方案

本篇博客是为了记录自己在遇到password函数无法生效时的解决方案。通过使用AES_ENCRYPT(str,key)和AES_DECRYPT(str,key)进行加密和解密。 一、问题 自己想创建一个user表&#xff0c;user表中有一个password属性列&#xff0c;自己想对密码进行加密后再存入数据库&#xff0c…

java质数的判断 C语言指针变量的使用

1. public static void main(String[] args) {Scanner scnew Scanner(System.in);System.out.println("请输入一个值");int num sc.nextInt();boolean flagtrue;for (int i2;i<num;i){if (num%i0){flagfalse;break;}}if (flag){System.out.println(num"是一…

Midjourney 3D:探索未来沉浸式体验的无限可能

一、Midjourney 3D:开启沉浸式新时代 最近,Midjourney宣布即将推出一款全新的3D产品,这不仅仅是一次简单的3D生成技术的升级,而是一场革命。这款新产品将基于先进的光场技术,而非传统的3D网格模型,为用户提供前所未有的沉浸式体验。用户不仅可以“跳入”生成的场景中自由…

CasPL: Cascade Prompt Learning for Vision-Language Model Adaptation

文章汇总 当前的问题 目前可学习的提示符号主要用于适应任务的单一阶段(即适应提示)&#xff0c;容易导致过度拟合风险。 动机 提示符将分两个阶段逐步优化。在初始阶段&#xff0c;学习增强提示&#xff0c;**通过使用大量未标记的领域图像数据对齐其预测逻辑&#xff0c;从…

【文献及模型、制图分享】基于投入品减量增效视角的长江经济带农业生产绿色化演进研究

文献介绍 绿色化转型是农业可持续发展研究的重要议题。以农业生产绿色化转型过程的理论分析为基础&#xff0c;运用文献调查、访谈与问卷调查、脱钩分析相结合的方法&#xff0c;研究了长江经济带农业生产绿色化转型过程和投入品减量增效的趋势。 结果表明&#xff1a; 2015…

记录一个容器间访问不通问题

docker-compose装了zookeeper和一个服务。 zk服务如下&#xff1a; szxc-zk:image: "image.sd001.cn:30003/base/zookeeper:3.8"privileged: trueenvironment:- "TZAsia/Shanghai"#- "ALLOW_ANONYMOUS_LOGINyes"- "ZOO_MY_ID1"- &qu…

redis详细教程(3.ZSet,Bitmap,HyperLogLog)

ZSet Redis 的 ZSet&#xff08;有序集合&#xff09;是一种特殊的数据类型&#xff0c;它允许存储一系列不重复的字符串元素&#xff0c;并为每个元素关联一个分数&#xff08;score&#xff09;。这个分数用于对集合中的元素进行排序。ZSet 的特点是&#xff1a; 唯一性&am…

【Windows】电脑端口明明没有进程占用但显示端口被占用(动态端口)

TOC 一、问题 重启电脑后&#xff0c;启用某个服务显示1089端口被占用。 查看是哪个进程占用了&#xff1a; netstat -aon | findstr "1089"没有输出&#xff0c;但是换其他端口&#xff0c;是可以看到相关进程的&#xff1a; 现在最简单的方式是给我的服务指定另…

RHCE的学习(8)

动态网站 lnmp&#xff08;LAMP&#xff09; 解析index.php界面 &#xff08;1&#xff09;预配&#xff0c;确保服务能够被访问 systemctl stop firewalld setenforce 0 &#xff08;2&#xff09;安装nginx服务 mount /dev/sr0 /mnt cat /etc/yum.repos.d/base.repo dnf …

【待学习 】 DHTMLX Gantt

DHTMLX Gantt是一个开源 JavaScript 甘特图库&#xff0c;可以帮助您以美观的图表形式说明和管理项目计划。 它可以将任务之间的依赖关系显示为线条&#xff0c;并允许您设置任务之间的不同关系&#xff08;完成-开始、开始-开始、完成-完成、开始-完成&#xff09;。标准版还…

一二三应用开发平台自定义查询设计与实现系列2——查询方案功能实现

查询方案功能实现 上面实现了自定义查询功能框架&#xff0c;从用户角度出发&#xff0c;有些条件组合可以形成特定的查询方案&#xff0c;对应着业务查询场景。诸多查询条件的组合&#xff0c;不能每次都让用户来设置&#xff0c;而是应该保存下来&#xff0c;下次可以直接使…

一文解决单调栈的应用

单调栈的定义&#xff1a; 单调栈是栈的一中特殊形式&#xff0c;在栈中的元素必须满足单调性&#xff08;一定是单调上升或单调下降等等的规律&#xff09;。 单调栈的性质&#xff1a; 单调栈解决的问题 单调栈解决的常见问题&#xff1a;给定一个序列&#xff0c;求每个位置…

css绘制s型(grid)

在之前有通过flex布局实现了s型布局&#xff0c;是通过截取数组形式循环加载数据 这次使用grid直接加载数据通过css实现 <div id"app"><template v-for"(item,inx) in items"><div class"row"><template v-for"(ite…