EGOSwarm—Prometheus代码阅读笔记【三】——EGOReplanFSM类实现

文章详细介绍了Prometheusv2中的EGOSwarm模块中EGOReplanFSM类的代码实现,包括状态机转移逻辑、类的功能、成员变量和成员函数。类主要负责无人机的轨迹规划、重规划和执行,同时处理碰撞检测和紧急停止情况。通过对不同回调函数的描述,展示了状态机如何在不同状态间切换以完成复杂的规划任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Prometheusv2中的EGOSwarm代码阅读笔记,个人学习用。
目前还有部分代码没看明白,欢迎大家交流!
EGOPlanner—Prometheus代码阅读笔记汇总

主要参考资料:
Fast Planner——代码解读参考资料整理
EGO-Swarm代码阅读笔记汇总
代码来源:
https://github.com/ZJU-FAST-Lab/ego-planner
https://github.com/amov-lab/Prometheus



(一)EGOReplanFSM类【状态机转移】

1.源文件位置

ego_planner_swarm\plan_manage\src\ego_replan_fsm.cpp

2.主要功能

  • 订阅:无人机位姿odom_world、编号小于该无人机的轨迹/uav*_planning/swarm_trajs、planning/broadcast_bspline_to_planner
  • 发布:当前无人机的/uav*_planning/swarm_trajs、planning/broadcast_bspline_from_planner、planning/bspline、planning/data_display
  • 在状态间切换,完成初始状态、等待触发、生成轨迹、重规划轨迹、执行轨迹、紧急停止等操作
  • 在全局轨迹上按照局部规划半径得到局部目标

3.成员变量

    enum FSM_EXEC_STATE { //状态机FSM状态
      INIT,			//初始状态
      WAIT_TARGET,  //等待触发
      GEN_NEW_TRAJ, //生成轨迹
      REPLAN_TRAJ,  //重规划轨迹
      EXEC_TRAJ,    //执行轨迹
      EMERGENCY_STOP,//紧急停止
      SEQUENTIAL_START //顺序启动
    };
  
    enum TARGET_TYPE {
      MANUAL_TARGET = 1, //手动选择目标点
      PRESET_TARGET = 2, //预设目标点
      REFENCE_PATH = 3
    };

    /* planning utils */
    EGOPlannerManager::Ptr planner_manager_;	//ego过程管理
    PlanningVisualization::Ptr visualization_;	//可视化
    traj_utils::DataDisp data_disp_;
    traj_utils::MultiBsplines multi_bspline_msgs_buf_; //traj_utils/msgs/MultiBsplines.msg

    /* parameters */
    int target_type_; //飞行模式 1:mannual select;2:hard code 使用给定的全局路标点
    double no_replan_thresh_, replan_thresh_;//目标点与无人机当前位置小于no_replan_thresh_就不需要再规划到这个点的轨迹
    double waypoints_[10][3];	//目标点三轴位置
    int waypoint_num_, wp_id_;  //路标点个数,每架飞机一个终点 
    double planning_horizen_, planning_horizen_time_;//局部规划的范围 
    double emergency_time_; //如果距离碰撞的时间小于该值,立刻切换到停止模式
    bool flag_realworld_experiment_;//是否为真实环境实验
    bool enable_fail_safe_;

    /* planning data */
    bool have_trigger_, have_target_, have_odom_, have_new_target_, have_recv_pre_agent_;
    FSM_EXEC_STATE exec_state_;
    int continously_called_times_{0};

    Eigen::Vector3d odom_pos_, odom_vel_, odom_acc_; // odometry state
    Eigen::Quaterniond odom_orient_;

    Eigen::Vector3d init_pt_, start_pt_, start_vel_, start_acc_, start_yaw_; // start state
    Eigen::Vector3d end_pt_, end_vel_;                                       // goal state
    Eigen::Vector3d local_target_pt_, local_target_vel_;                     // local target state
    std::vector<Eigen::Vector3d> wps_;
    int current_wp_;

    bool flag_escape_emergency_; //如果无人机停止或者得到新的无碰撞路径就设为true代表安全了
 
    /* ROS utils */
    ros::NodeHandle node_;
    ros::Timer exec_timer_, safety_timer_;
    ros::Subscriber waypoint_sub_, odom_sub_, swarm_trajs_sub_, broadcast_bspline_sub_, trigger_sub_;
    ros::Publisher replan_pub_, new_pub_, bspline_pub_, data_disp_pub_, swarm_trajs_pub_, broadcast_bspline_pub_;

4.成员函数

EGOReplanFSM::init

参数初始化
设置/parameters/中的成员变量,修改位置:advanced_param.xml


定时器
exec_timer_:状态机切换,周期0.01秒。回调函数execFSMCallback
safety_timer_:安全检查,周期0.05秒。回调函数checkCollisionCallback


订阅
/odom_world:回调odometryCallback。保存无人机当前里程计信息,包括位置、速度和姿态
/uav*_planning/swarm_trajs:回调swarmTrajsCallback。订阅前一架无人机的MultiBsplines,0号机随便飞
/planning/broadcast_bspline_to_planner:回调BroadcastBsplineCallback


发布
/uav*_planning/swarm_trajs:发布当前无人机的MultiBsplines
/planning/broadcast_bspline_from_planner:
/planning/bspline:
/planning/data_display:


1.手动设置目标模式:TARGET_TYPE::MANUAL_TARGET
订阅/uav*/prometheus/motion_planning/goal:回调waypointCallback
2.预先设置目标模式:TARGET_TYPE::PRESET_TARGET
订阅/uav*/traj_start_trigger启动信号:回调函数triggerCallback。等待里程计信息和启动信号,两者都收到后调用readGivenWps。

EGOReplanFSM::readGivenWps

预设目标模式下:读取waypoints_中的路标点后可视化,调用planNextWaypoint

EGOReplanFSM::planNextWaypoint

调用EGOPlannerManager::planGlobalTraj(输入始末位置、速度和加速度)得到全局多项式轨迹gloabl_traj_
如果规划成功{
则end_pt_=next_wp;
如果当前状态为WAIT_TARGET,则转移到GEN_NEW_TRAJ状态;
否则等待切换到EXEC_TRAJ状态,并执行轨迹,直到不再是EXEC_TRAJ状态后切换到REPLAN_TRAJ状态。
}

EGOReplanFSM::triggerCallback

have_trigger_ = true;
init_pt_ = odom_pos_;

EGOReplanFSM::waypointCallback

初始位置设为当前位置,调用planNextWaypoint规划到msg的路径并执行。

EGOReplanFSM::odometryCallback

保存无人机的位置、速度和方向到odom_pos_、odom_vel_和odom_orient_
have_odom_=true

EGOReplanFSM::BroadcastBsplineCallback

首先判断接收到的轨迹是不是自己的轨迹,或者接收到的轨迹距离发出已经过去超过0.25秒,如果是就结束
如果接收到的无人机轨迹目前位置,距离当前无人机大于规划距离的4/3就结束
保存收到无人机轨迹的控制点、区间间隔、时长、初始位置和时间到planner_manager_->swarm_trajs_buf_[id]
调用EGOPlannerManager::checkCollision检查轨迹是否会和自己小于安全距离,如果小于安全距离,则切换到REPLAN_TRAJ状态

EGOReplanFSM::swarmTrajsCallback

如果没有当前无人机odom 或 接收到的集群轨迹不等于接收到的无人机id+1 或 B样条轨迹的阶数不为3就结束。
对于接收到的每一条无人机的轨迹{
如果该架无人机目前位置与当前无人机的距离大于规划距离的4/3就跳过
保存收到无人机轨迹的控制点、区间间隔、时长、初始位置和初始时间到planner_manager_->swarm_trajs_buf_[id]
调用checkCollision(int drone_id)
}
最后have_recv_pre_agent_=true

EGOReplanFSM::changeFSMExecState

切换到new_state模式
若新状态与当前状态一致,则continously_called_times_++ ,否则continously_called_times_置1

EGOReplanFSM::timesOfConsecutiveStateCalls

返回当前状态被连续切换的次数continously_called_times_

EGOReplanFSM::printFSMExecState

打印当前无人机的exec_state_

EGOReplanFSM::execFSMCallback【重要】

(待补充一个流程图)

进入状态机时会类似于关中断,防止在中断过程中相应其他中断。每秒会判断是否有里程计信息和目标位置。


INIT 如果没程计信息就直接结束,否则切换到WAIT_TARGET
WAIT_TARGET>如果没有目标位置和启动信号就直接结束,否则切换到SEQUENTIAL_START
SEQUENTIAL_START 如果收到了其他无人机的轨迹并且有位姿、目标、启动信号则调用planFromGlobalTraj(10)> 。如果成功,则切换到EXEC_TRAJ 并publishSwarmTrajs(true) ;否则切换到SEQUENTIAL_START 。
GEN_NEW_TRAJ 调用planFromGlobalTraj(10) 。如果成功,则切换到EXEC_TRAJ并publishSwarmTrajs(false),flag_escape_emergency_=true;否则切换到GEN_NEW_TRAJ。
REPLAN_TRAJ 调用planFromCurrentTraj(1) 。如果成功,则切换到EXEC_TRAJ 并publishSwarmTrajs(false);否则切换到REPLAN_TRAJ 。
EXEC_TRAJ 如果是预先设置目标模式并且还有路标点没到并且当前位置与当前路标点的位置小于no_replan_thresh_则调用planNextWaypoint()规划到下一个路标点的轨迹;否则如果局部规划的终点与全局终点接近,并且{当前局部轨迹的执行时间已经超过了局部轨迹预计的执行时间,『则have_target_=have_trigger_=false,『如果是预先设置目标模式则规划到第一个路标点的路径』,切换到WAIT_TARGET模式』},否则如果当前位置与全局目标点距离大于no_replan_thresh_并且当前局部轨迹的执行时间超过了replan_thresh_,则切换到REPLAN_TRAJ;否则如果当前局部轨迹的执行时间超过了replan_thresh_ ,则切换到REPLAN_TRAJ 。
EMERGENCY_STOP 如果flag_escape_emergency_=true,则调用callEmergencyStop(odom_pos_)
在当前位置停下;否则如果enable_fail_safe_=true并且无人机速度小于0.1,则切换到GEN_NEW_TRAJ
。最后flag_escape_emergency_=false 。


发布data_disp_ 。开中断。

EGOReplanFSM::planFromGlobalTraj

设置起始位置速度为当前无人机的位置和速度。如果是新切换到当前状态,则flag_random_poly_init=false;否则为true。调用trial_times 次callReboundReplan(true, flag_random_poly_init) ,如果有一次返回True则返回True,否则返回false。

EGOReplanFSM::planFromCurrentTraj

设置起始位置速度为当前时刻局部轨迹计算出的位置和速度。调用callReboundReplan(false, false) ,如果返回false,再调用callReboundReplan(true, false) ,如果返回false,则调用trial_times 次callReboundReplan(true, true) ,如果全都返回false,则函数返回false;否则函数返回true。

EGOReplanFSM::checkCollisionCallback

首先检查是否丢失位姿,如果丢失,enable_fail_safe_ = false ,状态切换到EMERGENCY_STOP 。
从当前时刻开始,以0.01秒为步长,如果t时刻轨迹上为障碍物(包括与其他无人机距离小于安全距离),如果planFromCurrentTraj() 返回true,则切换到EXEC_TRAJ 执行新的安全路径,并publishSwarmTrajs(false) ;如果返回false,如果将要碰撞的时间与现在的时间小于安全时间emergency_time_ 则切换到EMERGENCY_STOP 紧急停止,否则切换到REPLAN_TRAJ 重新规划轨迹。

EGOReplanFSM::callReboundReplan

调用getLocalTarget() 得到局部轨迹的目标点和到达该点时的速度。调用planner_manager_->reboundReplan,令have_new_target_=false。
如果reboundReplan()返回true,则发布新规划出的局部轨迹,并返回true,否则返回false。

EGOReplanFSM::publishSwarmTrajs

如果startup_pub 为true,multi_bspline_msgs_buf_.drone_id_from设为当前无人机。如果multi_bspline_msgs_buf_中轨迹的数量与当前无人机id+1一样,则修改最后一个轨迹为当前无人机此刻的局部B样条轨迹;如果multi_bspline_msgs_buf_中轨迹的数量与当前无人机id一样,则压入当前无人机此刻的局部B样条轨迹。最后发布multi_bspline_msgs_buf_和当前无人机此刻的局部B样条轨迹。
如果startup_pub为false,则只发布当前无人机此刻的局部B样条轨迹。

EGOReplanFSM::callEmergencyStop

规划出到stop_pos 的B样条路径,并用bspline_pub_ 发布

EGOReplanFSM::getLocalTarget

在全局轨迹planner_manager_->global_data_ 找到距离当前无人机位置planning_horizen_处的点作为局部轨迹的目标点,并将全局轨迹上离当前位置最近的点对应的时间赋值给planner_manager_->global_data_.last_progress_time_。
如果局部轨迹的目标点对应的时间大于了全局轨迹的时长那么将全局终点作为局部轨迹的目标点。如果全局终点到局部轨迹的目标点的距离小于无人机以做大速度和加速度可飞行的距离,则局部轨迹的目标点速度设为0;否则局部轨迹的目标点速度为全局轨迹在该点的速度。

<think>嗯,用户又遇到了新的错误。之前的错误是找不到`map_dfr`函数,现在加载了`purrr`包之后,又出现了关于`group_by(time)`找不到`time`列的问题。我需要仔细分析这个错误的原因。 首先,用户提供的代码中,置换函数里使用了`group_by(time)`,但错误提示说找不到`time`列。这说明在数据框`data`中确实没有名为`time`的列,或者在函数内部的数据处理过程中这个列被改名或删除了。可能的情况有几个: 1. **列名拼写错误**:可能用户的数据中列名是`Time`(首字母大写)或者其他变体,而函数中使用的是小写的`time`。R是区分大小写的,所以这会导致错误。 2. **数据中确实没有该列**:可能在传入的数据`data`中没有`time`这一列,或者列名被错误地指定了。例如,用户可能在数据准备阶段将时间列命名为`date`或其他名称,但在函数中仍使用`time`作为参数。 3. **作用域问题**:可能在函数内部,由于某些操作(如重命名、选择子集等),`time`列被意外删除或改名。例如,使用`dplyr::select()`时可能排除了`time`列。 接下来,我需要考虑用户可能的代码上下文。在`augsynth`函数调用中,用户指定了`unit = unit`和`time = time`,这意味着`augsynth`期望数据中存在`unit`和`time`列。如果这些列不存在,就会导致错误。 另外,用户在之前的代码中可能已经正确运行过`augsynth`,所以可能数据中确实存在这些列,但在置换函数内部的处理中,某些操作导致列名被改变。例如,可能在`mutate(treated = sample(treated))`之后使用了`select`或其他操作,导致`time`列被排除。 还需要检查用户是否在函数外部对数据进行了处理,例如重命名列,但在函数内部未做相应调整。例如,数据中的时间列可能被命名为`year`,但函数中仍使用`time`作为参数。 此外,用户可能在之前的步骤中使用了不同的变量名,例如将时间列命名为`period`,但在函数调用时没有正确传递参数。 可能的解决方案包括: 1. **检查数据中的列名**:确保数据中存在`time`列,并且拼写正确。可以使用`names(data)`来确认列名。 2. **修改函数中的列名引用**:如果数据中的列名不同,需要将`group_by(time)`和`augsynth`参数中的`time = time`调整为实际的列名,例如`group_by(period)`和`time = period`。 3. **确保数据传递正确**:确认在调用`manual_placebo(data, 100)`时,传入的`data`确实包含所需的列。 另外,用户可能在之前的代码中将`time`作为变量名,但在数据中使用了不同的名称,或者在数据处理过程中被覆盖。 还需要检查`augsynth`函数的参数是否正确,例如是否将`time`参数正确指定为数据中的列名。 可能用户在之前的步骤中正确运行了`augsynth`,但在置换函数中由于分组操作导致列名被改变,或者数据被过滤导致`time`列丢失。 例如,在`mutate(treated = sample(treated))`之后,如果数据被重新排列或选择,可能会意外删除`time`列。需要确保在数据处理步骤中保留所有必要的列。 另一个可能性是,用户的数据中`time`列是数值型,而分组操作中需要某些处理,但更可能的是列名错误或不存在。 总结,这个错误的核心是数据中缺少`time`列,可能由于列名拼写错误、数据传递错误或数据处理步骤中的列丢失。需要指导用户检查数据中的列名,并调整函数中的列名引用。</think>--- ### **错误诊断升级** 最新错误 `Error in group_by(): Column 'time' is not found` 表示数据中缺失时间变量列。该问题通常由以下原因引发: | 可能原因 | 验证方式 | 典型场景 | |------------------|------------------------------|--------------------------------------------------------------------------| | 列名大小写不一致 | `names(data)` 检查实际列名 | 数据实际列名为 `Time`(首字母大写),而代码中使用小写 `time` | | 数据被意外修改 | 检查 `data` 传入前的处理步骤 | 在调用函数前对 `data` 进行了 `select(-time)` 等操作 | | 模型参数传递错误 | 核对 `augsynth` 参数定义 | 实际时间变量列名为 `year`,但函数中仍使用 `time` 作为参数名 | --- ### **分步解决方案** #### 1. 验证数据列名 在调用函数前检查数据框结构: ```r # 检查数据列名 names(data) # 预期输出应包含:["PM2.5", "treated", "unit", "time"](或对应实际列名) # 如果列名不符,修正数据 data <- data %>% rename(time = your_actual_time_col, # 如实际列名为 date/week/year unit = your_actual_unit_col) ``` #### 2. 修正函数内列引用 修改 `manual_placebo` 函数,确保列名与实际数据匹配: ```r manual_placebo <- function(data, n_permute = 100) { map_dfr(1:n_permute, ~{ data_permuted <- data %>% # 明确指定分组所需的关键列 mutate( treated = sample(treated), time = as.numeric(time), # 强制转换为数值型(如果原为字符) unit = as.character(unit) # 确保单位标识为字符型 ) %>% group_by(time) %>% # 确认此处 time 列存在 mutate(treated = sample(treated)) %>% ungroup() # 显式声明 augsynth 参数 augsynth( form = PM2.5 ~ treated, unit = as.name("unit"), # 使用字符串匹配列名更安全 time = as.name("time"), data = data_permuted ) %>% summary() %>% .$att %>% mutate(permutation = .x) }) } ``` #### 3. 增强代码鲁棒性 在函数开头添加防御性编程检查: ```r manual_placebo <- function(data, n_permute = 100) { # 关键列存在性检查 required_cols <- c("PM2.5", "treated", "unit", "time") if (!all(required_cols %in% names(data))) { missing_cols <- setdiff(required_cols, names(data)) stop(paste("缺失必要列:", paste(missing_cols, collapse = ", "))) } # 后续代码保持不变... } ``` --- ### **关键验证步骤** #### 1. 最小化测试案例 生成符合要求的数据结构进行验证: ```r set.seed(123) test_data <- tibble( PM2.5 = rnorm(1000), treated = sample(c(0,1), 1000, replace = TRUE), unit = rep(1:10, each = 100), time = rep(1:100, times = 10) # 确保列名与代码完全一致 ) test_result <- manual_placebo(test_data, n_permute = 2) ``` #### 2. 数据流追踪 在函数内部添加调试输出: ```r data_permuted <- data %>% mutate(treated = sample(treated)) %>% group_by(time) # 添加调试语句 # 打印当前数据列名和分组状态 print("当前数据列名:") print(names(data_permuted)) print("分组结构:") print(group_vars(data_permuted)) ``` --- ### **技术细节优化** #### 1. 时间变量处理建议 | 场景 | 处理方案 | |--------------------------|--------------------------------------------------------------------------| | 时间列为字符型 | `mutate(time = as.numeric(time))` 转换为数值 | | 存在缺失时间 | `complete(time = full_seq(time, period = 1))` 补全缺失时间 | | 面板数据不平衡 | 使用 `tsibble::fill_gaps()` 处理 | #### 2. 并行计算强化 使用 `furrr` 时增加错误重试机制: ```r future_map_dfr(1:n_permute, ~{ # 原有代码 }, .options = furrr_options( seed = TRUE, scheduling = 2, # 动态任务分配 chunk_size = 10 # 减少内存峰值 ), .progress = TRUE, # 显示进度条 .packages = c("dplyr", "augsynth") # 显式声明依赖包 ) ``` --- ### **预期修正结果** 成功运行后应输出: ``` # A tibble: 100 × 5 Time Estimate Lower_bound Upper_bound permutation <dbl> <dbl> <dbl> <dbl> <int> 1 1 -0.123 -0.456 0.210 1 2 2 0.345 0.102 0.588 1 ... ``` 若仍存在问题,请提供以下信息辅助诊断: 1. 执行 `dput(head(data))` 的输出 2. `sessionInfo()` 的完整结果 3. 数据预处理阶段的完整代码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值