FTC局部路径规划代码分析

2023-10-18 16:44

本文主要是介绍FTC局部路径规划代码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前置知识:

costmap_2d::Costmap2DROS costmap;

costmap_2d::Costmap2DROS 是一个ROS包中提供的用于处理2D成本地图的类。它是一个高级的接口,通常用于与ROS导航栈中的导航规划器和本地路径跟踪器等模块进行集成。

costmap 是一个指向 Costmap2DROS 对象的指针。通常,通过这个指针,你可以访问与导航和路径跟踪相关的成本地图信息,以及执行一些与导航相关的操作,如查询障碍物信息、获取机器人的当前全局位置等。这个指针指向了整个ROS导航栈中的成本地图管理器。

costmap_2d::Costmap2D costmap_map_;

costmap_2d::Costmap2D 是一个用于表示二维成本地图的类,通常用于底层的地图管理和处理。

costmap_map_ 是一个指向 Costmap2D 对象的指针。这个指针通常用于直接访问成本地图的数据,如地图的大小、分辨率、栅格信息以及每个栅格的成本值等。这个指针通常用于执行与成本地图的低级操作,如路径规划和避障算法。

costmap->getOrientedFootprint(std::cector<geometry_msgs::Point>& oriented_footprint)

构建机器人在当前姿态下的足迹,结果将存储在参数 oriented_footprint 中, 这个函数可以返回机器人当前位置的足迹, 获取的是一个机器人在世界坐标系下的坐标, 通常用于碰撞检测或生成局部代价地图

costmap->getRobotFootprint()

获取机器人的轮廓, 表现为一系列std::vector<geometry_msgs::Point>的形式

costmap->getRobotFootprintPolygon()

获取机器人的轮廓, 表现为一个凸多边形, geometry_msgs::Polygon 类型

initialize(std::string name, tf2_ros::Buffer* tf, costmap_2d::Costmap2DROS* costmap_ros)

参数:

name

tf

costmap_ros 一个ROS的上层接口

发布者:

global_plan_pub : 将global_planner中发过来的路径不经过任何处理发布到”/ftc_local_planner/global_plan”

global_point_pub:

obstacle_marker_pub

setPlan(const std::vector<geometry_msgs::PoseStamped> &plan)

参数:

plan 包含位姿信息的向量, 从起始点指向结束点

逻辑:

  1. 定义当前状态 PRE_ROTATE
  2. 复制plan到global_plan
  3. 如果规划中大于两个点(路径规划存在), 则将倒数第二个点的朝向改为倒数第三个点的朝向 global_plan[global_plan.size() - 2].pose.orientation = global_plan[global_plan.size() - 3].pose.orientation;
  4. 创建导航路径 nav_msgs::Path path;
  5. 将收到的路径plan原封不动放到path.poses中
  6. 发布path到”/ftc_local_planner/global_plan”

distanceLookahead() #未知

double FTCPlannerROS::distanceLookahead(){if (global_plan.size() < 2){return 0;}Eigen::Quaternion<double> current_rot(current_control_point.linear());Eigen::Affine3d last_straight_point = current_control_point;for (uint32_t i = current_index + 1; i < global_plan.size(); i++){tf2::fromMsg(global_plan[i].pose, last_straight_point);// check, if direction is the same. if so, we add the distanceEigen::Quaternion<double> rot2(last_straight_point.linear());if (abs(rot2.angularDistance(current_rot)) > config.speed_fast_threshold_angle * (M_PI / 180.0)){break;}}return (last_straight_point.translation() - current_control_point.translation()).norm();}

void FTCPlannerROS::update_control_point(double dt)

作用:

根据当前状态确定执行内容

状态: PRE_ROTATE, FOLLOWING, POST_ROTATE, WAITTING_FOR_GOAL_APPROACH, FINISHED

参数:

dt: 当前时间 - 上次执行的时间

逻辑: current_control_point: map坐标系下的点

local_control_point: base_link坐标系下的点

  1. 判断当前执行状态

    1. PRE_ROTATE
      1. 将global_plan[0].pose存入current_control_point
    2. FOLLOWING
      1. 计算前视距离 straight_dist=distanceLookahead()
      2. 如果前视距离大于满速行进的阈值, 则全速前进
      3. 若目标速度大于当前速度, 则根据配置的加速度对当前速度进行提升
      4. 如果: 需要移动的距离大于零, 同时需要转动的角度大于零且当前点不为全局路径规划的倒数第二个点, 进入循环
        1. 从全局规划中获取下两个点, currentPose以及nextPose
        2. 计算两个点的距离 pose_distance
        3. 如果两个点之间的距离小于等于0, 则警告: 跳过重复点(Skipping duplicate point in global plan)
        4. 通过current_progress(0-1之间的数), 计算剩余距离与角度
        5. 如果剩余的距离和角度可以在当前时间步内到达(remaining_distance_to_next_pose < distance_to_move*并且*remaining_angular_distance_to_next_pose < angle_to_move),则将机器人移动到下一个位置,更新**current_progress,减少distance_to_moveangle_to_move**
        6. 如果无法在当前时间步内到达下一个位置,则更新**current_progress**以确保机器人在下一个时间步内能够到达下一个位置
      5. 最后,通过插值计算机器人在当前时间步的控制点位置和方向,然后将其存储在**current_control_point**中,以供后续控制使用
    3. POST_ROTATE
      1. 将global_plan中的最后一个点保存到current_control_point中
    4. WAITTING_FOR_GOAL_APPROACH
      1. break
    5. FINISHED
      1. break
  2. 创建目标点, viz用于发布current_control_point的坐标

  3. 通过tf::doTransform(currnet_control_point, local_control_point, map_to_base)转化current_control_point的坐标到 local_control_point. 注: current_control_point为基于map的全局坐标, 而local_control_point通过下面 两行, 将map全局坐标系下的点, 转化为从base_link到map的局部变换的点

    auto map_to_base = tf_buffer->lookupTransform("base_link", "map", ros::Time(), ros::Duration(1.0));
    tf2::doTransform(current_control_point, local_control_point, map_to_base);
    
  4. 计算误差 lat_error(横向误差), lon_error(纵向误差), angle_error(角度误差)

update_planner_state()

逻辑:

  1. 根据当前状态更新下一个状态
    1. PRE_ROTATE
      1. 判断setPlan的时间是否超时, 是, ERROR: Timeout in PRE_ROTATE phase; is_crashed=true; return FINISHED;
      2. 判断angle_error是否小于max_goal_angle_error, 是, INFO: FTCLocalPlannerROS: PRE_ROTATE finished return FOLLOWING
      3. break
    2. FOLLOWING
      1. 计算distance = local_control_point.translation().norm()
      2. 判断distance是否大于最大追踪距离max_follow_distance 是, ERROR: FTCLocalPlannerROS: Robot is far away from global plan. distance is_crashed=true return FINISHED
      3. 判断current_index是否为倒数第二个点 是, INFO: FTCLocalPlannerROS: switching planner to position mode return WAITING_FOR_GOAL_APPROACH
      4. break
    3. WAITING_FOR_GOAL_APPEOACH
      1. 计算distance = local_control_point.translation().norm()
      2. 判断是否超时 是, WARN: FTCLocalPlannerROS: Could not reach goal position return POST_ROTATE
      3. 判断距离是否小于max_goal_distance_error 是, INFO: FTCLocalPlannerROS: Reached goal position. return POST_ROTATE
      4. break
    4. POST_ROTATE
      1. 判断是否超时 是, WARN: FTCLocalPlannerROS: Could not reach goal rotation return FINISHED
      2. 判断角度误差是否小于max_goal_angle_error 是, INFO: FTCLocalPlannerROS: POST_ROTATE finished. return FINISHED
      3. break
    5. FINISHED
      1. break
  2. return current_state

bool checkCollision(int max_points)

参数:

max_points 向前预判的点的个数, 根据实际global_plan中每一个点之间的距离设置max_points

例如: 机器人当前点为 global_plan[current_index], 而max_points则负责检测从global_plan[current_index]global_plan[current_index + max_points]这段路径中间是否有障碍物, 有则为true

逻辑:

  1. 判断是否开启了障碍检测
  2. 如果global_plan.size() < max_points 则将最大值设为路径中点的总数
  3. 判断是否开启了obstacle_footprint 使用实际足迹检测碰撞
    1. costmap->getOrientedFootprint(footprint);获取当前机器人足迹, 保存到footprint
    2. 遍历footprint, 将每一个点从world转换到map
    3. 获取每一个点的代价
    4. 判断当前代价是否大于既定的致命值costs >= costmap_2d::LETHAL_OBSTACLE
    5. 是, WARN: FTCLocalPlannerROS: Possible collision of footprint at actual pose. Stop local planner. return true
  4. for (int i = 0; i < max_points; i++) 判断前视距离是否有障碍物
    1. 获取current_index + i 点的坐标
    2. 将该坐标转换到map坐标系
    3. 获取该坐标的代价值
    4. 判断 costs >127 && costs > previous_cost 是, WARN(FTCLocalPlannerROS: Possible collision. Stop local planner.) return true
    5. 更新previous_cost为costs
    6. 开始下个循环
  5. return false

void calculate_velocity_commands(double dt, geometry_msgs::Twist &cmd_vel)

参数:

dt 现在时间与上次执行时间之差

cmd_vel 速度置零

逻辑:

  1. 判断current_state == FINISHED || is_crashed 速度置零, 返回
  2. 计算PID参数
  3. 判断current_state==FOLLOWING
    1. 通过PID计算线速度
    2. 如果线速度 < 0 且 forward_only 为true 速度归零
    3. 限制线速度在 -max_cmd_vel_speed 到 max_cmd_vel_speed之间
    4. cmd_vel.linear.x = lin_speed
  4. 角速度的运算分两部分, current_state == FOLLOWING以及current_state == PRE_ROTATE
    1. 判断current_state==FOLLOWING
      1. 通过PID计算角速度
      2. 限制角速度
      3. cmd_vel.angular.z = ang_speed
    2. 否则(也就是机器人处于PRE_ROTATE阶段)
      1. 通过PID计算角速度
      2. 限制角速度
      3. cmd_vel.angular.z = ang_speed
      4. 震荡检测, 如果震荡, 则角速度拉满

bool computeVelocityCommands(geometry_msgs::Twist &cmd_vel)

参数:

cmd_vel 速度

逻辑:

  1. 计算dt 执行该代码时的时间 - 上次执行改代码的时间
  2. 判断是否碰撞(is_crashed) 是, 速度置零, return false
  3. 判断是否完成(current_state==FINISHED), 是, 速度置零, return true
  4. 根据dt, 更新控制点 update_control_point(dt)
  5. 更新当前状态 auto new_planner_state = update_planner_state()
  6. 判断: 新状态与当前状态是否一致 是, INFO: FTCLocalPlannerROS: Switching to state 更新state_entered_time (用于判断一个状态是否会超时) 更新当前状态为新状态
  7. 判断是否存在碰撞 checkCollision(obstacle_lookahead) 是, 速度置零 is_crashed = true return false
  8. 计算速度 calculate_velocity_commands(dt, cmd_vel)

这篇关于FTC局部路径规划代码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/233863

相关文章

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使