自动驾驶——车辆动力学模型

在这里插入图片描述

/*lat_controller.cpp*/
namespace apollo {
namespace control {using apollo::common::ErrorCode;//故障码
using apollo::common::Status;//状态码
using apollo::common::TrajectoryPoint;//轨迹点
using apollo::common::VehicleStateProvider;//车辆状态信息
using Matrix = Eigen::MatrixXd;//矩阵库
using apollo::cyber::Clock;//Apollo时钟namespace {
//日志文件名称,名称与时间相关
std::string GetLogFileName() {time_t raw_time;char name_buffer[80];std::time(&raw_time);std::tm time_tm;localtime_r(&raw_time, &time_tm);strftime(name_buffer, 80, "/tmp/steer_log_simple_optimal_%F_%H%M%S.csv",&time_tm);return std::string(name_buffer);
}
//在指定的日志文件内写入各列数据标题
void WriteHeaders(std::ofstream &file_stream) {file_stream << "current_lateral_error,"<< "current_ref_heading,"<< "current_heading,"<< "current_heading_error,"<< "heading_error_rate,"<< "lateral_error_rate,"<< "current_curvature,"<< "steer_angle,"<< "steer_angle_feedforward,"<< "steer_angle_lateral_contribution,"<< "steer_angle_lateral_rate_contribution,"<< "steer_angle_heading_contribution,"<< "steer_angle_heading_rate_contribution,"<< "steer_angle_feedback,"<< "steering_position,"<< "v" << std::endl;
}
}  // namespace
//构造函数
LatController::LatController() : name_("LQR-based Lateral Controller") {if (FLAGS_enable_csv_debug) {steer_log_file_.open(GetLogFileName());steer_log_file_ << std::fixed;steer_log_file_ << std::setprecision(6);//保留六位小数WriteHeaders(steer_log_file_);//写入数据标题}AINFO << "Using " << name_;
}
//析构函数 关闭日志文件
LatController::~LatController() { CloseLogFile(); }
//加载控制配置 "control_conf.pb.txt"
//车辆参数 "vehicle_param.pb.txt"
bool LatController::LoadControlConf(const ControlConf *control_conf) {if (!control_conf) {AERROR << "[LatController] control_conf == nullptr";return false;}vehicle_param_ =common::VehicleConfigHelper::Instance()->GetConfig().vehicle_param();
//LatController类内成员控制周期ts_加载纵向控制配置中的控制周期control_conf.pb.txt--lat_controller_conf--tsts_ = control_conf->lat_controller_conf().ts();if (ts_ <= 0.0) {AERROR << "[MPCController] Invalid control update interval.";return false;}//control_conf.pb.txt--lat_controller_conf--cf加载的值赋值到cf_ 前轮侧偏刚度(左右轮之和)cf_ = control_conf->lat_controller_conf().cf();//后轮侧偏刚度之和cr_ = control_conf->lat_controller_conf().cr();//预览窗口的大小preview_window_ = control_conf->lat_controller_conf().preview_window();//低速前进预瞄距离lookahead_station_low_speed_ =control_conf->lat_controller_conf().lookahead_station();//低速倒车预瞄距离lookback_station_low_speed_ =control_conf->lat_controller_conf().lookback_station();//高速前进预瞄距离lookahead_station_high_speed_ =control_conf->lat_controller_conf().lookahead_station_high_speed();//高速倒车预瞄距离lookback_station_high_speed_ =control_conf->lat_controller_conf().lookback_station_high_speed();//轴距wheelbase_ = vehicle_param_.wheel_base();//转向传动比 = 方向盘转角/前轮转角steer_ratio_ = vehicle_param_.steer_ratio();//单边方向盘最大转角转化成degsteer_single_direction_max_degree_ =vehicle_param_.max_steer_angle() / M_PI * 180;//最大允许的横向加速度max_lat_acc_ = control_conf->lat_controller_conf().max_lateral_acceleration();//低速切换到高速的边界low_speed_bound_ = control_conf_->lon_controller_conf().switch_speed();//低速窗口//主要应用在低高速切换之间的线性插值,凡是涉及低高速控制切换的,就在这个窗口做线性插值过渡低高速的控制参数low_speed_window_ =control_conf_->lon_controller_conf().switch_speed_window();//左前悬的质量const double mass_fl = control_conf->lat_controller_conf().mass_fl();//右前悬的质量const double mass_fr = control_conf->lat_controller_conf().mass_fr();//左后悬的质量const double mass_rl = control_conf->lat_controller_conf().mass_rl();//右后悬的质量const double mass_rr = control_conf->lat_controller_conf().mass_rr();//前悬质量const double mass_front = mass_fl + mass_fr;//后悬质量const double mass_rear = mass_rl + mass_rr;//车辆总质量mass_ = mass_front + mass_rear;//前悬长度lf_ = wheelbase_ * (1.0 - mass_front / mass_);//后悬长度lr_ = wheelbase_ * (1.0 - mass_rear / mass_);//转动惯量iz_ = lf_ * lf_ * mass_front + lr_ * lr_ * mass_rear;//LQR求解精度lqr_eps_ = control_conf->lat_controller_conf().eps();//迭代最大次数lqr_max_iteration_ = control_conf->lat_controller_conf().max_iteration();//若打开query_time_nearest_point_only模式,则会用到此参数,但是默认关闭//若打开此种模式,则目标点选为当前时间+query_relative_time_ 这个时间在参考轨迹上对应的点//query_relative_time默认设置为0.8s,若打开此种模式就默认始终用将来0.8s的轨迹点作为目标点驱动当前车产生控制量向前走query_relative_time_ = control_conf->query_relative_time();//最小速度保护,避免v=0作为分母情况出现minimum_speed_protection_ = control_conf->minimum_speed_protection();return true;
}
//处理日志数据函数,将储存在debug中的各种误差信息写入日志文件里
void LatController::ProcessLogs(const SimpleLateralDebug *debug,const canbus::Chassis *chassis) {const std::string log_str = absl::StrCat(debug->lateral_error(), ",", debug->ref_heading(), ",", debug->heading(),",", debug->heading_error(), ",", debug->heading_error_rate(), ",",debug->lateral_error_rate(), ",", debug->curvature(), ",",debug->steer_angle(), ",", debug->steer_angle_feedforward(), ",",debug->steer_angle_lateral_contribution(), ",",debug->steer_angle_lateral_rate_contribution(), ",",debug->steer_angle_heading_contribution(), ",",debug->steer_angle_heading_rate_contribution(), ",",debug->steer_angle_feedback(), ",", chassis->steering_percentage(), ",",injector_->vehicle_state()->linear_velocity());//默认enable_csv_debug日志debug开关关闭if (FLAGS_enable_csv_debug) {steer_log_file_ << log_str << std::endl;}ADEBUG << "Steer_Control_Detail: " << log_str;
}
//打印初始化参数
//显示控制器的名称 
//显示参数:车辆总质量、转动惯量、前悬长度、后悬长度
void LatController::LogInitParameters() {AINFO << name_ << " begin.";AINFO << "[LatController parameters]"<< " mass_: " << mass_ << ","<< " iz_: " << iz_ << ","<< " lf_: " << lf_ << ","<< " lr_: " << lr_;
}
//初始化滤波器
void LatController::InitializeFilters(const ControlConf *control_conf) {//低通滤波器std::vector<double> den(3, 0.0);//初始化滤波器传递函数分母[3,0]std::vector<double> num(3, 0.0);//初始化滤波器传递函数分子为[3,0]//将控制周期和cutoff_freq()作为参数输入,计算得到滤波器传递函数分子分母,注意此处引用变量作为实参,就是为了被函数修改之后传回来。common::LpfCoefficients(ts_, control_conf->lat_controller_conf().cutoff_freq(), &den, &num);//上面计算出的分子,分母用来设置数字滤波器类对象digital_filter_,用于对方向盘转角控制指令进行滤波digital_filter_.set_coefficients(den, num);//对反馈的横向误差进行均值滤波,简而言之就是移动窗口内的多个值取平均达到滤波的效果//从控制配置读取均值滤波窗口大小(默认设置为10)设置均值滤波器类对象lateral_error_filter_lateral_error_filter_ = common::MeanFilter(static_cast<std::uint_fast8_t>(control_conf->lat_controller_conf().mean_filter_window_size()));heading_error_filter_ = common::MeanFilter(static_cast<std::uint_fast8_t>(control_conf->lat_controller_conf().mean_filter_window_size()));
}
//横向控制器LQR的初始化工作
//injector车辆当前状态信息
Status LatController::Init(std::shared_ptr<DependencyInjector> injector,const ControlConf *control_conf) {control_conf_ = control_conf;injector_ = injector;//如果加载失败。。。。if (!LoadControlConf(control_conf_)) {AERROR << "failed to load control conf";return Status(ErrorCode::CONTROL_COMPUTE_ERROR,"failed to load control_conf");}//矩阵初始化操作//车辆状态方程中矩阵的大小 = 基础状态矩阵大小 + 预览窗口大小const int matrix_size = basic_state_size_ + preview_window_;//4+0matrix_a_ = Matrix::Zero(basic_state_size_, basic_state_size_);//4*4零矩阵//计算机控制需要转换成离散的差分方程形式//matrix_ad_是系数矩阵A的离散形式,A通过双线性变化法变成Admatrix_ad_ = Matrix::Zero(basic_state_size_, basic_state_size_);matrix_adc_ = Matrix::Zero(matrix_size, matrix_size);/*A matrix (Gear Drive) 前进挡的状态方程系数矩阵A[0.0, 1.0, 0.0, 0.0;0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,(l_r * c_r - l_f * c_f) / m / v;0.0, 0.0, 0.0, 1.0;0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,(-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]*///此处apolloA矩阵有误matrix_a_(0, 1) = 1.0;matrix_a_(1, 2) = (cf_ + cr_) / mass_;matrix_a_(2, 3) = 1.0;matrix_a_(3, 2) = (lf_ * cf_ - lr_ * cr_) / iz_;//matrix_a_coeff_矩阵也初始化为4*4的零矩阵matrix_a_coeff_ = Matrix::Zero(matrix_size, matrix_size);matrix_a_coeff_(1, 1) = -(cf_ + cr_) / mass_;matrix_a_coeff_(1, 3) = (lr_ * cr_ - lf_ * cf_) / mass_;matrix_a_coeff_(3, 1) = (lr_ * cr_ - lf_ * cf_) / iz_;matrix_a_coeff_(3, 3) = -1.0 * (lf_ * lf_ * cf_ + lr_ * lr_ * cr_) / iz_;/*b = [0.0, c_f / m, 0.0, l_f * c_f / i_z]^T*///初始化矩阵B为4*1的零矩阵matrix_b_ = Matrix::Zero(basic_state_size_, 1);matrix_bd_ = Matrix::Zero(basic_state_size_, 1);matrix_bdc_ = Matrix::Zero(matrix_size, 1);//此处感觉也有误matrix_b_(1, 0) = cf_ / mass_;matrix_b_(3, 0) = lf_ * cf_ / iz_;//矩阵B的离散形式Bd就等于 B * tsmatrix_bd_ = matrix_b_ * ts_;//车辆状态矩阵X,[e1 e1' e2 e2']matrix_state_ = Matrix::Zero(matrix_size, 1);//初始化K矩阵为1*4的0矩阵matrix_k_ = Matrix::Zero(1, matrix_size);//初始化R矩阵为1*1的单位阵matrix_r_ = Matrix::Identity(1, 1);//初始化Q矩阵为4*4的0矩阵,Q矩阵是LQR中目标函数中各个状态量(X=[e1 e1' e2 e2'])平方和的权重系数matrix_q_ = Matrix::Zero(matrix_size, matrix_size);//q_param_size默认为4int q_param_size = control_conf_->lat_controller_conf().matrix_q_size();//倒车的reverse_q_param_sizeint reverse_q_param_size =control_conf_->lat_controller_conf().reverse_matrix_q_size();if (matrix_size != q_param_size || matrix_size != reverse_q_param_size) {const auto error_msg = absl::StrCat("lateral controller error: matrix_q size: ", q_param_size,"lateral controller error: reverse_matrix_q size: ",reverse_q_param_size," in parameter file not equal to matrix_size: ", matrix_size);AERROR << error_msg;return Status(ErrorCode::CONTROL_COMPUTE_ERROR, error_msg);}//加载控制配置中matrix_q(0),matrix_q(1),matrix_q(2),matrix_q(3)//默认分别为0.05,0.0,1.0,0.0//加载到LatController类数据成员matrix_q_即LQR的Q矩阵中for (int i = 0; i < q_param_size; ++i) {matrix_q_(i, i) = control_conf_->lat_controller_conf().matrix_q(i);}//更新后的Q矩阵matrix_q_updated_ = matrix_q_;//初始化3个滤波器,1个低通滤波是对计算出方向盘转角控制指令进行滤波InitializeFilters(control_conf_);//横向控制配置auto &lat_controller_conf = control_conf_->lat_controller_conf();//加载增益调度表,就是横向误差和航向误差在车速不同时乘上个不同的比例//这个比例决定了实际时的控制效果,根据实际经验低速和高速应该采取不同的比例,低速比例较大LoadLatGainScheduler(lat_controller_conf);//打印一些车辆参数的信息LogInitParameters();enable_leadlag_ = control_conf_->lat_controller_conf().enable_reverse_leadlag_compensation();//是否打开超前滞后器if (enable_leadlag_) {leadlag_controller_.Init(lat_controller_conf.reverse_leadlag_conf(), ts_);}enable_mrac_ =control_conf_->lat_controller_conf().enable_steer_mrac_control();if (enable_mrac_) {mrac_controller_.Init(lat_controller_conf.steer_mrac_conf(),vehicle_param_.steering_latency_param(), ts_);}
//是否能使前进倒车的预瞄控制enable_look_ahead_back_control_enable_look_ahead_back_control_ =control_conf_->lat_controller_conf().enable_look_ahead_back_control();//返回状态码return Status::OK();
}
//关闭日志文件
void LatController::CloseLogFile() {if (FLAGS_enable_csv_debug && steer_log_file_.is_open()) {steer_log_file_.close();}
}
//加载增益调度表
void LatController::LoadLatGainScheduler(const LatControllerConf &lat_controller_conf) {//横向误差增益表const auto &lat_err_gain_scheduler =lat_controller_conf.lat_err_gain_scheduler();//朝向误差增益表const auto &heading_err_gain_scheduler =lat_controller_conf.heading_err_gain_scheduler();AINFO << "Lateral control gain scheduler loaded";//定义了两张插值表xy1,xy2Interpolation1D::DataType xy1, xy2;//遍历调度表 将每一组横向误差调度表的speed,ratio结对make_pair存入插值表xy1for (const auto &scheduler : lat_err_gain_scheduler.scheduler()) {xy1.push_back(std::make_pair(scheduler.speed(), scheduler.ratio()));}//将每组朝向误差调度表的speed,ratio结对make_pair存入插值表for (const auto &scheduler : heading_err_gain_scheduler.scheduler()) {xy2.push_back(std::make_pair(scheduler.speed(), scheduler.ratio()));}//将lat_err_interpolation_复位,然后用xy1初始化lat_err_interpolation_lat_err_interpolation_.reset(new Interpolation1D);ACHECK(lat_err_interpolation_->Init(xy1))<< "Fail to load lateral error gain scheduler";//将heading_err_interpolation_复位,然后用xy1初始化heading_err_interpolation_heading_err_interpolation_.reset(new Interpolation1D);ACHECK(heading_err_interpolation_->Init(xy2))<< "Fail to load heading error gain scheduler";
}
//关闭横向日志文件
void LatController::Stop() { CloseLogFile(); }
//返回横向控制器的名称字符串
std::string LatController::Name() const { return name_; }
//计算控制指令
Status LatController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,ControlCommand *cmd) {//通过injector_获取当前车辆状态信息存储在vehicle_stateauto vehicle_state = injector_->vehicle_state();//车辆期望轨迹存放到target_tracking_trajectoryauto target_tracking_trajectory = *planning_published_trajectory;//是否打开导航模式,默认关闭if (FLAGS_use_navigation_mode &&FLAGS_enable_navigation_mode_position_update) {auto time_stamp_diff =planning_published_trajectory->header().timestamp_sec() -current_trajectory_timestamp_;auto curr_vehicle_x = localization->pose().position().x();auto curr_vehicle_y = localization->pose().position().y();double curr_vehicle_heading = 0.0;const auto &orientation = localization->pose().orientation();if (localization->pose().has_heading()) {curr_vehicle_heading = localization->pose().heading();} else {curr_vehicle_heading =common::math::QuaternionToHeading(orientation.qw(), orientation.qx(),orientation.qy(), orientation.qz());}// new planning trajectoryif (time_stamp_diff > 1.0e-6) {init_vehicle_x_ = curr_vehicle_x;init_vehicle_y_ = curr_vehicle_y;init_vehicle_heading_ = curr_vehicle_heading;current_trajectory_timestamp_ =planning_published_trajectory->header().timestamp_sec();} else {auto x_diff_map = curr_vehicle_x - init_vehicle_x_;auto y_diff_map = curr_vehicle_y - init_vehicle_y_;auto theta_diff = curr_vehicle_heading - init_vehicle_heading_;auto cos_map_veh = std::cos(init_vehicle_heading_);auto sin_map_veh = std::sin(init_vehicle_heading_);auto x_diff_veh = cos_map_veh * x_diff_map + sin_map_veh * y_diff_map;auto y_diff_veh = -sin_map_veh * x_diff_map + cos_map_veh * y_diff_map;auto cos_theta_diff = std::cos(-theta_diff);auto sin_theta_diff = std::sin(-theta_diff);auto tx = -(cos_theta_diff * x_diff_veh - sin_theta_diff * y_diff_veh);auto ty = -(sin_theta_diff * x_diff_veh + cos_theta_diff * y_diff_veh);auto ptr_trajectory_points =target_tracking_trajectory.mutable_trajectory_point();std::for_each(ptr_trajectory_points->begin(), ptr_trajectory_points->end(),[&cos_theta_diff, &sin_theta_diff, &tx, &ty,&theta_diff](common::TrajectoryPoint &p) {auto x = p.path_point().x();auto y = p.path_point().y();auto theta = p.path_point().theta();auto x_new = cos_theta_diff * x - sin_theta_diff * y + tx;auto y_new = sin_theta_diff * x + cos_theta_diff * y + ty;auto theta_new = common::math::NormalizeAngle(theta - theta_diff);p.mutable_path_point()->set_x(x_new);p.mutable_path_point()->set_y(y_new);p.mutable_path_point()->set_theta(theta_new);});}}//target_tracking_trajectory是从输入参数传进来的规划轨迹信息//将target_tracking_trajectory对象内容move移动到LatController类数据成员trajectory_analyzer_里trajectory_analyzer_ =std::move(TrajectoryAnalyzer(&target_tracking_trajectory));//将规划轨迹从后轴中心变换到质心,如果条件满足的话,倒档是否需要转换到质心坐标的开关//这个if不满足,可以略过if (((FLAGS_trajectory_transform_to_com_reverse &&vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) ||//前进档是否需要转换到质心坐标的开关,默认false(FLAGS_trajectory_transform_to_com_drive &&vehicle_state->gear() == canbus::Chassis::GEAR_DRIVE)) &&enable_look_ahead_back_control_) {trajectory_analyzer_.TrajectoryTransformToCOM(lr_);}//倒挡重建车辆的动力学模型,尤其是横向的动力学模型if (vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) {/*倒档时的系数矩阵AA matrix (Gear Reverse)[0.0, 0.0, 1.0 * v 0.0;0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,(l_r * c_r - l_f * c_f) / m / v;0.0, 0.0, 0.0, 1.0;0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,(-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]*///下面4项都是D档,R档A中会变化的项,D档和R档这4项不同cf_ = -control_conf_->lat_controller_conf().cf();cr_ = -control_conf_->lat_controller_conf().cr();matrix_a_(0, 1) = 0.0;matrix_a_coeff_(0, 2) = 1.0;} else {/*前进档的系数矩阵AA matrix (Gear Drive)[0.0, 1.0, 0.0, 0.0;0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,(l_r * c_r - l_f * c_f) / m / v;0.0, 0.0, 0.0, 1.0;0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,(-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]*///前进挡的一些参数:前轮侧偏刚度等cf_ = control_conf_->lat_controller_conf().cf();cr_ = control_conf_->lat_controller_conf().cr();matrix_a_(0, 1) = 1.0;matrix_a_coeff_(0, 2) = 0.0;}matrix_a_(1, 2) = (cf_ + cr_) / mass_;matrix_a_(3, 2) = (lf_ * cf_ - lr_ * cr_) / iz_;matrix_a_coeff_(1, 1) = -(cf_ + cr_) / mass_;matrix_a_coeff_(1, 3) = (lr_ * cr_ - lf_ * cf_) / mass_;matrix_a_coeff_(3, 1) = (lr_ * cr_ - lf_ * cf_) / iz_;matrix_a_coeff_(3, 3) = -1.0 * (lf_ * lf_ * cf_ + lr_ * lr_ * cr_) / iz_;/*b = [0.0, c_f / m, 0.0, l_f * c_f / i_z]^T*/matrix_b_(1, 0) = cf_ / mass_;matrix_b_(3, 0) = lf_ * cf_ / iz_;matrix_bd_ = matrix_b_ * ts_;//调用更新驾驶航向函数UpdateDrivingOrientation();//简单横向调试SimpleLateralDebug *debug = cmd->mutable_debug()->mutable_simple_lat_debug();debug->Clear();//更新车辆状态矩阵X=[e1 e1' e2 e2']//首先该函数UpdateState()内部调用了ComputeLateralErrors()函数得到的各种误差信息存放到debug中//然后又用debug去更新车辆状态矩阵X即matrix_state_UpdateState(debug);//主要是更新车辆状态方程系数矩阵A及其离散形式中与速度相关的时变项UpdateMatrix();//更新以及组装离散矩阵Ad,Bd和预览窗口模型,预览窗在横向控制中都关闭了,控制配置里preview_window为0UpdateMatrixCompound();//R档调整矩阵Qint q_param_size = control_conf_->lat_controller_conf().matrix_q_size();int reverse_q_param_size =control_conf_->lat_controller_conf().reverse_matrix_q_size();if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {//R档加载控制配置里的reverse_matrix_qfor (int i = 0; i < reverse_q_param_size; ++i) {matrix_q_(i, i) =control_conf_->lat_controller_conf().reverse_matrix_q(i);}} else {//非R档加载控制配置里的matrix_qfor (int i = 0; i < q_param_size; ++i) {matrix_q_(i, i) = control_conf_->lat_controller_conf().matrix_q(i);}}//对于更高速度的转向增加增益调度表//取出control_gflags.cc中enable_gain_scheduler的值,默认是false,但是实际上很有用//如果打开增益调度表if (FLAGS_enable_gain_scheduler) {matrix_q_updated_(0, 0) =matrix_q_(0, 0) * lat_err_interpolation_->Interpolate(std::fabs(vehicle_state->linear_velocity()));matrix_q_updated_(2, 2) =matrix_q_(2, 2) * heading_err_interpolation_->Interpolate(std::fabs(vehicle_state->linear_velocity()));
//求解LQR问题,求解到的最优状态反馈矩阵K放入matrix_k_中,最后一个引用变量作参数,明摆着就是要用对形参的修改改变实参,
//常用来存放函数计算结果 
//这个if和else都是调用SolveLQRProblem函数,其中不同就只有一个是matrix_q_updated_是考虑调度增益表的Q,Q与车速有关//一个是matrix_q_不考虑增益调度表的Q矩阵,Q与车速无关,看你是否打开调度增益表//根据经验打开的话更容易获得更好的控制性能,否则低速适用的Q用到高速往往容易出现画龙
common::math::SolveLQRProblem(matrix_adc_, matrix_bdc_, matrix_q_updated_,matrix_r_, lqr_eps_, lqr_max_iteration_,&matrix_k_);} else {common::math::SolveLQRProblem(matrix_adc_, matrix_bdc_, matrix_q_,matrix_r_, lqr_eps_, lqr_max_iteration_,&matrix_k_);}// feedback = - K * state// Convert vehicle steer angle from rad to degree and then to steer degree//状态反馈控制量 u = -K*X//将计算出的控制量(车辆的前轮转角)从rad转化为deg,然后再乘上转向传动比steer_ratio_转化成方向盘转角//最后再根据单边的方向盘最大转角转化控制为百分数 -100-100//这里计算出的是反馈的控制量const double steer_angle_feedback = -(matrix_k_ * matrix_state_)(0, 0) * 180 /M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;//定义临时常量steer_angle_feedforward存放前馈控制量//调用函数ComputeFeedForward计算前馈控制量const double steer_angle_feedforward = ComputeFeedForward(debug->curvature());double steer_angle = 0.0;double steer_angle_feedback_augment = 0.0;//在期望的速度域增强横向误差的反馈控制//如果打开leadlag超前滞后控制器if (enable_leadlag_) {//如果车辆打开高速的反馈增强控制 或 车速小于低高速边界速度if (FLAGS_enable_feedback_augment_on_high_speed ||std::fabs(vehicle_state->linear_velocity()) < low_speed_bound_) {//满足上述条件,实际上就是低速时打开超前滞后控制器,然后这个超前滞后控制器只对横向误差进行增强控制//计算出反馈增强控制方向盘转角百分数steer_angle_feedback_augmentsteer_angle_feedback_augment =leadlag_controller_.Control(-matrix_state_(0, 0), ts_) * 180 / M_PI *steer_ratio_ / steer_single_direction_max_degree_ * 100;if (std::fabs(vehicle_state->linear_velocity()) >low_speed_bound_ - low_speed_window_) {// Within the low-high speed transition window, 线性插值增强控制// augment control gain for "soft" control switchsteer_angle_feedback_augment = common::math::lerp(steer_angle_feedback_augment, low_speed_bound_ - low_speed_window_,0.0, low_speed_bound_, std::fabs(vehicle_state->linear_velocity()));}}}//方向盘转角控制量=反馈控制量+前馈控制量+增强反馈控制量(超前滞后控制器)steer_angle = steer_angle_feedback + steer_angle_feedforward +steer_angle_feedback_augment;//根据最大的横向加速度限制计算方向盘转向的限制//若限制横向加速度 最大方向盘转角百分数 = atan(ay_max * L / v^2) * steerratio * 180/pi /max_steer_ang * 100//根据上述公式可以从限制的最大横向加速度计算出最大的方向盘转角控制百分数//若无限制 最大方向盘转角百分数 = 100 const double steer_limit =FLAGS_set_steer_limit ? std::atan(max_lat_acc_ * wheelbase_ /(vehicle_state->linear_velocity() *vehicle_state->linear_velocity())) *steer_ratio_ * 180 / M_PI /steer_single_direction_max_degree_ * 100: 100.0;//这一部分主要是对方向盘转动速率进行限制,默认关闭//如果没打开,最大就限制为100//一个周期方向盘转角最大增量 = 最大方向盘角速度 * 控制周期//此刻方向盘转角控制量只能在范围内:上一时刻方向盘转角控制量 +/- 一个周期方向盘转角最大增量const double steer_diff_with_max_rate =FLAGS_enable_maximum_steer_rate_limit? vehicle_param_.max_steer_angle_rate() * ts_ * 180 / M_PI /steer_single_direction_max_degree_ * 100: 100.0;
//方向盘实际转角
//方向盘实际转角从chassis信息读,canbus从车辆can线上读到发出来的const double steering_position = chassis->steering_percentage();//如果打开MRAC模型参考自适应控制 enable_mrac_//重新计算方向盘转角控制量,并用方向盘转角和转速限制对转角控制量进行限幅if (enable_mrac_) {const int mrac_model_order = control_conf_->lat_controller_conf().steer_mrac_conf().mrac_model_order();Matrix steer_state = Matrix::Zero(mrac_model_order, 1);steer_state(0, 0) = chassis->steering_percentage();if (mrac_model_order > 1) {steer_state(1, 0) = (steering_position - pre_steering_position_) / ts_;}if (std::fabs(vehicle_state->linear_velocity()) >control_conf_->minimum_speed_resolution()) {mrac_controller_.SetStateAdaptionRate(1.0);mrac_controller_.SetInputAdaptionRate(1.0);} else {mrac_controller_.SetStateAdaptionRate(0.0);mrac_controller_.SetInputAdaptionRate(0.0);}steer_angle = mrac_controller_.Control(steer_angle, steer_state, steer_limit, steer_diff_with_max_rate / ts_);// Set the steer mrac debug messageMracDebug *mracdebug = debug->mutable_steer_mrac_debug();Matrix steer_reference = mrac_controller_.CurrentReferenceState();mracdebug->set_mrac_model_order(mrac_model_order);for (int i = 0; i < mrac_model_order; ++i) {mracdebug->add_mrac_reference_state(steer_reference(i, 0));mracdebug->add_mrac_state_error(steer_state(i, 0) -steer_reference(i, 0));mracdebug->mutable_mrac_adaptive_gain()->add_state_adaptive_gain(mrac_controller_.CurrentStateAdaptionGain()(i, 0));}mracdebug->mutable_mrac_adaptive_gain()->add_input_adaptive_gain(mrac_controller_.CurrentInputAdaptionGain()(0, 0));mracdebug->set_mrac_reference_saturation_status(mrac_controller_.ReferenceSaturationStatus());mracdebug->set_mrac_control_saturation_status(mrac_controller_.ControlSaturationStatus());}//enable_mrac_控制配置文件里的参数默认是false,略过//将当前转角设置为上一时刻的转角pre_steering_position_ = steering_position;//将enable_mrac_是否打开信息加入debug调试信息结构体debug->set_steer_mrac_enable_status(enable_mrac_);//根据当前车速对下发方向盘转角进行限幅,横向加速度的限制转化到此刻方向盘转角限制就会引入车速//steer_limit通过横向最大加速度或者方向盘最大转角限制//限幅后的方向盘转角steer_angle_limiteddouble steer_angle_limited =common::math::Clamp(steer_angle, -steer_limit, steer_limit);steer_angle = steer_angle_limited;//方向盘转角信息写入debug结构体中debug->set_steer_angle_limited(steer_angle_limited);//对方向盘转角数字滤波,然后控制百分数又限制在正负100steer_angle = digital_filter_.Filter(steer_angle);steer_angle = common::math::Clamp(steer_angle, -100.0, 100.0);//当处于D档或倒档且车速小于转向自锁速度且处于自驾模式时锁定方向盘,方向盘控制转角就保持上一次的命令值if (std::abs(vehicle_state->linear_velocity()) < FLAGS_lock_steer_speed &&(vehicle_state->gear() == canbus::Chassis::GEAR_DRIVE ||vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) &&chassis->driving_mode() == canbus::Chassis::COMPLETE_AUTO_DRIVE) {steer_angle = pre_steer_angle_;}//设定转角指令,再通过最大转角速率再次进行限制幅度 最多只能=上次的转角指令+/-最大转角速率 * Tscmd->set_steering_target(common::math::Clamp(steer_angle, pre_steer_angle_ - steer_diff_with_max_rate,pre_steer_angle_ + steer_diff_with_max_rate));//将此刻的方向盘命令值赋给上一时刻方向盘命令值cmd->set_steering_rate(FLAGS_steer_angle_rate);pre_steer_angle_ = cmd->steering_target();//日志和调试计算的一些额外信息//横向误差贡献的控制量百分数const double steer_angle_lateral_contribution =-matrix_k_(0, 0) * matrix_state_(0, 0) * 180 / M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;//横向误差率贡献的控制量百分数const double steer_angle_lateral_rate_contribution =-matrix_k_(0, 1) * matrix_state_(1, 0) * 180 / M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;//航向误差贡献的控制量百分数const double steer_angle_heading_contribution =-matrix_k_(0, 2) * matrix_state_(2, 0) * 180 / M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;//航向误差率贡献的控制量百分数const double steer_angle_heading_rate_contribution =-matrix_k_(0, 3) * matrix_state_(3, 0) * 180 / M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;//将信息放进指针debug里 //驾驶朝向debug->set_heading(driving_orientation_);//转向角debug->set_steer_angle(steer_angle);//转向角反馈debug->set_steer_angle_feedforward(steer_angle_feedforward);//转向角横向贡献debug->set_steer_angle_lateral_contribution(steer_angle_lateral_contribution);//转向角横向贡献率debug->set_steer_angle_lateral_rate_contribution(steer_angle_lateral_rate_contribution);//转向角朝向贡献debug->set_steer_angle_heading_contribution(steer_angle_heading_contribution);//转向角朝向贡献率debug->set_steer_angle_heading_rate_contribution(steer_angle_heading_rate_contribution);//转向角反馈debug->set_steer_angle_feedback(steer_angle_feedback);//转向角增强反馈debug->set_steer_angle_feedback_augment(steer_angle_feedback_augment);//当前转向角debug->set_steering_position(steering_position);debug->set_ref_speed(vehicle_state->linear_velocity());//将debug和chassis信息放入记录日志里ProcessLogs(debug, chassis);return Status::OK();
}
//mrac模型参考自适应控制的复位函数
//enable_steer_mrac_control,其默认关闭,是另外一种横向控制器
Status LatController::Reset() {matrix_state_.setZero();if (enable_mrac_) {mrac_controller_.Reset();}return Status::OK();
}
//横向控制器的车辆状态矩阵函数
SimpleLateralDebug是control_cmd.proto下的一个message类型,包含各种调试信息
//用于返回车辆当前状态
void LatController::UpdateState(SimpleLateralDebug *debug) {auto vehicle_state = injector_->vehicle_state();//是否使用navigation_modeif (FLAGS_use_navigation_mode) {ComputeLateralErrors(0.0, 0.0, driving_orientation_, vehicle_state->linear_velocity(),vehicle_state->angular_velocity(), vehicle_state->linear_acceleration(),trajectory_analyzer_, debug);} else {// Transform the coordinate of the vehicle states from the center of the// rear-axis to the center of mass, if conditions matchedconst auto &com = vehicle_state->ComputeCOMPosition(lr_);ComputeLateralErrors(com.x(), com.y(), driving_orientation_,vehicle_state->linear_velocity(), vehicle_state->angular_velocity(),vehicle_state->linear_acceleration(), trajectory_analyzer_, debug);}// 状态矩阵更新;//当打开这个e1和e3分别赋值横向反馈误差和航向反馈误差,和下面else两行的区别在哪里?//若打开lookahead(D档),lookback(R档)则x中的e1,e3就为考虑了lookahead的误差//lateral_error_feedback = lateral_error + 参考点到lookahead点的横向误差//heading_error_feedback = heading_error + ref_heading - lookahead点的heading 实际上就是匹配点到lookahead点的航向差 //heading_error = 车辆heading - ref_headingif (enable_look_ahead_back_control_) {matrix_state_(0, 0) = debug->lateral_error_feedback();matrix_state_(2, 0) = debug->heading_error_feedback();} else {matrix_state_(0, 0) = debug->lateral_error();matrix_state_(2, 0) = debug->heading_error();}matrix_state_(1, 0) = debug->lateral_error_rate();matrix_state_(3, 0) = debug->heading_error_rate();// 这一部分是更新状态矩阵中的preview项,但是preview_window默认为0忽略for (int i = 0; i < preview_window_; ++i) {const double preview_time = ts_ * (i + 1);const auto preview_point =trajectory_analyzer_.QueryNearestPointByRelativeTime(preview_time);const auto matched_point = trajectory_analyzer_.QueryNearestPointByPosition(preview_point.path_point().x(), preview_point.path_point().y());const double dx =preview_point.path_point().x() - matched_point.path_point().x();const double dy =preview_point.path_point().y() - matched_point.path_point().y();const double cos_matched_theta =std::cos(matched_point.path_point().theta());const double sin_matched_theta =std::sin(matched_point.path_point().theta());const double preview_d_error =cos_matched_theta * dy - sin_matched_theta * dx;matrix_state_(basic_state_size_ + i, 0) = preview_d_error;}
}
//更新矩阵函数,主要是更新系数矩阵A,Ad
void LatController::UpdateMatrix() {double v;//倒档代替横向平动动力学用对应的运动学模型if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {v = std::min(injector_->vehicle_state()->linear_velocity(),-minimum_speed_protection_);matrix_a_(0, 2) = matrix_a_coeff_(0, 2) * v;} else {//v为车辆纵向速度和最小速度保护里的最大值v = std::max(injector_->vehicle_state()->linear_velocity(),minimum_speed_protection_);matrix_a_(0, 2) = 0.0;}//更新A矩阵中与v有关的项matrix_a_(1, 1) = matrix_a_coeff_(1, 1) / v;matrix_a_(1, 3) = matrix_a_coeff_(1, 3) / v;matrix_a_(3, 1) = matrix_a_coeff_(3, 1) / v;matrix_a_(3, 3) = matrix_a_coeff_(3, 3) / v;//定义了单位阵(A列xA列)Matrix matrix_i =Matrix::Identity(matrix_a_.cols(), matrix_a_.cols());//计算A矩阵的离散化形式Ad,用双线性变换法matrix_ad_ = (matrix_i - ts_ * 0.5 * matrix_a_).inverse() *(matrix_i + ts_ * 0.5 * matrix_a_);
}
//更新组装矩阵Adc,就是Ad考虑preview后扩展的结果
void LatController::UpdateMatrixCompound() {// Initialize preview matrixmatrix_adc_.block(0, 0, basic_state_size_, basic_state_size_) = matrix_ad_;matrix_bdc_.block(0, 0, basic_state_size_, 1) = matrix_bd_;if (preview_window_ > 0) {matrix_bdc_(matrix_bdc_.rows() - 1, 0) = 1;// Update A matrix;for (int i = 0; i < preview_window_ - 1; ++i) {matrix_adc_(basic_state_size_ + i, basic_state_size_ + 1 + i) = 1;}}
}
//计算横向控制的前馈量
double LatController::ComputeFeedForward(double ref_curvature) const {
//kv=lr*m/2/Cf/L - lf*m/2/Cr/Lconst double kv =lr_ * mass_ / 2 / cf_ / wheelbase_ - lf_ * mass_ / 2 / cr_ / wheelbase_;// 计算前馈项并且转化成百分数const double v = injector_->vehicle_state()->linear_velocity();double steer_angle_feedforwardterm;if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {steer_angle_feedforwardterm = wheelbase_ * ref_curvature * 180 / M_PI *steer_ratio_ /steer_single_direction_max_degree_ * 100;} else {steer_angle_feedforwardterm =(wheelbase_ * ref_curvature + kv * v * v * ref_curvature -matrix_k_(0, 2) *(lr_ * ref_curvature -lf_ * mass_ * v * v * ref_curvature / 2 / cr_ / wheelbase_)) *180 / M_PI * steer_ratio_ / steer_single_direction_max_degree_ * 100;}return steer_angle_feedforwardterm;
}
//计算横向误差,放入debug指针中
//参数:xy坐标,车辆当前heading,纵向速度v,heading变化率,纵向加速度
//轨迹相关信息trajectory_analyzer对象用于提供轨迹的参考点,匹配点,lookahead点等信息
void LatController::ComputeLateralErrors(const double x, const double y, const double theta, const double linear_v,const double angular_v, const double linear_a,const TrajectoryAnalyzer &trajectory_analyzer, SimpleLateralDebug *debug) {//TrajectoryPoint类包含轨迹点的v,acc,jerk,相对时间,前轮转向角等信息TrajectoryPoint target_point;
//如果只将车辆当前的时间向前加固定时间长度后在轨迹上对应点作为目标点if (FLAGS_query_time_nearest_point_only) {//在时域上向前看0.8秒作为目标点target_point = trajectory_analyzer.QueryNearestPointByAbsoluteTime(Clock::NowInSeconds() + query_relative_time_);} else {//默认不采用导航模式,直接看elseif (FLAGS_use_navigation_mode &&!FLAGS_enable_navigation_mode_position_update) {target_point = trajectory_analyzer.QueryNearestPointByAbsoluteTime(Clock::NowInSeconds() + query_relative_time_);} else {//默认不采用导航模式,则目标点取轨迹上距离当前车辆xy坐标点最近的点,默认目标点就是取距离最近点target_point = trajectory_analyzer.QueryNearestPointByPosition(x, y);}}//dx就是当前车辆和目标点的x坐标之差const double dx = x - target_point.path_point().x();//dy就是当前车辆和目标点的y坐标之差const double dy = y - target_point.path_point().y();
//将目标点的x坐标存入debug指针debug->mutable_current_target_point()->mutable_path_point()->set_x(target_point.path_point().x());//将目标点的y坐标存入debug指针debug->mutable_current_target_point()->mutable_path_point()->set_y(target_point.path_point().y());ADEBUG << "x point: " << x << " y point: " << y;ADEBUG << "match point information : " << target_point.ShortDebugString();
//计算目标点处的heading角的正余弦值const double cos_target_heading = std::cos(target_point.path_point().theta());const double sin_target_heading = std::sin(target_point.path_point().theta());
//根据目标点处的heading角正余弦值计算横向误差double lateral_error = cos_target_heading * dy - sin_target_heading * dx;if (FLAGS_enable_navigation_mode_error_filter) {//如果打开导航模式误差滤波器,modules/control/common/control_gflags.cc里//默认时关闭的,导航模式及滤波器都关闭,直接略过lateral_error = lateral_error_filter_.Update(lateral_error);}debug->set_lateral_error(lateral_error);
//将目标点的heading角填入debug指针的debug.ref_headingdebug->set_ref_heading(target_point.path_point().theta());//计算航向误差//车辆当前航向角theta-ref_heading角//角度标准化double heading_error =common::math::NormalizeAngle(theta - debug->ref_heading());//如果导航模式滤波器打开对航向偏差进行滤波if (FLAGS_enable_navigation_mode_error_filter) {heading_error = heading_error_filter_.Update(heading_error);}debug->set_heading_error(heading_error);//在低-高速切换窗口,现行插值预瞄距离为了更柔和的预测切换double lookahead_station = 0.0;//Ddouble lookback_station = 0.0;//R//速度大于低速边界if (std::fabs(linear_v) >= low_speed_bound_) {lookahead_station = lookahead_station_high_speed_;//若为高速就用高速的预瞄距离lookback_station = lookback_station_high_speed_;} 
//若纵向速度绝对值小于低速边界-低速窗口
else if (std::fabs(linear_v) < low_speed_bound_ - low_speed_window_) {lookahead_station = lookahead_station_low_speed_;//就采用低速的预瞄距离包括非R档和R档lookback_station = lookback_station_low_speed_;} else {//若纵向速度绝对值小于低速边界又大于(低速边界-低速窗口)就插值计算预瞄距离lookahead_station = common::math::lerp(lookahead_station_low_speed_, low_speed_bound_ - low_speed_window_,lookahead_station_high_speed_, low_speed_bound_, std::fabs(linear_v));lookback_station = common::math::lerp(lookback_station_low_speed_, low_speed_bound_ - low_speed_window_,lookback_station_high_speed_, low_speed_bound_, std::fabs(linear_v));}//估计考虑预瞄距离的航向误差heading_error_feedbackdouble heading_error_feedback;if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {heading_error_feedback = heading_error;} else {auto lookahead_point = trajectory_analyzer.QueryNearestPointByRelativeTime(//目标点的相对时间再加上预瞄时间(预瞄距离/车辆纵向速度)作为总相对时间//然后去trajectory_analyzer轨迹信息上根据总相对时间找出预瞄点target_point.relative_time() +lookahead_station ///在估计预瞄时间时纵向速度若小于0.1就按0.1(std::max(std::fabs(linear_v), 0.1) * std::cos(heading_error)));//heading_error=车辆当前heading-参考点heading//heading_error_feedback=heading_error+参考点heading-预瞄点heading=车辆当前heading-预瞄点headingheading_error_feedback = common::math::NormalizeAngle(heading_error + target_point.path_point().theta() -lookahead_point.path_point().theta());}debug->set_heading_error_feedback(heading_error_feedback);//估计考虑预瞄距离的横向误差lateral_error_feedbackdouble lateral_error_feedback;if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {//倒档的lateral_error_feedback=lateral_error-倒车预瞄距离*sin(heading_error)lateral_error_feedback =lateral_error - lookback_station * std::sin(heading_error);} else {
//前进档的lateral_error_feedback=lateral_error+前进预瞄距离*sin(heading_error)lateral_error_feedback =lateral_error + lookahead_station * std::sin(heading_error);}//将lateral_error_feedback存入指针debugdebug->set_lateral_error_feedback(lateral_error_feedback);auto lateral_error_dot = linear_v * std::sin(heading_error);//横向误差率=纵向速度v*sin(heading_error)auto lateral_error_dot_dot = linear_a * std::sin(heading_error);//横向误差加速度=纵向加速度*sin(heading_error)//测试倒车航向控制if (FLAGS_reverse_heading_control) {if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {lateral_error_dot = -lateral_error_dot;lateral_error_dot_dot = -lateral_error_dot_dot;}}//将计算得到的横向误差率填入debug指针debug->set_lateral_error_rate(lateral_error_dot);//将计算得到的横向误差加速度填入debug指针debug->set_lateral_acceleration(lateral_error_dot_dot);//利用横向加速度差分得到横向加加速度jerk放入debug指针debug->set_lateral_jerk((debug->lateral_acceleration() - previous_lateral_acceleration_) / ts_);//差分法需要迭代更新下上一时刻的横向加速度previous_lateral_acceleration_ = debug->lateral_acceleration();if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {//angular_v是ComputeLateralError函数传进来的参数heading的变化率,若倒车debug.heading_rate为负的angular_vdebug->set_heading_rate(-angular_v);} else {debug->set_heading_rate(angular_v);}//参考的航向角变化率=目标点纵向速度/目标点转弯半径,绕Z轴w=v/R,下面的kappa就是曲率 结果放入debug指针中debug->set_ref_heading_rate(target_point.path_point().kappa() *target_point.v());//航向角误差率=车辆的航向角变化率-目标点航向角变化率,结果放入debug指针中debug->set_heading_error_rate(debug->heading_rate() -debug->ref_heading_rate());
//航向角变化的加速度就用差分法,这一时刻航向角变化率减去上一时刻之差然后再除以采样周期ts,结果放入debug指针中debug->set_heading_acceleration((debug->heading_rate() - previous_heading_rate_) / ts_);//目标点航向角变化的加速度也用差分法计算得到,结果放入debug指针中debug->set_ref_heading_acceleration((debug->ref_heading_rate() - previous_ref_heading_rate_) / ts_);//航向角误差变化的加速度debug->set_heading_error_acceleration(debug->heading_acceleration() -//当前时刻的航向角变化率     debug->ref_heading_acceleration());previous_heading_rate_ = debug->heading_rate();previous_ref_heading_rate_ = debug->ref_heading_rate();
//heading角的加加速度debug->set_heading_jerk((debug->heading_acceleration() - previous_heading_acceleration_) / ts_);//目标点航向角的加加速度debug->set_ref_heading_jerk((debug->ref_heading_acceleration() - previous_ref_heading_acceleration_) /ts_);//heading角误差变化率的加加速度debug->set_heading_error_jerk(debug->heading_jerk() -debug->ref_heading_jerk());//迭代将当前时刻的值赋给上一时刻,以便用差分法求导previous_heading_acceleration_ = debug->heading_acceleration();previous_ref_heading_acceleration_ = debug->ref_heading_acceleration();
//kappadebug->set_curvature(target_point.path_point().kappa());
}
//更新驾驶航向
void LatController::UpdateDrivingOrientation() {
//更新车辆状态auto vehicle_state = injector_->vehicle_state();//朝向driving_orientation_ = vehicle_state->heading();//横向控制状态方程B离散化为Bdmatrix_bd_ = matrix_b_ * ts_;//倒挡就更改方向180°if (FLAGS_reverse_heading_control) {if (vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) {driving_orientation_ =common::math::NormalizeAngle(driving_orientation_ + M_PI);// Update Matrix_b for reverse modematrix_bd_ = -matrix_b_ * ts_;ADEBUG << "Matrix_b changed due to gear direction";}}
}}  // namespace control
}  // namespace apollo

A矩阵离散化

void LatController::UpdateMatrix() {double v;// At reverse driving, replace the lateral translational motion dynamics with// the corresponding kinematic modelsif (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {v = std::min(injector_->vehicle_state()->linear_velocity(),-minimum_speed_protection_);matrix_a_(0, 2) = matrix_a_coeff_(0, 2) * v;} else {v = std::max(injector_->vehicle_state()->linear_velocity(),minimum_speed_protection_);matrix_a_(0, 2) = 0.0;}matrix_a_(1, 1) = matrix_a_coeff_(1, 1) / v;matrix_a_(1, 3) = matrix_a_coeff_(1, 3) / v;matrix_a_(3, 1) = matrix_a_coeff_(3, 1) / v;matrix_a_(3, 3) = matrix_a_coeff_(3, 3) / v;Matrix matrix_i = Matrix::Identity(matrix_a_.cols(), matrix_a_.cols());matrix_ad_ = (matrix_i - ts_ * 0.5 * matrix_a_).inverse() *(matrix_i + ts_ * 0.5 * matrix_a_);
}

B矩阵离散化

/*b = [0.0, c_f / m, 0.0, l_f * c_f / i_z]^T*/matrix_b_(1, 0) = cf_ / mass_;matrix_b_(3, 0) = lf_ * cf_ / iz_;matrix_bd_ = matrix_b_ * ts_;

反馈计算

const double steer_angle_feedback = -(matrix_k_ * matrix_state_)(0, 0) * 180 /M_PI * steer_ratio_ /steer_single_direction_max_degree_ * 100;

前馈计算:

double LatController::ComputeFeedForward(double ref_curvature) const {const double kv =lr_ * mass_ / 2 / cf_ / wheelbase_ - lf_ * mass_ / 2 / cr_ / wheelbase_;// Calculate the feedforward term of the lateral controller; then change it// from rad to %const double v = injector_->vehicle_state()->linear_velocity();double steer_angle_feedforwardterm;if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {steer_angle_feedforwardterm = wheelbase_ * ref_curvature * 180 / M_PI *steer_ratio_ /steer_single_direction_max_degree_ * 100;} else {steer_angle_feedforwardterm =(wheelbase_ * ref_curvature + kv * v * v * ref_curvature -matrix_k_(0, 2) *(lr_ * ref_curvature -lf_ * mass_ * v * v * ref_curvature / 2 / cr_ / wheelbase_)) *180 / M_PI * steer_ratio_ / steer_single_direction_max_degree_ * 100;}return steer_angle_feedforwardterm;
}

超前滞后反馈:steer_angle_feedback_augment
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
参考【运动控制】Apollo6.0的leadlag_controller解析

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

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

相关文章

皮爷咖啡基于亚马逊云科技的数据架构,加速数据治理进程

皮爷咖啡&#xff08;Peet’s Coffee&#xff09;是美国精品咖啡品牌&#xff0c;于2017年进入中国&#xff0c;为中国消费者带来传统经典咖啡饮品&#xff0c;并特别呈现更加丰富的品质咖啡饮品体验。通过深入应用亚马逊云科技云原生数据库产品Amazon Redshift以及Amazon DMS等…

新研究:Gartner 公有云成本管理框架

2023年6月28日&#xff0c;Gartner 出版了名为《Beyond FinOps: the Gartner Framework for Public Cloud Financial Management》的公有云成本管理框架&#xff0c;旨在帮助企业/组织应对公有云支出的挑战&#xff0c;同时抓住新机遇&#xff0c;推动更有效的 IT 使用。新框架…

Practices11|41. 缺失的第一个正数(数组)、73. 矩阵置零(矩阵)

41. 缺失的第一个正数(数组) 1.题目&#xff1a; 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xf…

web文件上传

文件上传指的是&#xff0c;将本地的图片、视频、音频上传到服务器&#xff0c;提供给其他用户浏览和下载的过程 前端需求 想要进行文件上传对于web前端来说有三个重要要素 1.<input type"file" name"image"> 提供这样的file文件上传格式 2. metho…

无代码集成飞书连接更多应用

场景描述&#xff1a; 基于飞书开放平台能力&#xff0c;无代码集成飞书连接更多应用&#xff0c;打通数据孤岛。通过Aboter可轻松搭建业务自动化流程&#xff0c;实现多个应用之间的数据连接。 支持包括飞书事件监听和接口调用的能力&#xff1a; 事件监听&#xff1a; 用…

神经网络基础-神经网络补充概念-54-softmax回归

概念 Softmax回归&#xff08;Softmax Regression&#xff09;是一种用于多分类任务的机器学习算法&#xff0c;特别是在神经网络中常用于输出层来进行分类。它是Logistic回归在多分类问题上的推广。 原理 Softmax回归的主要思想是将原始的线性分数&#xff08;得分&#xf…

SOPC之NIOS Ⅱ实现电机转速PID控制

通过FPGA开发板上的NIOS Ⅱ搭建电机控制的硬件平台&#xff0c;包括电机正反转、编码器的读取&#xff0c;再通过软件部分实现PID算法对电机速度进行控制&#xff0c;使其能够渐近设定的编码器目标值。 一、PID算法 PID算法&#xff08;Proportional-Integral-Derivative Algo…

Yalmip入门教程(5)-约束条件操作的相关函数

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;https://yalmip.github.io/tutorials/ 这篇博客将详细介绍yalmip工具箱中约束条件操作相关函数的用法。 1.约束条件操作的相关函数 1.1 boundingbox函数 boundingbox函数用于求出一组约…

opencv 进阶13-Fisherfaces 人脸识别-函数cv2.face.FisherFaceRecognizer_create()

Fisherfaces 人脸识别 PCA 方法是 EigenFaces 方法的核心&#xff0c;它找到了最大化数据总方差特征的线性组合。不可否认&#xff0c;EigenFaces 是一种非常有效的方法&#xff0c;但是它的缺点在于在操作过程中会损失许多特征信息。 因此&#xff0c;在一些情况下&#xff0c…

PSP - 开源可训练的蛋白质结构预测框架 OpenFold 的环境配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132334671 Paper: OpenFold: Retraining AlphaFold2 yields new insights into its learning mechanisms and capacity for generalization Open…

制作电商网站帮助中心,节省60%的咨询工作量

随着电子商务的快速发展&#xff0c;越来越多的企业选择在网上建立自己的电商平台。然而&#xff0c;一旦电商网站上线&#xff0c;就会面临一系列的问题和挑战。其中一个重要问题是如何有效管理和解答大量用户的咨询和问题&#xff0c;这对于提高用户体验和促进销售至关重要。…

YOLOv5、v8改进:引入SKAttention注意力机制

目录 1.简介 2.YOLOv5改进 2.1增加以下SKAttention.yaml文件 2.2common.py配置 2.3yolo.py配置 1.简介 论文链接&#xff1a;https://arxiv.org/pdf/1903.06586.pdf 最近对卷积神经网络中的“自适应调整感受野”这样的操作很感兴趣&#xff0c;从字面的意思可以理解&…

Spring练习---28 (用户表和角色表分析,角色列表展示,角色层和Dao层的设置,页面展示操作)

84、下面进入我们的业务层面&#xff0c;进入我们的业务层面我们先分析一个东西&#xff0c;我们要分析用户和角色的关系&#xff0c;因为我们只有在分析完用户和角色之间的关系后&#xff0c;我们才知道表的关系&#xff0c;实体的关系 85、现在我们先画一张表&#xff0c;分析…

Apache Doris IP变更问题详解

Apache Doris IP变更问题详解 一、背景二、环境硬件信息软件信息 三、FE恢复3.1 异常日志3.2 获取当前ip3.3 重置ip信息3.4 重置元数据记录3.5 元数据模式恢复3.6 重置fe集群节点3.7 关闭元数据模式重启fe 四、BE恢复4.1 获取当前ip4.2 重置ip信息4.3 重置be集群节点 一、背景 …

vue3 基础知识

vue3创建一个项目 PS D:\code> npm init vuelatestVue.js - The Progressive JavaScript Framework√ Add TypeScript? ... No / Yes √ Add JSX Support? ... No / Yes √ Add Vue Router for Single Page Application development? ... No / Yes √ Add Pinia for sta…

WordPress用于您的企业网站的优点和缺点

如今&#xff0c;WordPress 被广泛认为是一个可靠、可扩展且安全的平台&#xff0c;能够为商业网站提供支持。然而&#xff0c;许多人质疑 WordPress 是否是适合企业的平台。 这就是我们创建本指南的原因。通过探索 WordPress 的优点和缺点&#xff0c;您可以确定世界上最受欢…

linux部署kafka3.5.1(单机)

一、下载jdk17 kafka3.x版本需要jdk11以上版本才能更好的兼容&#xff0c;jdk11、jdk17都是LTS长期维护版本&#xff0c;而且jdk17支持springboot3.x,所以我选择了openjdk17。 下载地址: Archived OpenJDK GA Releaseshttps://jdk.java.net/archive/ 二、上传jdk安装包解压 …

PHP加密与安全的最佳实践

PHP加密与安全的最佳实践 概述 在当今信息时代&#xff0c;数据安全是非常重要的。对于开发人员而言&#xff0c;掌握加密和安全的最佳实践是必不可少的。PHP作为一种常用的后端开发语言&#xff0c;提供了许多功能强大且易于使用的加密和安全性相关函数和类。本文将介绍一些P…

【目标检测中对IoU的改进】GIoU,DIoU,CIoU的详细介绍

文章目录 1、IoU2、GIoU(Generalized Intersection over Union)3、DIoU4、CIoU 1、IoU IoU为交并比&#xff0c;即对于pred和Ground Truth&#xff1a;交集/并集 1、IoU可以作为评价指标使用&#xff0c;也可以用于构建IoU loss 1 - IoU 缺点&#xff1a; 2、对于pred和GT相…

Android Studio导入项目需要做的一些配置

点击项目结构 选择本地安装的SDK、NDK目录 选择java版本 重新加载项目 Clean Project Rebuild Project 选择要构建的版本 可选debug和release 打包apk安装包 打包完成&#xff0c;就可以安装到安卓手机了