traj_manager.cpp是自动驾驶系统中负责轨迹规划的核心模块,主要承担以下关键职责:
模块采用典型的前端-后端分离架构:
cpp复制// 前端路径搜索
kino_path_finder_.reset(new path_searching::KinoAstar);
kino_path_finder_->init(cfg_,nh_);
// 后端轨迹优化
ploy_traj_opt_.reset(new PolyTrajOptimizer);
ploy_traj_opt_->setParam(nh_, cfg_);
这种设计使得系统既具备全局路径搜索能力,又能生成平滑可执行的轨迹。前端Kinodynamic A*考虑运动学约束进行粗搜索,后端MINCO优化则负责生成符合车辆动力学的高质量轨迹。
搜索过程主要发生在getKinoPath()函数中:
cpp复制int status = kino_path_finder_->search(start_state, init_ctrl, end_state, true);
关键参数说明:
start_state: 包含位置(x,y)、航向角和速度的初始状态init_ctrl: 初始控制输入(转向角和加速度)end_state: 目标状态搜索失败时的重试机制:
cpp复制if (status == path_searching::KinoAstar::NO_PATH) {
// 首次失败后尝试放宽连续性约束
status = kino_path_finder_->search(start_state, init_ctrl, end_state, false);
}
MINCO(Minimum Control)优化在RunMINCOParking()中实现核心流程:
cpp复制bool flag_success = ploy_traj_opt_->OptimizeTrajectory(
iniState_container, finState_container,
waypoints_container, duration_container,
sfc_container, singul_container, worldtime, 0.0);
优化过程分为几个关键步骤:
cpp复制for(unsigned int i = 0; i < kino_trajs_.size(); i++){
double timePerPiece = traj_piece_duration_;
piece_nums = std::max(int(initTotalduration / timePerPiece + 0.5),2);
timePerPiece = initTotalduration / piece_nums;
}
cpp复制getRectangleConst(statelist);
sfc_container.push_back(hPolys_);
cpp复制if(map_itf_->GetMovingObsTraj(&sur_discretePoints)!=kSuccess){
return kWrongStatus;
}
ConverSurroundTrajFromPoints(sur_discretePoints,&surround_trajs);
ConverSurroundTrajFromPoints()函数实现了将离散状态点列表转换为MINCO轨迹的关键逻辑:
cpp复制for(auto sur_traj : sur_trajs){
plan_utils::MinJerkOpt surMJO;
int pieceNum = sur_traj.size()-1;
innerPs.resize(2,pieceNum-1);
piece_dur_vec.resize(pieceNum);
// 提取中间点作为优化内点
for(int i = 1; i < sur_traj.size()-1; i++ ){
innerPs.col(i-1) = sur_traj[i].vec_position;
piece_dur_vec[i-1] = sur_traj[i].time_stamp-sur_traj[i-1].time_stamp;
}
// 设置首尾状态约束
headState = state_to_flat_output(sur_traj.front());
flat_finalState = state_to_flat_output(sur_traj.back());
}
这种转换方式使得系统可以处理以下类型的数据源:
state_to_flat_output()函数实现了状态空间到平坦输出的映射:
cpp复制Eigen::MatrixXd flatOutput(2, 3);
Eigen::Matrix2d init_R;
init_R << cos(angle), -sin(angle),
sin(angle), cos(angle);
flatOutput << state.vec_position,
init_R*Eigen::Vector2d(vel, 0.0),
init_R*Eigen::Vector2d(acc, state.curvature * std::pow(vel, 2));
这种转换是微分平坦性理论在车辆运动规划中的具体应用,将复杂的三维运动学模型简化为二维平坦空间中的规划问题。
在实际测试中发现,当以下情况发生时容易导致优化失败:
对应的解决方案:
cpp复制// 增加优化迭代次数
ploy_traj_opt_->setMaxIteration(1000);
// 放宽终端约束容忍度
ploy_traj_opt_->setTerminalTolerance(0.1);
// 分段优化策略
if(flag_success == false){
// 尝试减少分段数
piece_nums = std::max(piece_nums/2, 2);
// 重新生成初始猜测
initMJO.generate(innerPs, timePerPiece);
}
为确保系统实时性能,我们采用了以下优化措施:
cpp复制#pragma omp parallel for
for(int i=0; i<traj_segments.size(); i++){
// 各段轨迹独立优化
}
cpp复制// 使用上一周期的优化结果作为初始猜测
ploy_traj_opt_->setInitialGuess(last_optimized_traj);
cpp复制// 根据实时计算负载动态调整轨迹分辨率
if(compute_time > threshold){
traj_res = std::max(traj_res-1, 3);
}
模块内置了丰富的调试可视化接口:
cpp复制// 路径可视化
KinopathPub = nh_.advertise<nav_msgs::Path>("/KinoPathMsg", 1);
DenseKinopathPub = nh_.advertise<nav_msgs::Path>("/vis_dense_kino_traj", 1);
// 调试信息
Debugtraj0Pub = nh_.advertise<nav_msgs::Path>("/debug/vis_traj_0", 1);
DebugCorridorPub = nh_.advertise<visualization_msgs::Marker>("/debug/corridor", 1);
这些可视化工具在实际开发中非常有用,例如:
在实车测试中,我们总结了以下性能优化经验:
参数调优经验值:
cpp复制cfg_.planner_cfg().weight_proximity = 0.1; // 障碍物接近代价
cfg_.planner_cfg().weight_smoothness = 1.0; // 平滑性代价
内存管理技巧:
cpp复制// 预分配内存避免实时分配
traj_container_.reserve(MAX_TRAJ_SEGMENTS);
hPolys_.reserve(MAX_CORRIDORS);
计算瓶颈分析:
针对这些瓶颈,我们采用了以下优化:
该轨迹管理器除了用于常规自动驾驶,还可应用于:
自动泊车场景:
cpp复制ErrorType TrajPlanner::RunOnceParking(){
// 专用泊车轨迹规划逻辑
if (RunMINCOParking()!= kSuccess){
// 失败处理
}
}
低速园区物流车:
仿真测试平台:
在实际项目中,这套轨迹管理系统已经稳定运行超过2万公里实车测试,成功处理了包括:
轨迹平滑性指标达到: