文章目录
- 1. 纵向控制算法
- 1.1 算法结构
- 1.1.1 外环:位置环
- 1.1.2 内环:速度环
- 1.2 参数整定
- 2. 代码解析
- 2.1 控制器初始化 LonController::Init()
- 2.1.1 PID控制参数和标定表参数加载
- 2.1.2 PID控制器初始化
- 2.1.3 超前/滞后控制器初始化
- 2.1.4 俯仰角滤波器初始化
- 2.1.5 标定表插值查表器初始化
- 2.2 加速度命令计算 LonController::ComputeControlCommand()
- 2.2.1 纵向误差计算
- 2.2.2 加速度控制命令计算
- 2.2.3 油门控制命令计算
本章将详细讲解Apollo纵向控制器LonController - lon_based_pid_controller。
1. 纵向控制算法
Apollo 自动驾驶平台的纵向控制算法采用位置-速度双环 PID 控制器,用于实现车辆在纵向运动中的精确控制。该算法通过分层控制结构,将目标位置和速度的控制任务分解为外环(位置环)和内环(速度环),从而实现高精度的跟踪和稳定的动态响应。
1.1 算法结构
1.1.1 外环:位置环
输入:
- 目标位置(来自规划模块)。
- 当前车辆位置(来自定位模块)。
输出:
- 目标速度(作为内环的设定值)。
控制逻辑:
- 计算位置误差:
e p o s i t i o n = t a r g e t _ p o s i t i o n − c u r r e n t _ p o s i t i o n e e_{position}=target\_position − current\_positione eposition=target_position−current_positione - 使用 PID 控制器计算目标速度:
t a r g e t _ v e l o c i t y = K p _ p o s ⋅ e p o s i t i o n + K i _ p o s ⋅ ∫ e p o s i t i o n d t + K d _ p o s ⋅ d e p o s i t i o n d t target\_velocity =K_{p\_pos} \cdot e_{position}+K_{i\_pos} \cdot \int e_{position} dt+K_{d\_pos} \cdot \frac{d e_{position}}{dt} target_velocity=Kp_pos⋅eposition+Ki_pos⋅∫epositiondt+Kd_pos⋅dtdeposition - 对目标速度进行限幅,确保其在合理范围内。
1.1.2 内环:速度环
输入:
- 目标速度(来自外环)。
- 当前车辆速度(来自车辆状态反馈)。
输出:
- 控制量(如油门或刹车指令)。
控制逻辑:
-
计算速度误差:
e v e l o c i t y = t a r g e t _ v e l o c i t y − c u r r e n t _ v e l o c i t y e_{velocity}=target\_velocity−current\_velocity evelocity=target_velocity−current_velocity -
使用 PID 控制器计算控制量:
c o n t r o l _ o u t p u t = K p _ v e l ⋅ e v e l o c i t y + K i _ v e l ⋅ ∫ e v e l o c i t y d t + K d _ v e l ⋅ d e v e l o c i t y d t control\_output =K_{p\_vel} \cdot e_{velocity}+K_{i\_vel} \cdot \int e_{velocity} dt+K_{d\_vel} \cdot \frac{d e_{velocity}}{dt} control_output=Kp_vel⋅evelocity+Ki_vel⋅∫evelocitydt+Kd_vel⋅dtdevelocity -
对控制量进行限幅,确保其在执行器的可操作范围内。
1.2 参数整定
PID 控制器的性能依赖于参数的合理整定。以下是参数整定的基本原则:
- Kp:调整系统对目标的响应速度,但过大的Kp可 能会导致明显的超调和振荡,降低系统的动态性能。
- Ki:有利于减小响应的超调量,减小系统的稳态误 差,提高控制精度。
- Kd:有利于减小系统超调量,提高系统稳定性
总结一句话就是:先调Kp,再调Ki,先调速度闭环,再调位置闭环。
2. 代码解析
其实官方已经给出了一个比较详细的图(/apollo/modules/control/controllers/lon_based_pid_controller/docs/images/control-controller-lon-based-pid-controller.png)来展示LonController 的代码框架,主要包含两部分:
- 控制器初始化 LonController::Init(),完成控制参数和标定表参数的加载,初始化位置\速度PID控制器,初始化位置\速度超前/滞后控制器等等准备工作;
- 加速度命令计算 LonController::ComputeControlCommand(),这是纵向控制命令计算的整个流程,大体上也可以分为3步:
- 首先,根据参考轨迹和Frenet坐标系,计算出纵向误差;
- 然后,根据位置PID控制器和速度PID控制器计算出了参考加速度;
- 最后,根据车辆当前的速度和所需的加速度并且参照标定表计算出油门控制命令。
2.1 控制器初始化 LonController::Init()
2.1.1 PID控制参数和标定表参数加载
Status LonController::Init(std::shared_ptr<DependencyInjector> injector) {if (!ControlTask::LoadConfig<LonBasedPidControllerConf>(&lon_based_pidcontroller_conf_)) {AERROR << "failed to load control conf";return Status(ErrorCode::CONTROL_INIT_ERROR,"failed to load lon control_conf");}if (!ControlTask::LoadCalibrationTable(&calibration_table_)) {AERROR << "failed to load calibration table";return Status(ErrorCode::CONTROL_INIT_ERROR,"lon failed to load calibration table");}...
}
PID控制参数位于"/apollo/modules/control/controllers/lon_based_pid_controller/conf/controller_conf.pb.txt",这里使用ProtoBuf,将该文件里面的所有数据保存到lon_based_pidcontroller_conf_
对象里面。
标定表参数位于"/apollo/modules/control/control_component/conf/calibration_table.pb.txt",这里同样使用ProtoBuf,将该文件里面的所有数据保存到calibration_table_
对象里面。
上述关于如何ProtoBuf加载配置参数,在Planning的初始化部分已经详细介绍过,如果还有不懂的,大家可以往前看一看。关于什么是标定表,2.1.5节会详细介绍。
2.1.2 PID控制器初始化
Status LonController::Init(std::shared_ptr<DependencyInjector> injector) {...// 位置、速度PID控制器初始化station_pid_controller_.Init(lon_based_pidcontroller_conf_.station_pid_conf());speed_pid_controller_.Init(lon_based_pidcontroller_conf_.low_speed_pid_conf());...
}
Apollo官方提供了3个PID控制器,默认使用PIDController:
PIDBCController和PIDICController主要针对积分饱和问题做了优化:
- IC -> 积分遇限消弱法
- BC -> 反馈抑制抗饱和
- 设定阈值,偏差大时才启用积分 , 即积分分离
这里对PIDController的初始化也比较简单:
2.1.3 超前/滞后控制器初始化
超前/滞后控制器默认关闭,通过配置enable_reverse_leadlag_compensation可以打开。
Status LonController::Init(std::shared_ptr<DependencyInjector> injector) {...// 位置、速度超前/滞后控制器初始化if (enable_leadlag) {station_leadlag_controller_.Init(lon_based_pidcontroller_conf_.reverse_station_leadlag_conf(), ts);speed_leadlag_controller_.Init(lon_based_pidcontroller_conf_.reverse_speed_leadlag_conf(), ts);}...
}
2.1.4 俯仰角滤波器初始化
初始化俯仰角滤波器 digital_filter_pitch_angle_
,主要是设置滤波器的周期和滤波系数。俯仰角滤波器用于车辆俯仰角的过滤,而车辆俯仰角用于计算坡度加速度补偿。
Status LonController::Init(std::shared_ptr<DependencyInjector> injector) {...// 俯仰角低通滤波处理,考虑坡度产生的纵向加速度SetDigitalFilterPitchAngle(lon_controller_conf);...
}
LonController::SetDigitalFilterPitchAngle
void LonController::SetDigitalFilterPitchAngle(const LonControllerConf &lon_controller_conf) {//配置中读到的参数滤波器的截止频率存到变量cutoff_freq中double cutoff_freq = lon_controller_conf.pitch_angle_filter_conf().cutoff_freq();//配置中读到的参数滤波器的采样周期存到变量ts中double ts = lon_controller_conf.ts();SetDigitalFilter(ts, cutoff_freq, &digital_filter_pitch_angle_);
}
LonController::SetDigitalFilter
void LonController::SetDigitalFilter(double ts, double cutoff_freq,common::DigitalFilter *digital_filter) {std::vector<double> denominators;std::vector<double> numerators;common::LpfCoefficients(ts, cutoff_freq, &denominators, &numerators);digital_filter->set_coefficients(denominators, numerators);
}
2.1.5 标定表插值查表器初始化
Status LonController::Init(std::shared_ptr<DependencyInjector> injector) {...InitControlCalibrationTable();...
}
(1)为什么使用标定表?
- 纵向动力学包括发动机/电动机、变速器等传动机构,建模难度大,计算复杂度高
- 不同车辆的动力学模型不相同
- 同一车辆的动力学模型会随着轮胎磨损等影响而变化
因此,Apollo采用标定表的方式进行纵向控制,从而避免了对于纵向动力学的建模过程。
上面其实是一些比较专业的说法,Apollo官方给出了以下的解释,其实更简单明了:Apollo使用的车辆底盘控制接口是油门、刹车,所以要使用油门、刹车标定表!换而言之,如果底盘控制接口不是油门、刹车,就不要这两个表。
其实现在自动驾驶,大多数都使用纵向加速度作为纵向控制接口。所以大家忘了这两个表吧!!!
(2)标定表介绍
参考论文:1808.10134 Baidu Apollo Auto-Calibration System - An
Industry-Level Data-Driven and Learning based Vehicle Longitude
Dynamic Calibrating Algorithm (arxiv.org)
这篇论文是Apollo发表的一种基于离线模型和在线学习的(对于纵向动力学)自动校准系统。首先从人类驾驶数据中生成离线标定表。离线表作为后期使用的初始猜测,只需要二十分钟的数据收集和处理。然后,基于实时性能分析,采用在线学习算法适当更新初始表(离线表)。
基于人体驾驶数据,生成标定表,将油门、刹车、车速作为输入,加速度作为输出。简单来说,就是我们有了标定表之后,可以根据当前速度和所需加速度确定油门/刹车控制量。
其中,T为标定表,acc为期望加速度,v为当前车速,cmd为控制指令
论文中提到了离线模型和在线模型这两种调整标定表的过程:
-
离线模型:离线模型从人工驾驶数据中生成最能反映车辆行驶时纵向性能的初始标定表。
离线算法的工作流程包括:( 1 )采集人类驾驶数据;( 2 )对数据进行预处理并选择输入特征;( 3 )通过机器学习模型生成标定表 -
在线学习:自动驾驶模式下,在线算法根据实时反馈即时调整离线表格。其目的是基于人工驾驶数据建立的离线模型,以最佳匹配当前车辆动力学。该算法需要处理的几个挑战包括车辆载荷的频繁变化和机械部件的长期疲劳。
在线算法的工作流程包括:( 1 )实时采集车辆状态和反馈;( 2 )预处理和筛选数据;( 3 )相应调整标定表。
(3)标定表插值查表器
在2.1.1节就已经从配置文件中载入了标定表的参数,并存到calibration_table_对象里面。这里使用该对象里面的数据,创建一个二维插值查表器control_interpolation_
,由于加速度和加速度被合成了一张表,所以后续只需要通过车速和目标加速度就可以查表插值计算得到目标油门/刹车开度。
2.2 加速度命令计算 LonController::ComputeControlCommand()
2.2.1 纵向误差计算
纵向误差计算在一个单独的成员函数ComputeLongitudinalErrors中。
注:这个函数其实计算了横纵向所有的误差。
Status LonController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,control::ControlCommand *cmd) {...ComputeLongitudinalErrors(trajectory_analyzer_.get(), preview_time, ts, debug);...
}
在讲解误差计算之前,先给出一些基本概念。如下图,Planning给出的轨迹为虚线所示,自车当前的位置 ( x , y ) (x,y) (x,y)。Planning参考轨迹起始点为全局时间戳,其他点采用相对时间戳表示,即相对于起始点的时间戳。
为了计算车辆纵向的位置误差和速度误差,我们需要知道车辆在轨迹上的实际位置点–匹配点,以及目标位置点–参考点或预瞄点。
- 匹配点( x m a t c h {{x}}{}_{{match}} xmatch , y m a t c h {{y}}{}_{{match}} ymatch ):参考路径上,与自车当前位置距离最近点。由位置寻找,与相对时间戳无关。
void LonController::ComputeLongitudinalErrors(const TrajectoryAnalyzer *trajectory_analyzer, const double preview_time,const double ts, SimpleLongitudinalDebug *debug) {...// 找到距离车辆位置最近的参考路径点// 匹配最近点时:插值法+优选法(黄金分割法)来找到最近的点auto matched_point = trajectory_analyzer->QueryMatchedPathPoint(vehicle_state->x(), vehicle_state->y());// 根据匹配点建立Frenet坐标系,并计算横/纵向坐标trajectory_analyzer->ToTrajectoryFrame(vehicle_state->x(), vehicle_state->y(), vehicle_state->heading(),vehicle_state->linear_velocity(), matched_point, &s_matched,&s_dot_matched, &d_matched, &d_dot_matched);...
}
匹配点的计算使用了黄金分隔法来加快求解速度,可以参考 https://en.wikipedia.org/wiki/Golden-section_search 。
得到匹配点后,就是计算该点的Frenet坐标 ( s , s ˙ , d , d ˙ ) (s,\dot{s} ,d,\dot{d} ) (s,s˙,d,d˙):
车辆在Frenet坐标系下的相对坐标表示:
车辆的全局坐标需考虑参考点的纵向里程距离 s m a t c h {{s}}{}_{{match}} smatch :
车辆的纵向速度和横向速度如下:
- 参考点( x r e f {{x}}{}_{{ref}} xref , y r e f {{y}}{}_{{ref}} yref):参考轨迹上,当前时刻车辆应该处于的位置。
- 预瞄点( x p r e {{x}}{}_{{pre}} xpre, y p r e {{y}}{}_{{pre}} ypre):参考轨迹上,预瞄绝对时间处车辆应该处于的位置。
预瞄绝对时间戳 = 参考绝对时间戳 + 预瞄时间。
void LonController::ComputeLongitudinalErrors(const TrajectoryAnalyzer *trajectory_analyzer, const double preview_time,const double ts, SimpleLongitudinalDebug *debug) {...double preview_time = lon_controller_conf.preview_window() * ts;// 当前时间确定参考点,预瞄时间确定预瞄点// 预瞄时间 = 预瞄窗口数目 * 预瞄窗口时间 (此例为 20 * 0.01 = 0.2s)double current_control_time = ::apollo::cyber::Clock::NowInSeconds();double preview_control_time = current_control_time + preview_time;// 根据绝对时间戳匹配参考点和预瞄点TrajectoryPoint reference_point =trajectory_analyzer->QueryNearestPointByAbsoluteTime(current_control_time);TrajectoryPoint preview_point =trajectory_analyzer->QueryNearestPointByAbsoluteTime(preview_control_time);...
}
// header_time_为参考轨迹的全局时间戳
// 相对路径时间戳 = 绝对时间戳 - 参考轨迹全局时间戳
// 最后根据相对路径时间戳找到参考轨迹对应点
TrajectoryPoint TrajectoryAnalyzer::QueryNearestPointByAbsoluteTime(const double t) const {return QueryNearestPointByRelativeTime(t - header_time_);
}
最后根据匹配点和参考点/预瞄点计算位置误差和速度误差:
根据参考点计算出的位置和速度误差如式(5)所示:
根据预瞄点计算出的位置和速度误差如式(6)所示:
在之后的加速度控制命令计算中,可以自由选择使用参考点还是预瞄点的位置/速度误差,取决于参数配置文件。
void LonController::ComputeLongitudinalErrors(const TrajectoryAnalyzer *trajectory_analyzer, const double preview_time,const double ts, SimpleLongitudinalDebug *debug) {...//纵向位置误差debug.station_error=参考点路径点的累积弧长-匹配点的累积弧长(匹配点就是路径最近点)debug->set_station_error(reference_point.path_point().s() - s_matched);debug->set_speed_error(reference_point.v() - s_dot_matched); // 速度误差 = 参考点速度 - 匹配点速度 // 预览点位置误差 = 预览点的纵向位置s - 匹配点纵向位置sdebug->set_preview_station_error(preview_point.path_point().s() - s_matched);// 预览点速度误差 = 预览点的纵向速度v - 匹配点纵向速度debug->set_preview_speed_error(preview_point.v() - s_dot_matched);...
}
2.2.2 加速度控制命令计算
这部分对照第一张图,更容易理解。其实也就是计算好相应的误差后带入到PID控制器即可。
1)位置PID控制器
输入:位置偏差 = 规划位置 - 实际位置
输出:速度补偿
Status LonController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,control::ControlCommand *cmd) {...double station_error_limit = lon_based_pidcontroller_conf_.station_error_limit();double station_error_limited = 0.0; // 限幅后的纵向位置误差// 考虑预瞄时纵向位置偏差的上下限不同if (lon_based_pidcontroller_conf_.enable_speed_station_preview()) {// preview_station_error = 预览点纵向位置 - 匹配点纵向位置station_error_limited = common::math::Clamp(debug->preview_station_error(),-station_error_limit, station_error_limit);} else {// station_error = 参考点纵向位置-匹配点纵向位置station_error_limited = common::math::Clamp(debug->station_error(), -station_error_limit,station_error_limit);}if (trajectory_message_->gear() == canbus::Chassis::GEAR_REVERSE) {if (CheckPit::CheckInPit(debug, &lon_based_pidcontroller_conf_,injector_->vehicle_state()->linear_velocity(),trajectory_message_->is_replan())) {ADEBUG << "in pit";station_pid_controller_.SetPID(lon_based_pidcontroller_conf_.pit_station_pid_conf());speed_pid_controller_.SetPID(lon_based_pidcontroller_conf_.pit_speed_pid_conf());} else {station_pid_controller_.SetPID(lon_based_pidcontroller_conf_.reverse_station_pid_conf());speed_pid_controller_.SetPID(lon_based_pidcontroller_conf_.reverse_speed_pid_conf());}if (enable_leadlag) {station_leadlag_controller_.SetLeadlag(lon_based_pidcontroller_conf_.reverse_station_leadlag_conf());speed_leadlag_controller_.SetLeadlag(lon_based_pidcontroller_conf_.reverse_speed_leadlag_conf());}} else if (injector_->vehicle_state()->linear_velocity() <= lon_based_pidcontroller_conf_.switch_speed()) {if (CheckPit::CheckInPit(debug, &lon_based_pidcontroller_conf_,injector_->vehicle_state()->linear_velocity(),trajectory_message_->is_replan())) {ADEBUG << "in pit";//速度PID控制器对象speed_pid_controller_加载控制配置文件中低速PID参数station_pid_controller_.SetPID(lon_based_pidcontroller_conf_.pit_station_pid_conf());speed_pid_controller_.SetPID(lon_based_pidcontroller_conf_.pit_speed_pid_conf());} else {station_pid_controller_.SetPID(lon_based_pidcontroller_conf_.station_pid_conf());speed_pid_controller_.SetPID(lon_based_pidcontroller_conf_.low_speed_pid_conf());}} else {//速度PID控制器对象加载控制配置文件中高速PID参数,通常低速PID参数要更大些station_pid_controller_.SetPID(lon_based_pidcontroller_conf_.station_pid_conf());speed_pid_controller_.SetPID(lon_based_pidcontroller_conf_.high_speed_pid_conf());}// 位置环输出到速度环输入补偿,也就是速度环为内环// delta_s =vt 我们期望在当前纵向偏差下的速度补偿//速度偏差 = 位置PID控制器根据(限幅后位置误差,采样周期)计算出控制量即速度double speed_offset = station_pid_controller_.Control(station_error_limited, ts);if (enable_leadlag) {speed_offset = station_leadlag_controller_.Control(speed_offset, ts);}...
}
2)速度PID控制器
输入:速度偏差 = 速度误差(规划速度 - 实际速度) + 速度补偿
输出:加速度补偿
Status LonController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,control::ControlCommand *cmd) {...double speed_controller_input = 0.0;double speed_controller_input_limit = lon_based_pidcontroller_conf_.speed_controller_input_limit();double speed_controller_input_limited = 0.0;if (lon_based_pidcontroller_conf_.enable_speed_station_preview()) {// 速度控制器的输入 = 位置控制器计算出的speed_offset + 当前时间向前加上预览时间在轨迹上的对应点的速度和当前车速的偏差speed_controller_input = speed_offset + debug->preview_speed_error();} else {// 速度控制器的输入 = 位置控制器计算出的speed_offset + 参考点车速和当前车速的偏差speed_controller_input = speed_offset + debug->speed_error();}speed_controller_input_limited = common::math::Clamp(speed_controller_input, -speed_controller_input_limit,speed_controller_input_limit);double acceleration_cmd_closeloop = 0.0;// 求解速度环输出,给出加速度结果 v = at//闭环的加速度指令就等于速度PID控制器根据速度控制器的输入,以及采样周期去计算acceleration_cmd_closeloop = speed_pid_controller_.Control(speed_controller_input_limited, ts);...
}
3)期望加速度
期望加速度 = 加速度补偿 + 预瞄点参考加速度 + 坡度加速度补偿
Status LonController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,control::ControlCommand *cmd) {...//定义斜坡补偿加速度 = (重力加速度 * 车辆俯仰角的正弦值)再经过数字滤波器滤波得到斜坡加速度补偿double slope_offset_compensation = lon_based_pidcontroller_conf_.use_opposite_slope_compensation() *GRA_ACC * std::sin(vehicle_pitch_rad + FLAGS_pitch_offset_deg * M_PI / 180);//判断坡道补偿加速度是否为非数NaN,当浮点数过小下溢就可能出现NaN非数if (std::isnan(slope_offset_compensation)) {slope_offset_compensation = 0;}debug->set_slope_offset_compensation(slope_offset_compensation);//总的加速度指令 = 闭环加速度指令 + 预览参考加速度 + 坡道补偿加速度double acceleration_cmd = acceleration_cmd_closeloop + debug->preview_acceleration_reference() +lon_based_pidcontroller_conf_.enable_slope_offset() * debug->slope_offset_compensation();...
}
不同车辆的纵向控制大致分为两种:
- 加速度命令:对于加速度命令控制的车辆,到这里已经计算完成,限幅后可直接发给底层控制器
- 油门/刹车命令:需根据加速度命令参照标定表继续出计算油门/刹车
2.2.3 油门控制命令计算
根据标定表插值查表,计算油门/刹车控制命令。这部分很简单,唯一需要说一下的是,最后根据acceleration_lookup正负,决定将查表值calibration_value赋值给油门还是刹车。
标定表输入:速度、加速度
标定表输出:油门/刹车命令
Status LonController::ComputeControlCommand(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis,const planning::ADCTrajectory *planning_published_trajectory,control::ControlCommand *cmd) {...//定义油门指令的下边界,为 车辆配置里的throttle_deadzone 和 lon_controller_conf配置里的throttle_minimum_action 两者中的较大值double throttle_lowerbound = std::max(vehicle_param_.throttle_deadzone(),lon_based_pidcontroller_conf_.throttle_minimum_action());double brake_lowerbound = std::max(vehicle_param_.brake_deadzone(),lon_based_pidcontroller_conf_.brake_minimum_action());double calibration_value = 0.0;//要用来查表加速度,若R档为加速度控制指令取反,非R档保持加速度控制指令double acceleration_lookup = (chassis->gear_location() == canbus::Chassis::GEAR_REVERSE)? -acceleration_cmd: acceleration_cmd;double acceleration_lookup_limited = vehicle_param_.max_acceleration() +lon_based_pidcontroller_conf_.enable_slope_offset() * debug->slope_offset_compensation();double acceleration_lookup_limit = 0.0;if (lon_based_pidcontroller_conf_.use_acceleration_lookup_limit()) {acceleration_lookup_limit = (acceleration_lookup > acceleration_lookup_limited)? acceleration_lookup_limited: acceleration_lookup;}//是否用预览点速度来查标定表(车速-加速度-控制指令百分数)if (FLAGS_use_preview_speed_for_table) {if (lon_based_pidcontroller_conf_.use_acceleration_lookup_limit()) {calibration_value = control_interpolation_->Interpolate(std::make_pair(std::fabs(debug->preview_speed_reference()),acceleration_lookup_limit));} else {// 用chassis里反馈的实际速度 加速度 根据标定表线性插值得到控制量百分数calibration_valuecalibration_value = control_interpolation_->Interpolate(std::make_pair(std::fabs(debug->preview_speed_reference()), acceleration_lookup));}} else {if (lon_based_pidcontroller_conf_.use_acceleration_lookup_limit()) {calibration_value = control_interpolation_->Interpolate(std::make_pair(std::fabs(injector_->vehicle_state()->linear_velocity()),acceleration_lookup_limit));} else {calibration_value = control_interpolation_->Interpolate(std::make_pair(std::fabs(injector_->vehicle_state()->linear_velocity()),acceleration_lookup));}}// 油门/刹车命令处理if (acceleration_lookup >= 0) {if (calibration_value >= 0) {//设置油门控制百分数,为油门下边界和查表得到的控制百分数之间的较大值throttle_cmd = std::max(calibration_value, throttle_lowerbound);} else {throttle_cmd = throttle_lowerbound;}brake_cmd = 0.0;} else {throttle_cmd = 0.0;if (calibration_value >= 0) {brake_cmd = brake_lowerbound;} else {brake_cmd = std::max(-calibration_value, brake_lowerbound);}}...
}