TEB算法详解(TebLocalPlannerROS::computeVelocityCommands(3))

2023-05-16

第一章主要分析了teb算法的准备条件,包括获取当前位姿与速度、对全局路径的裁减以获取局部路径等以及局部地图的获取等。第二章主要讲述了在获取前置条件后,如何根据前置条件进行位姿优化,teb的路径优化主要是调用了g2o优化算法,以全局路径的点以及理论上点与点之间的运动时间作为g2o的优化顶点,以距离障碍物的距离、机器人最大运动速度、点与点之间的最短路径等约束条件作为g2o的边对局部路径规划的姿态进行了优化。这一章则是主要看一下算法怎么从优化后的点位到运动输出之间的关系。

假设算法已经获得了g2o优化后的结果,则算法在发布速度前会经历以下几步:

1、判断结果是否发散

//这里似乎对最后一个点进行了评价,如果最后优化的结果超过最大可接受Mahalanobis距离则返回true,说明优化结果发散。
  if (planner_->hasDiverged())
  {
    cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;

    // Reset everything to start again with the initialization of new trajectories.
    // 重置所有变量,再次开始新轨迹的初始化
    planner_->clearPlanner();
    ROS_WARN_THROTTLE(1.0, "TebLocalPlannerROS: the trajectory has diverged. Resetting planner...");

    ++no_infeasible_plans_; // increase number of infeasible solutions in a row
    time_last_infeasible_plan_ = ros::Time::now();
    last_cmd_ = cmd_vel.twist;
    return mbf_msgs::ExePathResult::NO_VALID_CMD;
  }

在获取g2o优化的位姿结果后,算法第一步是对结果进行了收敛性判断。判断的依据应该是:判断局部路径规划的最后一个点的优化前后的曼哈顿距离是否在满足的阈值内,如果不满足则说明优化的结果发散了。

2、是否动态更新footprint

	// Check feasibility (but within the first few states only)检查路径是否可行
  //动态更新footprint_spec_,如果为true,则在检查轨迹可行性之前更新足迹
  if(cfg_.robot.is_footprint_dynamic)
  {
    // Update footprint of the robot and minimum and maximum distance from the center of the robot to its footprint vertices.
    // 更新机器人的足迹以及从机器人中心到足迹顶点的最小和最大距离,这个默认为false,好像是跟机器人模型相关?
    // teb初始化的时候执行过一次getRobotFootprint以及calculateMinAndMaxDistances,如果模型没有变化这里应该是不用重新计算的。
    footprint_spec_ = costmap_ros_->getRobotFootprint();
    //calculateMinAndMaxDistances函数对车体尺寸进行建模,对不同形状的车体进行外壳的位置计算。
    costmap_2d::calculateMinAndMaxDistances(footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius);
  }

这个过程是由参数cfg_.robot.is_footprint_dynamic决定的,机器人的footprint也就是机器人的模型一般来说是固定的,footprint的获取会在teb的初始化函数中获取一次,如果不改变的话后面算法会一直使用这个footprint,但是如果说机器人的模型会动态变化的话这会在这个位置进行一次更新。

3、轨迹可行性判读

bool feasible = planner_->isTrajectoryFeasible(costmap_model_.get(), footprint_spec_, robot_inscribed_radius_, robot_circumscribed_radius, cfg_.trajectory.feasibility_check_no_poses);

前面全局路径规划得到一条路径,这条路径是确认可以到达终点的,但是因为这里的局部路径规划优化后的轨迹会与全局路径规划有所区别,同时还有上面的可能存在的机器人模型的变化,所以对于优化后的轨迹我们需要重新判断它的可行性。

if (i<look_ahead_idx)
    {//查看两个点之间的角度以及距离
      double delta_rot = g2o::normalize_theta(g2o::normalize_theta(teb().Pose(i+1).theta()) -
                                              g2o::normalize_theta(teb().Pose(i).theta()));
      Eigen::Vector2d delta_dist = teb().Pose(i+1).position()-teb().Pose(i).position();
      //如果两个点之间的角度差超过阈值,或者点与点之间的距离超过一个机器人的身位(说明只遍历点是无法确认路径是否有效的,中间有盲区)
      if(fabs(delta_rot) > cfg_->trajectory.min_resolution_collision_check_angular || delta_dist.norm() > inscribed_radius)
      {
        //计算需要插入几个点
        int n_additional_samples = std::max(std::ceil(fabs(delta_rot) / cfg_->trajectory.min_resolution_collision_check_angular), 
                                            std::ceil(delta_dist.norm() / inscribed_radius)) - 1;
        PoseSE2 intermediate_pose = teb().Pose(i);
        //计算每个中间点的坐标
        for(int step = 0; step < n_additional_samples; ++step)
        {
          intermediate_pose.position() = intermediate_pose.position() + delta_dist / (n_additional_samples + 1.0);
          intermediate_pose.theta() = g2o::normalize_theta(intermediate_pose.theta() + 
                                                           delta_rot / (n_additional_samples + 1.0));
          //验证每个坐标点上的机器人模型是否会与障碍物重合
          if ( costmap_model->footprintCost(intermediate_pose.x(), intermediate_pose.y(), intermediate_pose.theta(),
            footprint_spec, inscribed_radius, circumscribed_radius) == -1 )
          {
            if (visualization_) 
            {
              visualization_->publishInfeasibleRobotPose(intermediate_pose, *robot_model_);
            }
            return false;
          }
        }
      }
    }

判断的方式也很简单:

首先对于局部路径规划中的每个点,将机器人模型投影到这个点上去判断这个点是否会跟障碍物i模型相重合,如果存在重合就说明这条路径有问题需要重新规划。

其次,判断局部路径上每个点之间的距离以及朝向角度差,这个距离不能超过机器人的长度,如果超过了机器人的长度则点上的模型不能完全覆盖机器人路径,可能会存在说两个姿态点都是没有问题的但是点与点之间存在障碍物的问题,这样仅仅对点进行判断是不能完全保证路径可行的。对于这个问题的处理方式也很简单,对路径按照机器人长度进行差值,直到模型覆盖整条路径为止。角度的问题也是一样的。

4、计算速度

//teb的速度比较简单,就是受cfg_.trajectory.control_look_ahead_poses影响,取向前多少个位姿点的姿态。然后用当前点的位姿与目标点做差
  //速度的话就是两点间距离/前面计算过的点与点之间理想时间和
  //在前面运动优化部分有计算过每个位姿之间的理论运动时间。例如这里如果要取前向第五个点,那么时间就是前面计算过的点1-2-3-4-5的时间和,距离就是点1与点5的距离差
  //角度的话也是简单明了的两点之间角度差/时间
  if (!planner_->getVelocityCommand(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z, cfg_.trajectory.control_look_ahead_poses))
  {
    planner_->clearPlanner();
    ROS_WARN("TebLocalPlannerROS: velocity command invalid. Resetting planner...");
    ++no_infeasible_plans_; // increase number of infeasible solutions in a row
    time_last_infeasible_plan_ = ros::Time::now();
    last_cmd_ = cmd_vel.twist;
    message = "teb_local_planner velocity command invalid";
    return mbf_msgs::ExePathResult::NO_VALID_CMD;
  }

重头戏!!!!!teb的用处是什么???不就是为了求机器人的速度吗?弯弯绕绕走了这么久终于算速度了。点进去看一下。

emmm,其实还是比较简单的哈。注意到传入了一个参数:cfg_.trajectory.control_look_ahead_poses。这个参数是用来判断机器人使用前向第几个姿态点作为速度计算的终点。这里有个细节,就是对于靠近终点附近的点位,剩余点的数量可能会少于这个参数,那就直接用终点了。然后我们就有了一个起点一个终点。要怎么计算运动过去的速度呢?往下看:

 //计算两个点的距离,得到x、y两个方向的距离
  Eigen::Vector2d deltaS = pose2.position() - pose1.position();
  //差速轮,没有y方向的速度
  if (cfg_->robot.max_vel_y == 0) // nonholonomic robot
  {
    //
    Eigen::Vector2d conf1dir( cos(pose1.theta()), sin(pose1.theta()) );
    // translational velocity
    //这边是在求方向?
    double dir = deltaS.dot(conf1dir);
    //deltaS.norm()返回的是斜边长度,也就是两点之间的距离
    //g2o::sign(dir)函数,当dir>0返回1,dir<0返回-1
    vx = (double) g2o::sign(dir) * deltaS.norm()/dt;
    vy = 0;
  }
  else // holonomic robot
  {
    // transform pose 2 into the current robot frame (pose1)
    // for velocities only the rotation of the direction vector is necessary.
    // (map->pose1-frame: inverse 2d rotation matrix)
    double cos_theta1 = std::cos(pose1.theta());
    double sin_theta1 = std::sin(pose1.theta());
    double p1_dx =  cos_theta1*deltaS.x() + sin_theta1*deltaS.y();
    double p1_dy = -sin_theta1*deltaS.x() + cos_theta1*deltaS.y();
    vx = p1_dx / dt;
    vy = p1_dy / dt;    
  }
  
  // rotational velocity
  //求旋转的角速度
  double orientdiff = g2o::normalize_theta(pose2.theta() - pose1.theta());
  omega = orientdiff/dt;

首先deltaS 里面存储的是xy两个方向的距离,dir获取了机器人需要的方向,计算方式使用的是xy方向的距离乘以角度的三角函数。g2o::sign(dir)函数对速度进行归一化,当dir>0返回1,dir<0返回-1。然后具体的速度大小则是距离/时间

这里的时间是来自于点与点之间的理论时间和。在前面运动优化部分有计算过每个位姿之间的理论运动时间。例如这里如果要取前向第五个点,那么时间就是前面计算过的点1-2-3-4-5的时间和,距离就是点1与点5的距离差:

for(int counter = 0; counter < look_ahead_poses; ++counter)
  {
    //计算一个运动时间,从当前点到前向多少个点的理论时间和
    dt += teb_.TimeDiff(counter);
    //dt_ref为两个点之间允许的最大时间,如果超过这个时间会进行点的插入
    if(dt >= cfg_->trajectory.dt_ref * look_ahead_poses)  // TODO: change to look-ahead time? Refine trajectory?
    {
        look_ahead_poses = counter + 1;
        break;
    }
  }

同样的,角速度也是两点之间的角度差/时间和。

5、速度限定

  //速度硬约束,保证计算结果不超过设定值
  // Saturate velocity, if the optimization results violates the constraints (could be possible due to soft constraints).
  saturateVelocity(cmd_vel.twist.linear.x, cmd_vel.twist.linear.y, cmd_vel.twist.angular.z,
                   cfg_.robot.max_vel_x, cfg_.robot.max_vel_y, cfg_.robot.max_vel_trans, cfg_.robot.max_vel_theta, 
                   cfg_.robot.max_vel_x_backwards);

这里就是速度进行最后的判断了,上述计算出来的速度是否满足算法设定的最大值,如果超过这个值的话会被限定到范围内。

6、非原地转向车辆的角速度修正

//如果需要,将rot-vel转换为转向角(carlike robot)。min_turning_radius允许稍小,因为它是一个软约束,而不是其他不受penalty_epsilon影响的约束。用户可以为参数本身添加安全裕度。
  if (cfg_.robot.cmd_angle_instead_rotvel)
  {
    cmd_vel.twist.angular.z = convertTransRotVelToSteeringAngle(cmd_vel.twist.linear.x, cmd_vel.twist.angular.z,
                                                                cfg_.robot.wheelbase, 0.95*cfg_.robot.min_turning_radius);
    if (!std::isfinite(cmd_vel.twist.angular.z))
    {
      cmd_vel.twist.linear.x = cmd_vel.twist.linear.y = cmd_vel.twist.angular.z = 0;
      last_cmd_ = cmd_vel.twist;
      planner_->clearPlanner();
      ROS_WARN("TebLocalPlannerROS: Resulting steering angle is not finite. Resetting planner...");
      ++no_infeasible_plans_; // increase number of infeasible solutions in a row
      time_last_infeasible_plan_ = ros::Time::now();
      message = "teb_local_planner steering angle is not finite";
      return mbf_msgs::ExePathResult::NO_VALID_CMD;
    }
  }

这个函数是只针对类似于我们小汽车之类的无法原地转向的机器人的,则需要设定最小转弯半径。这里暂时就不展开看了。

7、发布信息

  last_cmd_ = cmd_vel.twist;
  // 可视化障碍物,路过点,全局路径
  // Now visualize everything    
  planner_->visualize();
  visualization_->publishObstacles(obstacles_);
  visualization_->publishViaPoints(via_points_);
  visualization_->publishGlobalPlan(global_plan_);
  return mbf_msgs::ExePathResult::SUCCESS;

最后,算法会发布机器人的速度以及轨迹,到此,算法的整体流程就完成了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TEB算法详解(TebLocalPlannerROS::computeVelocityCommands(3)) 的相关文章

  • 串口接收一帧数据及解析

    3 xff0e 下位机中的数据接收和协议解析 下位机接收数据也有两种方式 xff0c 一 等待接收 xff0c 处理器一直查询串口状态 xff0c 来判断是否接收到数据 二 中断接收 两种方法的优缺点在此前的一篇关于串口通信的文章中详细讨论
  • <string>库常用的函数

    include lt string h gt 库包含字符串处理函数 xff0c 常用的有strcpy strcat strcmp strchr等 1 strcpy是字符串赋值函数 char strcpy char target char s
  • APM(Ardupilot)——电机输出流程图

    电机类声明 xff1a system cpp void Copter allocate motors void switch AP Motors motor frame class g2 frame class get if FRAME C
  • C++中的三个特殊宏:__FILE__,__FUNCTION__和__LINE__

    C 43 43 中的三个特殊宏 xff1a FILE xff0c FUNCTION 和 LINE 1 FILE 宏 FILE 宏用于检查当前文件名 xff0c 例如 xff1a include lt cstdio gt using name
  • GD32F10x外部晶振配置108MHz系统时钟

    嵌入式 GD32F10x外部晶振配置108MHz系统时钟 文章目录 嵌入式 GD32F10x外部晶振配置108MHz系统时钟前言一 时钟树与配置思路二 时钟配置过程三 晶振故障排查总结 前言 由于公司更改硬件设计选择使用新的型号兆易创新国产
  • Keil MDK C (error: #29: expected an expression) 错误的解决

    今天 xff0c 自己建了一个EFM32工程模版 xff0c 调试代码时显示 App Panel main c 119 error 29 expected an expression 仔细的检查了半个小时 xff0c 最后解决了 xff01
  • HTTP协议之报文详解

    学习WEB开发需要对HTTP协议熟悉 xff0c 下面直接进入主题 一 什么是报文 报文 xff0c 是网络中交换和传输的数据单元 xff0c 即站点一次性要发送的数据块 报文包含了将要发送的完整的数据信息 xff0c 其长短很不一致 xf
  • linux发送tcp/udp请求

    本文章介绍下通过nc工具 iperf工具和python脚本 xff0c 实现发送tcp udp请求 一 nc工具 xff08 netcat工具 xff09 这个工具linux系统默认是自带的 xff0c 以下是命令的常用参数 1 1 发送t
  • 第一次网页前端培训笔记(HBuilderX)

    一 安装HBuilderX 官网 xff1a HBuilderX 高效极客技巧 dcloud io 二 了解HBuilderX lt DOCTYPE html gt lt html gt lt head gt lt meta charset
  • 第二次网页前端培训笔记(HBuilderX)

    常用标签 一 表单 form标签 xff1a 表单标签 xff0c 块级元素 xff0c 会自动换行 xff1b 将数据传输给服务器 常用属性 xff1a action 表单提交的地址url id 唯一标识 name 名称 target 表
  • 前端第7次培训

    对象 一对象的创建 1 字面量形式创建对象 var对象名 61 xff1b var对象名 61 键 xff1a 值 2 通过new Object创建 var对象名 61 new Object xff08 xff09 3 通过Object对象
  • MPU6050的再次深度学习与实操遇到问题

    数据的处理与实现 xff1a MPU6050芯片提供的数据夹杂有较严重的噪音 xff0c 在芯片处理静止状态时数据摆动都可能超过2 除了噪音 xff0c 各项数据还会有偏移的现象 xff0c 也就是说数据并不是围绕静止工作点摆动 xff0c
  • moderlarts第一次作业

    一 物体检测模型 以口罩检测为例 1 下载OBS 2 打开华为云 xff0c 找到modelarts控制台 xff0c 鼠标移到用户名上 xff0c 点击我的凭证 3 点击访问密钥 gt 新增访问密钥 xff0c 下载 xff0c 然后登陆
  • 培训modelarts 二

    一 进行声音分类 1 下载数据包 xff0c 导入OBS 2 创建声音分类项目 3 标签分类 4 开始训练 5 部署模型 6 预测结果 二 AI gallery 1 下载数据集 2 创建数据集 xff0c 导入外卖评论 3 进行创建文本分类
  • Jetson Xavier NX 配置(七)—— 数据传输之socket文件传输 & usb摄像头RTSP视频推流

    目录 1 Python socket 文件传输 xff08 1 xff09 发送单个文件 xff08 一次性 xff09 xff08 2 xff09 发送一个文件夹下的所有文件 xff08 一次性 xff09 xff08 3 xff09 发
  • 使用plist文件进行ipa的安装

    前提 将html 用户扫码 地址访问 ipa和plist放在https的远程服务器上 编写html文件 内容如下 lt DOCTYPE html PUBLIC W3C DTD XHTML 1 0 Transitional EN http w
  • 关于U盘变成RAW格式 windows无法格式化的解决方法

    网上有很多人说是U盘坏了 xff0c 其实不是这样 这个问题是可以解决的 xff0c 解决方法也是在网上搜索到的 xff0c 到这里同大家分享下 本人昨天使用U盘的时候就碰到了U盘变成RAW格式 xff0c 系统可以读出盘符 xff0c 但
  • Xcode11.6编写C++项目出现报错:vector or iostream file not found

    解决办法 xff1a
  • iPhone设备型号代码(iPhone 4s - iPhone 14)

    lt string gt iPhone15 3 lt string gt iPhone 14 Pro Max lt string gt iPhone15 2 lt string gt iPhone 14 Pro lt string gt i
  • [Linux] Vim 撤销 回退 操作

    在vi中按u可以撤销一次操作 u 撤销上一步的操作 Ctrl 43 r 恢复上一步被撤销的操作 注意 xff1a 如果你输入 u 两次 xff0c 你的文本恢复原样 xff0c 那应该是你的Vim被配置在Vi兼容模式了 重做 如果你撤销得太

随机推荐

  • 为什么系统进入到Checking Media Presence

    你按联想热启键来开机 xff0c 也就是那个还原键来开机 然后你选择BIOS Setup回车 选择Boot这项的boot mode把UEFI改成legacy support和boot priority把UEFI改成legacy 然后保存退出
  • MacBook Air响一声白屏故障情况说明及解决

    情况说明 xff1a 2013款的MacBook Air安装Windows 7系统 xff0c 结果导致开机响一声就白屏 xff0c 按option无选项出现 xff0c 其他各种组合按键尝试都无效 百度搜索 xff0c 有人说是屏幕故障
  • 如何在Eclipse中打开现有项目(高手免入)

    如果我们现在已经有了Java项目 xff08 网上下载的或者从别的电脑上拷贝过来的 xff09 xff0c 我们都知道 xff0c Eclipse和其他的编程软件不一样 xff0c 不能够通过直接双击某个文件的方式来打开 xff0c 那么我
  • svn is not a working copy 怎么解决

    确定当目录下是否含有 svn文件夹 如果没有就重新啊checkout xff0c 或者在上一层目录或下一层目录查找 xff0c 有则可执行 svn commit m 34 更新部分代码 34
  • CSS3设置Border边框是内边框还是外边框

    CSS3可以设置边框是向内还是向外 xff0c 如果要设置为内边框使用 1 box sizing border box 外边框 1 box sizing content box
  • If this view is optional add '@Nullable' (fields) or '@Optional' (methods) ...

    lt 在出错的activity中 xff0c 对应布局文件中加入 gt lt 不能缺少 xff0c 缺少后出现If this view is optional add 39 64 Nullable 39 fields or 39 64 Op
  • MTK Camera(OV13850) 驱动移植

    一 驱动源码包结构 拿到的驱动源码包解压后得到hal和kernel两个目录文件 xff0c 源码目录结构如下所示 13850 6592 driver 10 28 7z hal camera AE PLineTable ov13850mipi
  • 查看路由器WAN口IP是否为公网ip指南

    查看路由器WAN口IP是否为公网ip指南 吴捷 一 xff0e 公网ip和私网ip ip地址分类中常用的有A B C类 xff0c 每类IP中都规划了一段私网IP xff0c 除了这些私网外的IP都是公网IP 分类IP地址范围适用用户A1
  • [iOS] WKWebView 于JavaScript传值

    如果在项目中采用WKWebView的方法加载网页 OC向JS传值方法总结 xff1a 1 OC gt JS 传数组的方法 xff1a NSString arrStr 61 self arr componentsJoinedByString
  • iOS日历中的日程生成VCalendar 2.0(.vcs)格式的字符串和解析

    获取 VCalendar2 0 的格式字符串 43 NSString getVCalendar20StrWithEvents NSArray lt EKEvent gt events NSString vcalendar 61 NSStri
  • 数传电台术语详解

    数传电台 xff08 data radio xff09 是指借助DSP 技术和无线电技术实现的高性能专业数据传输电台 数传电台的使用从最早的按键电码 电报 模拟电台加无线MODEM xff0c 发展到目前的数字电台和DSP 软件无线电 xf
  • 无线数传电台的发展趋势

    摘自 专业无线通信 传媒 无线数传电台作为一种最简洁的通行方式 xff0c 具有很长的历史 xff0c 其基本的特征是通联方便 简捷 xff0c 同时也存在着封闭性强的特点 xff0c 正是由于上述原因 xff0c 无线数传电台产品的生命期
  • wget 提交post请求

    格式 xff1a wget post data 34 item1 61 value1 amp item2 61 value2 34 http xxx xxx com 示例 xff1a wget post data 34 username 6
  • 欧拉角、轴角与四元数

    1 欧拉角 欧拉角使用最简单的x y z值来分别表示在x xff0c y xff0c z轴上的旋转角度 xff0c 其取值为0 360 或者0 2pi xff09 xff0c 一般使用roll xff0c pitch xff0c yaw来表
  • Linux 系统投屏显示

    最近使用电脑跑Linux时需要用到显示器投屏 xff0c 于是快乐的拿上我的笔记本连上了投影仪 emmm 然鹅并没有什么卵用 果断问度娘 xff0c 看到好多人说使用xrandr命令设置 xff0c 于是便上手试了下 xff0c 看到我运行
  • C++ 判断一个 int 型整数是否为 2 的 N 次方(幂次)

    判断一个整数是否为2的幂次方法有以下几种 xff1a 1 循环除2 这是最简单最好理解的方式 对于一个数如果是2的幂次 xff0c 则其肯定可以被2一直整除直到其值为1 所以可以通过一个while循环判断 xff1a void judge
  • Dijkstra算法图文详解

    Dijkstra算法算是贪心思想实现的 xff0c 首先把起点到所有点的距离存下来找个最短的 xff0c 然后松弛一次再找出最短的 xff0c 所谓的松弛操作就是 xff0c 遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近 xff
  • 需求中如何画用例图

    UML用例图 用例图主要用来图示化系统的主事件流程 xff0c 它主要用来描述客户的需求 xff0c 即用户希望系统具备的完成一定功能的动作 xff0c 通俗地理解用例就是软件的功能模块 xff0c 所以是设计系统分析阶段的起点 xff0c
  • 反光板导航SLAM(二)VEnus代码浅析

    上一章简单介绍了VEnus中几个主要函数的作用 xff0c 这里详细展开看一下每个函数的具体思路 xff0c 通过研究具体的代码我们可以简单了解VEnus中对于反光柱定位的具体流程 1 IntensityExtraction Extract
  • TEB算法详解(TebLocalPlannerROS::computeVelocityCommands(3))

    第一章主要分析了teb算法的准备条件 xff0c 包括获取当前位姿与速度 对全局路径的裁减以获取局部路径等以及局部地图的获取等 第二章主要讲述了在获取前置条件后 xff0c 如何根据前置条件进行位姿优化 xff0c teb的路径优化主要是调