ros DWA局部规划模块

news2025/1/14 18:34:15

ROS-DWA模块

    • 主要流程
    • DWAPlannerROS::computeVelocityCommands
      • DWAPlannerROS::dwaComputeVelocityCommands
        • DWAPlanner::findBestPath
          • SimpleScoredSamplingPlanner::findBestTrajectory
    • 调参技巧
      • DWA被目标点过度吸引,且不听全局规划器指挥
    • 消融实验
      • goal_front_costs_
      • alignment_costs_
      • path_costs_
      • goal_costs_
      • 评价

在之前的学习中我们了解到,设定机器人的导航目标位置后,会在 MoveBase::executeCb函数中执行全局路径规划,并通过while循环反复执行 executeCycle函数控制机器人跟踪全局路径,这里控制机器人跟踪就是我们常说的局部规划,常用的局部规划方法有dwa、teb、pid、mpc、pure pursuit等,这一节对dwa算法进行研究。

主要流程

  bool DWAPlannerROS::setPlan(const std::vector<geometry_msgs::PoseStamped>& orig_global_plan) {
    if (! isInitialized()) {
      ROS_ERROR("This planner has not been initialized, please call initialize() before using this planner");
      return false;
    }
    //when we get a new plan, we also want to clear any latch we may have on goal tolerances
    latchedStopRotateController_.resetLatching();

    ROS_INFO("Got new plan");
    return dp_->setPlan(orig_global_plan);
  }

这里调用的是dp_的setPlan(),dp_是指向DWAPlanner的指针。

  bool DWAPlanner::setPlan(const std::vector<geometry_msgs::PoseStamped>& orig_global_plan) {
    oscillation_costs_.resetOscillationFlags();
    return planner_util_->setPlan(orig_global_plan);    // 将orig_global_plan设置给LocalPlannerUtil的global_plan_
  }

接着就是调用tc_->computeVelocityCommands(cmd_vel)进行局部规划。

DWAPlannerROS::computeVelocityCommands

bool DWAPlannerROS::computeVelocityCommands(geometry_msgs::Twist& cmd_vel) {
    // dispatches to either dwa sampling control or stop and rotate control, depending on whether we have been close enough to goal
    // 获取当前位姿
    if ( ! costmap_ros_->getRobotPose(current_pose_)) {
      ROS_ERROR("Could not get robot pose");
      return false;
    }
    // 调用planner_util_的getLocalPlan方法,以当前位姿为起点,
    // 获取局部路径,并将其存储在transformed_plan中。如果失败,则返回false。
    std::vector<geometry_msgs::PoseStamped> transformed_plan;
    if ( ! planner_util_.getLocalPlan(current_pose_, transformed_plan)) {
      ROS_ERROR("Could not get local plan");
      return false;
    }

    //if the global plan passed in is empty... we won't do anything
    if(transformed_plan.empty()) {
      ROS_WARN_NAMED("dwa_local_planner", "Received an empty transformed plan.");
      return false;
    }
    ROS_DEBUG_NAMED("dwa_local_planner", "Received a transformed plan with %zu points.", transformed_plan.size());

    // update plan in dwa_planner even if we just stop and rotate, to allow checkTrajectory
    dp_->updatePlanAndLocalCosts(current_pose_, transformed_plan, costmap_ros_->getRobotFootprint());
    // 使用latchedStopRotateController_来检查是否到达了目标位置。如果到达了,则执行停止和旋转的逻辑。
    if (latchedStopRotateController_.isPositionReached(&planner_util_, current_pose_)) {
      //publish an empty plan because we've reached our goal position
      std::vector<geometry_msgs::PoseStamped> local_plan;
      std::vector<geometry_msgs::PoseStamped> transformed_plan;
      publishGlobalPlan(transformed_plan);
      publishLocalPlan(local_plan);
      base_local_planner::LocalPlannerLimits limits = planner_util_.getCurrentLimits();
      return latchedStopRotateController_.computeVelocityCommandsStopRotate(
          cmd_vel,
          limits.getAccLimits(),
          dp_->getSimPeriod(),
          &planner_util_,
          odom_helper_,
          current_pose_,
          boost::bind(&DWAPlanner::checkTrajectory, dp_, _1, _2, _3));
    } else {
      // 计算DWA规划器的速度命令
      bool isOk = dwaComputeVelocityCommands(current_pose_, cmd_vel);
      if (isOk) {
        publishGlobalPlan(transformed_plan);
      } else {
        ROS_WARN_NAMED("dwa_local_planner", "DWA planner failed to produce path.");
        std::vector<geometry_msgs::PoseStamped> empty_plan;
        publishGlobalPlan(empty_plan);
      }
      return isOk;
    }
  }

主要的几个函数是:
1、
通过DWAPlannerROS::dwaComputeVelocityCommands计算施加在机器人上的控制速度,在该函数内部调用了dp_->findBestPath, 机器人控制命令通过drive_cmds拿到。

DWAPlannerROS::dwaComputeVelocityCommands

  bool DWAPlannerROS::dwaComputeVelocityCommands(geometry_msgs::PoseStamped &global_pose, geometry_msgs::Twist& cmd_vel) {
    // dynamic window sampling approach to get useful velocity commands
    if(! isInitialized()){
      ROS_ERROR("This planner has not been initialized, please call initialize() before using this planner");
      return false;
    }
    // 获取odom速度 
    geometry_msgs::PoseStamped robot_vel;
    odom_helper_.getRobotVel(robot_vel);

    /* For timing uncomment
    struct timeval start, end;
    double start_t, end_t, t_diff;
    gettimeofday(&start, NULL);
    */

    //compute what trajectory to drive along
    geometry_msgs::PoseStamped drive_cmds;
    drive_cmds.header.frame_id = costmap_ros_->getBaseFrameID();
    
    // call with updated footprint
    base_local_planner::Trajectory path = dp_->findBestPath(global_pose, robot_vel, drive_cmds);
    //ROS_ERROR("Best: %.2f, %.2f, %.2f, %.2f", path.xv_, path.yv_, path.thetav_, path.cost_);

    //pass along drive commands
    cmd_vel.linear.x = drive_cmds.pose.position.x;
    cmd_vel.linear.y = drive_cmds.pose.position.y;
    cmd_vel.angular.z = tf2::getYaw(drive_cmds.pose.orientation);

    //if we cannot move... tell someone
    std::vector<geometry_msgs::PoseStamped> local_plan;
    if(path.cost_ < 0) {
      ROS_DEBUG_NAMED("dwa_local_planner",
          "The dwa local planner failed to find a valid plan, cost functions discarded all candidates. This can mean there is an obstacle too close to the robot.");
      local_plan.clear();
      publishLocalPlan(local_plan);
      return false;
    }

    ROS_DEBUG_NAMED("dwa_local_planner", "A valid velocity command of (%.2f, %.2f, %.2f) was found for this cycle.", 
                    cmd_vel.linear.x, cmd_vel.linear.y, cmd_vel.angular.z);

    return true;
  }
DWAPlanner::findBestPath
  base_local_planner::Trajectory DWAPlanner::findBestPath(
      const geometry_msgs::PoseStamped& global_pose,
      const geometry_msgs::PoseStamped& global_vel,
      geometry_msgs::PoseStamped& drive_velocities) {

    //make sure that our configuration doesn't change mid-run
    boost::mutex::scoped_lock l(configuration_mutex_);

    Eigen::Vector3f pos(global_pose.pose.position.x, global_pose.pose.position.y, tf2::getYaw(global_pose.pose.orientation));
    Eigen::Vector3f vel(global_vel.pose.position.x, global_vel.pose.position.y, tf2::getYaw(global_vel.pose.orientation));
    geometry_msgs::PoseStamped goal_pose = global_plan_.back();
    Eigen::Vector3f goal(goal_pose.pose.position.x, goal_pose.pose.position.y, tf2::getYaw(goal_pose.pose.orientation));
    base_local_planner::LocalPlannerLimits limits = planner_util_->getCurrentLimits();

    // prepare cost functions and generators for this run
    generator_.initialise(pos,
        vel,
        goal,
        &limits,
        vsamples_);

    result_traj_.cost_ = -7;
    // find best trajectory by sampling and scoring the samples
    std::vector<base_local_planner::Trajectory> all_explored;
    scored_sampling_planner_.findBestTrajectory(result_traj_, &all_explored);

    // debrief stateful scoring functions
    oscillation_costs_.updateOscillationFlags(pos, &result_traj_, planner_util_->getCurrentLimits().min_vel_trans);

    //if we don't have a legal trajectory, we'll just command zero
    if (result_traj_.cost_ < 0) {
      drive_velocities.pose.position.x = 0;
      drive_velocities.pose.position.y = 0;
      drive_velocities.pose.position.z = 0;
      drive_velocities.pose.orientation.w = 1;
      drive_velocities.pose.orientation.x = 0;
      drive_velocities.pose.orientation.y = 0;
      drive_velocities.pose.orientation.z = 0;
    } else {
      drive_velocities.pose.position.x = result_traj_.xv_;
      drive_velocities.pose.position.y = result_traj_.yv_;
      drive_velocities.pose.position.z = 0;
      tf2::Quaternion q;
      q.setRPY(0, 0, result_traj_.thetav_);
      tf2::convert(q, drive_velocities.pose.orientation);
    }

    return result_traj_;
  }

generator_是轨迹生成器base_local_planner::SimpleTrajectoryGenerator,在DWAPlanner的构造函数中,可以看到,generator_被放置到了generator_list,然后传入了base_local_planner::SimpleScoredSamplingPlanner的构造函数构造出了scored_sampling_planner_。

std::vector<base_local_planner::TrajectoryCostFunction*> critics;
critics.push_back(&oscillation_costs_); // discards oscillating motions (assisgns cost -1)
critics.push_back(&obstacle_costs_); // discards trajectories that move into obstacles
critics.push_back(&goal_front_costs_); // prefers trajectories that make the nose go towards (local) nose goal
critics.push_back(&alignment_costs_); // prefers trajectories that keep the robot nose on nose path
critics.push_back(&path_costs_); // prefers trajectories on global path
critics.push_back(&goal_costs_); // prefers trajectories that go towards (local) goal, based on wave propagation
critics.push_back(&twirling_costs_); // optionally prefer trajectories that don't spin

// trajectory generators
std::vector<base_local_planner::TrajectorySampleGenerator*> generator_list;
generator_list.push_back(&generator_);

scored_sampling_planner_ = base_local_planner::SimpleScoredSamplingPlanner(generator_list, critics);

因此,DWAPlanner::findBestPath中下面这部分代码,就是对scored_sampling_planner_中所使用的轨迹生成器进行初始化。

generator_.initialise(pos,
    vel,
    goal,
    &limits,
    vsamples_);

vsamples_表示采样的数量。
接着调用了SimpleScoredSamplingPlanner::findBestTrajectory函数,dwa的最优轨迹通过参数traj返回,而参数all_explored则保存了DWA的所有搜索轨迹,下面来重点看这个函数。。

SimpleScoredSamplingPlanner::findBestTrajectory
  bool SimpleScoredSamplingPlanner::findBestTrajectory(Trajectory& traj, std::vector<Trajectory>* all_explored) {
    Trajectory loop_traj;
    Trajectory best_traj;
    double loop_traj_cost, best_traj_cost = -1;
    bool gen_success;
    int count, count_valid;
    // 检查所有的TrajectoryCostFunction是否准备好
    for (std::vector<TrajectoryCostFunction*>::iterator loop_critic = critics_.begin(); loop_critic != critics_.end(); ++loop_critic) {
      TrajectoryCostFunction* loop_critic_p = *loop_critic;
      if (loop_critic_p->prepare() == false) {
        ROS_WARN("A scoring function failed to prepare");
        return false;
      }
    }

    for (std::vector<TrajectorySampleGenerator*>::iterator loop_gen = gen_list_.begin(); loop_gen != gen_list_.end(); ++loop_gen) {
      count = 0;
      count_valid = 0;
      TrajectorySampleGenerator* gen_ = *loop_gen;
      // 遍历轨迹生成器生成的全部轨迹  
      while (gen_->hasMoreTrajectories()) {
        gen_success = gen_->nextTrajectory(loop_traj);
        if (gen_success == false) {
          // TODO use this for debugging
          continue;
        }
        // 对该轨迹进行打分  
        loop_traj_cost = scoreTrajectory(loop_traj, best_traj_cost);
        if (all_explored != NULL) {
          loop_traj.cost_ = loop_traj_cost;
          all_explored->push_back(loop_traj);
        }

        if (loop_traj_cost >= 0) {
          count_valid++;
          if (best_traj_cost < 0 || loop_traj_cost < best_traj_cost) {
            best_traj_cost = loop_traj_cost;   // 更新最佳得分
            best_traj = loop_traj;    // 更新最佳轨迹  
          }
        }
        count++;
        if (max_samples_ > 0 && count >= max_samples_) {
          break;
        }        
      }
      // 最佳轨迹的得分是合法的,说明找到了最佳轨迹 ,将最佳轨迹信息交给traj
      if (best_traj_cost >= 0) {
        traj.xv_ = best_traj.xv_;
        traj.yv_ = best_traj.yv_;
        traj.thetav_ = best_traj.thetav_;
        traj.cost_ = best_traj_cost;
        traj.resetPoints();
        double px, py, pth;
        for (unsigned int i = 0; i < best_traj.getPointsSize(); i++) {
          best_traj.getPoint(i, px, py, pth);
          traj.addPoint(px, py, pth);
        }
      }
      ROS_DEBUG("Evaluated %d trajectories, found %d valid", count, count_valid);
      if (best_traj_cost >= 0) {
        // do not try fallback generators
        break;
      }
    }
    return best_traj_cost >= 0;
  }

调参技巧

DWA被目标点过度吸引,且不听全局规划器指挥

如下图:
在这里插入图片描述
这是由于目标吸引的权重过大导致的,应该提高轨迹对齐的权重降低目标吸引的权重,如下,将goal_distance_bias的权重由24下降到5,机器人就能按照全局轨迹走而不是被目标吸引的卡住不动。
在这里插入图片描述

在这里插入图片描述

消融实验

goal_front_costs_

在这里插入图片描述
在这里插入图片描述
轨迹歪歪扭扭的,还喜欢拼命往墙上靠,感觉这个cost就是瞎搞的,还不如不要。。

alignment_costs_

在这里插入图片描述

在这里插入图片描述
轨迹平滑了很多,也不会往墙上靠,但是,变得很慢,而且到目的地后原地转圈。。

path_costs_

在这里插入图片描述
在这里插入图片描述

不按全局规划的路径走,原地转圈,怀疑原地转圈是属于恢复行为,但是速度为0,应该是由于该cost计算的是当前位姿和规划路径的偏移,而速度为0时偏移最小。

goal_costs_

在这里插入图片描述

在这里插入图片描述
按照对这个cost的理解,应该是始终的往离目的地最近的地方开,但是实际轨迹却歪歪扭扭,原地转圈,撞墙。

评价

ros dwa_local_planner包原生的这几个cost的实现各有各的问题,不是很好的实现,最好的解决办法就是直接抛弃重写。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1720687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

写大型C工程makefile构建~

正文 最开始学习linux应用开发编写的时候&#xff0c;估计大部分伙伴们都是在一个目录里面编译整个工程&#xff0c;主要是linux通常没有非常合适的集成开发环境。 以前单目录的方式实在太过捡漏&#xff0c;在linux环境中进行C代码工程开发很多时候需要编写一个相对比较通用的…

android gradle8.3 发布插件踩过的坑

之前写过gradle6.x和gradle7.x的插件&#xff0c;会有一些改动&#xff0c;到8.x我发现又有一些变化&#xff0c;记录一下&#xff0c;防止后边再遇到相同的情况 下边是插件的gradle文件配置 plugins {id("java-gradle-plugin") //会自动引入java-library、gradleAp…

SwiftUI中Popover的使用(弹出方式,箭头位置,如何退出)

在iOS中&#xff0c;popover是出现在现有内容顶部的UI元素&#xff0c;通常用于在上下文中向用户呈现新视图。与其他占用整个屏幕的视图控制器不同&#xff0c;popover出现在一个较小的、集中的区域&#xff0c;从而使用户能够在必要时与popover外的应用程序的其他部分进行交互…

C#的web项目ASP.NET

添加实体类和控制器类 using System; using System.Collections.Generic; using System.Linq; using System.Web;namespace WebApplication1.Models {public class Company{public string companyCode { get; set; }public string companyName { get; set; }public string com…

国产飞腾/龙芯/瑞芯微芯片在信创行业应用:金融行业、教育行业、党政机关

党政机构 方案背景&#xff1a; 在国家提出信息技术应用创新发展战略的大环境下&#xff0c;政务大厅需要基于国家科技自主技术深入推进“互联网政务服务”。加快建设全国一体化在线政务服务平台&#xff0c;进一步落实创新驱动发展战略&#xff0c;提升政务网络安全保障能力…

智慧园区整理技术方案(ppt,软件全套建设方案)

智慧园区管控平台整体技术方案 1.平台概述 2.公共安全 3.物业管理 4.综合管理 5.企业服务 平台规划&#xff0c;整理技术架构搭建&#xff0c;统一门户&#xff0c;lot物联平台&#xff0c;视频云管理平台&#xff0c;GIS服务平台&#xff0c;服务器架构&#xff0c;统一身份认…

Ps:调整画笔工具

调整画笔工具 Adjustment Brush Tool可以将选区、创建蒙版和应用调整的传统工作流程合并为一个步骤&#xff0c;简化了对图像进行非破坏性局部调整的操作。 快捷键&#xff1a;B 调整画笔工具是 Photoshop 2024 年 5 月版&#xff08;25.9 版&#xff09;新增的工具。 ◆ ◆ …

JavaSE:SE知识整体总结

1、引言 历时一个多月的学习&#xff0c;已经掌握了JavaSE的知识&#xff0c;这篇博客就来做一下SE知识的总结~ 2、数据类型和变量 Java中的数据类型分为基本数据类型和引用数据类型。 2.1 基本数据类型 基本数据类型共有四类八种&#xff1a; 四类&#xff1a;整形、浮点…

遗留和现代数据库中的向量搜索

遗留和现代数据库中的向量搜索 image1 向量数据库是一种将数据&#xff08;包括文本、图像、音频和视频&#xff09;存储为向量的数据库&#xff0c;向量是高维空间中对象或概念的数学表示。 注意&#xff1a;根据数据的复杂程度和细节&#xff0c;每个向量的维数可能差别很大&…

SpringBoot的第二大核心AOP系统梳理

目录 1 事务管理 1.1 事务 1.2 Transactional注解 1.2.1 rollbackFor 1.2.2 propagation 2 AOP 基础 2.1 AOP入门 2.2 AOP核心概念 3. AOP进阶 3.1 通知类型 3.2 通知顺序 3.3 切入点表达式 execution切入点表达式 annotion注解 3.4 连接点 1 事务管理 1.1 事务…

面试杂谈k8s

其实看我之前的博客&#xff0c;k8s刚有点苗头的时候我就研究过&#xff0c;然后工作的时候间接接触 也自己玩过 但是用的不多就忘记了&#xff0c;正苦于不知道写什么&#xff0c;水一篇 用来面试应该是够了 支持云应用开发、运行与运维一体化的云应用平台软件应运而生 k8s核…

前端树形结构组件的设计与实现:以企查查、天眼查股权结构为例

摘要 随着信息化时代的不断发展&#xff0c;数据可视化在各行各业的应用越来越广泛。特别是在商业信息查询领域&#xff0c;如企查查、天眼查等平台&#xff0c;通过直观的数据展示方式&#xff0c;帮助用户快速理解复杂的商业关系。本文将以一个前端tree树形结构模版组件为例…

CompassArena 司南大模型测评--代码编写

测试角度 要说测试模型&#xff0c;对咱们程序员来说&#xff0c;那自然是写代码的能力强不强比较重要了。那么下面我们以 leetcode 中的一道表面上是困难题的题目来考考各家大模型&#xff0c;看看哪个才应该是咱们日常写程序的帮手。 部分模型回答 问题部分如下截图&#…

Day-04python模块

一、模块 1-1 Python 自带模块 Json模块 处理json数据 {"key":"value"} json不是字典 本质是一个有引号的字符串数据 json注意点 {} 中的数据是字符串引号必须是双引号 使用json模块可以实现将json转为字典&#xff0c;使用字典的方法操作数据 。 或者将…

HCIP-Datacom-ARST自选题库__MAC【14道题】

一、单选题 1.缺省情况下&#xff0c;以下哪种安全MAC地址类型在设备重启后表项会丢失? 黑洞MAC地址 Sticky MAC地址 安全动态MAC地址 安全静态MAC地址 2.华为交换机MAC地址表中的动态sticky MAC地址的默认老化时间是多少秒? 300 不会老化 400 500 3.华为交换机MA…

apache大数据各组件部署搭建(超级详细)

apache大数据数仓各组件部署搭建 第一章 环境准备 1. 机器规划 准备3台服务器用于集群部署,系统建议CentOS7+,2核8G内存 172.19.195.228 hadoop101 172.19.195.229 hadoop102 172.19.195.230 hadoop103 [root@hadoop101 ~]# cat /etc/redhat-release CentOS Linux rele…

揭秘APP广告变现项目

在当今移动应用市场&#xff0c;广告变现已经成为开发者盈利策略的重要组成部分。 通过在应用程序中展示多种类型的广告&#xff0c;如插页式广告、横幅广告和激励视频广告&#xff0c;开发者能够获得经济效益。 实现这一目标的核心在于平衡收入与用户体验&#xff0c;避免过…

【差分隐私联邦学习从入门到发文】

差分隐私联邦学习从入门到发文 差分隐私联邦学习从入门到发文一、学习相关理论1. 差分隐私理论解读2. 联邦学习相关收敛性分析3. 差分隐私经典论文解读4. 联邦学习代码解读5. 深度学习相关代码网站 二、必读论文三、最新进展2023 差分隐私联邦学习从入门到发文 这是关于差分隐…

虚拟现实环境下的远程教育和智能评估系统(五)

查阅相关VR眼动注意力联合教育学相关论文 1.Exploring Eye Gaze Visualization Techniques for Identifying Distracted Students in Educational VR&#xff08;IEEE VR 2020&#xff09; 摘要&#xff1a;我们提出了一种架构&#xff0c;使VR教学代理能够响应眼动追踪监控…

校园交友|基于SprinBoot+vue的校园交友网站(源码+数据库+文档)

校园交友网站 目录 基于SprinBootvue的校园交友网站 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2后台功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#x…