国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

move_base代碼解析(四)局部路徑規(guī)劃:TrajectoryPlannerROS::computeVelocityCommands

這篇具有很好參考價(jià)值的文章主要介紹了move_base代碼解析(四)局部路徑規(guī)劃:TrajectoryPlannerROS::computeVelocityCommands。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

在第三章中講述了executeCycle的總體作用,可以看到這個(gè)函數(shù)的作用主要是將全局路徑規(guī)劃的路徑給到局部路徑規(guī)劃,并判斷機(jī)器人是否到位,如果沒(méi)有到位就調(diào)用computeVelocityCommands函數(shù)計(jì)算機(jī)器人的速度。這里也就是move_base的局部路徑規(guī)劃的所在之處。這章簡(jiǎn)單張開(kāi)看一下move_base的局部路徑規(guī)劃的流程:

move_base中的局部路徑規(guī)劃函數(shù)的默認(rèn)入口函數(shù)應(yīng)該是TrajectoryPlannerROS::computeVelocityCommands,這個(gè)函數(shù)主要包含了以下幾個(gè)部分:

1、獲取機(jī)器人的全局坐標(biāo)

路徑規(guī)劃首先需要知道機(jī)器人在哪兒,所以算法的第一步先得到機(jī)器人的位姿

	std::vector<geometry_msgs::PoseStamped> local_plan;
    geometry_msgs::PoseStamped global_pose;
    if (!costmap_ros_->getRobotPose(global_pose)) {
      return false;
    }

2、獲取全局路徑規(guī)劃

在獲取了機(jī)器人的坐標(biāo)后,下一步就是獲取之前規(guī)劃出來(lái)的全局路徑規(guī)劃了:

	if (!transformGlobalPlan(*tf_, global_plan_, global_pose, *costmap_, global_frame_, transformed_plan)) {
      ROS_WARN("Could not transform the global plan to the frame of the controller");
      return false;
    }

這里的transformGlobalPlan是定義在goal_function中的函數(shù),該函數(shù)的作用是根據(jù)本地代價(jià)地圖,從全局路徑global_plan截取一段,并將路徑點(diǎn)從/map坐標(biāo)系轉(zhuǎn)換到/odom坐標(biāo)系,生成結(jié)果放在transformed_plan。所以這里transformed_plan大小是小于全局路徑點(diǎn)的。它的大小由:

double dist_threshold = std::max(costmap.getSizeInCellsX() * costmap.getResolution() / 2.0,
                                       costmap.getSizeInCellsY() * costmap.getResolution() / 2.0);

決定,也就是代價(jià)地圖的大小。

3、路徑清除

在機(jī)器人運(yùn)動(dòng)過(guò)程中,全局路徑中的前面一部分點(diǎn)位是被經(jīng)歷過(guò)的,局部路徑中的一部分點(diǎn)也是已經(jīng)走過(guò)的。為了減小后面的計(jì)算量。所以將這些點(diǎn)從路徑中刪除:

void prunePlan(const geometry_msgs::PoseStamped& global_pose, std::vector<geometry_msgs::PoseStamped>& plan, std::vector<geometry_msgs::PoseStamped>& global_plan){
    ROS_ASSERT(global_plan.size() >= plan.size());
    std::vector<geometry_msgs::PoseStamped>::iterator it = plan.begin();
    std::vector<geometry_msgs::PoseStamped>::iterator global_it = global_plan.begin();
    while(it != plan.end()){
      const geometry_msgs::PoseStamped& w = *it;
      // Fixed error bound of 2 meters for now. Can reduce to a portion of the map size or based on the resolution
      double x_diff = global_pose.pose.position.x - w.pose.position.x;
      double y_diff = global_pose.pose.position.y - w.pose.position.y;
      double distance_sq = x_diff * x_diff + y_diff * y_diff;
      if(distance_sq < 1){
        ROS_DEBUG("Nearest waypoint to <%f, %f> is <%f, %f>\n", global_pose.pose.position.x, global_pose.pose.position.y, w.pose.position.x, w.pose.position.y);
        break;
      }
      it = plan.erase(it);
      global_it = global_plan.erase(global_it);
    }
  }

這里的清除方式很簡(jiǎn)單,因?yàn)榇鎯?chǔ)全局路徑的容器是按順序存放的,所以先經(jīng)歷過(guò)的點(diǎn)肯定是在global_plan的最前端,則我們判斷這個(gè)點(diǎn)離機(jī)器人的距離。因?yàn)殡S著機(jī)器人的運(yùn)動(dòng)這個(gè)點(diǎn)會(huì)逐漸遠(yuǎn)離機(jī)器人。當(dāng)其遠(yuǎn)離機(jī)器人的距離超過(guò)1m以上,則我們將這個(gè)點(diǎn)從全局路徑global_plan以及代價(jià)地圖中刪除。

4、如果機(jī)器人到達(dá)目標(biāo)點(diǎn)附近但是角度不滿足要求

在計(jì)算速度時(shí),首先判斷當(dāng)前機(jī)器人是否到達(dá)目標(biāo)點(diǎn),如果到達(dá)目標(biāo)點(diǎn)的話,則需要再判斷角度是否也滿足閾值要求:

if (xy_tolerance_latch_ || (getGoalPositionDistance(global_pose, goal_x, goal_y) <= xy_goal_tolerance_)) {
      if (latch_xy_goal_tolerance_) {
        xy_tolerance_latch_ = true;
      }
      //檢查是否到達(dá)目標(biāo)“朝向、姿態(tài)”
      //獲取當(dāng)前朝向和目標(biāo)姿態(tài)的差值
      double angle = getGoalOrientationAngleDifference(global_pose, goal_th);
      //check to see if the goal orientation has been reached
      //到達(dá)目標(biāo)位置,如果差值小于容忍度
      if (fabs(angle) <= yaw_goal_tolerance_) 

如果機(jī)器人的坐標(biāo)已經(jīng)再目標(biāo)點(diǎn)附近,同時(shí)判斷出來(lái)角度的差值也小于閾值,則我們認(rèn)為機(jī)器人已經(jīng)再目標(biāo)點(diǎn)離,此時(shí)將速度全都設(shè)置為0。如果此時(shí)角度差超過(guò)閾值,則認(rèn)為機(jī)器人當(dāng)前還沒(méi)有完全到位,則調(diào)用函數(shù)更新機(jī)器人運(yùn)動(dòng)速度:

		//如果到達(dá)位置,但朝向和姿態(tài)沒(méi)達(dá)到目標(biāo)要求
        //將全局路徑拷貝進(jìn)來(lái),并認(rèn)為全局路徑的最后一個(gè)點(diǎn)就是終點(diǎn)
        tc_->updatePlan(transformed_plan);
        //給定當(dāng)前機(jī)器人的位置和朝向,計(jì)算機(jī)器人應(yīng)該跟隨的“best”軌跡,存儲(chǔ)在drive_cmds中
        Trajectory path = tc_->findBestPath(global_pose, robot_vel, drive_cmds);
        //發(fā)布代價(jià)地圖點(diǎn)云
        map_viz_.publishCostCloud(costmap_);

        //copy over the odometry information
        //得到里程計(jì)信息
        nav_msgs::Odometry base_odom;
        odom_helper_.getOdom(base_odom);

首先這里的updatePlan(transformed_plan)將前面獲取的全局路徑規(guī)劃中代價(jià)地圖部分的位姿傳遞給trajectory_planner。然后再調(diào)用findBestPath(global_pose, robot_vel, drive_cmds)函數(shù)計(jì)算出實(shí)際的機(jī)器人的速度。數(shù)據(jù)存儲(chǔ)在drive_cmds中返回。展開(kāi)看一下findBestPath這個(gè)函數(shù):

4.1、獲取機(jī)器人的footprint

		//用當(dāng)前位姿獲取機(jī)器人footprint
    	std::vector<base_local_planner::Position2DInt> footprint_list =
        footprint_helper_.getFootprintCells(
            pos,
            footprint_spec_,
            costmap_,
            true);

在給定位置獲取足跡的單元格。這里應(yīng)該會(huì)根據(jù)機(jī)器人模型填充不同的單元格。然后將初始footprint內(nèi)的所有cell標(biāo)記 “within_robot = true”,表示在機(jī)器人足跡內(nèi):

	for (unsigned int i = 0; i < footprint_list.size(); ++i) {
      path_map_(footprint_list[i].x, footprint_list[i].y).within_robot = true;
    }

4.2、更新地圖

	//確保根據(jù)全局規(guī)劃global plan更新路徑,并計(jì)算代價(jià)
    //更新哪些地圖上的cell是在全局規(guī)劃路徑上的,target_dist設(shè)置為0
    //并且通過(guò)它們和其他點(diǎn)的相對(duì)位置計(jì)算出來(lái)地圖上所有點(diǎn)的target_dist
    path_map_.setTargetCells(costmap_, global_plan_);
    goal_map_.setLocalGoal(costmap_, global_plan_);

對(duì)于setLocalGoal函數(shù),是用來(lái)計(jì)算MapGrid地圖中所有點(diǎn)到局部地圖路徑末端點(diǎn)的最短距離。在該函數(shù)中,首先對(duì)映射到局部地圖中的路徑進(jìn)行柵格級(jí)的插補(bǔ)(MapGrid::adjustPlanResolution),從而使得整個(gè)路徑是柵格連續(xù)的。然后,查找路徑在局部地圖中的最后一個(gè)點(diǎn),并將MapGrid地圖中的該點(diǎn)標(biāo)記為已訪問(wèn)(標(biāo)記為已訪問(wèn)的點(diǎn)可以進(jìn)行最短路徑計(jì)算),然后調(diào)用computeTargetDistance函數(shù)采用類似dijkstra算法的逐步探索的方式,計(jì)算出MapGrid地圖中所有點(diǎn)(柵格級(jí))相對(duì)于與標(biāo)記點(diǎn)的最短距離。

對(duì)于setTargetCells函數(shù),是用來(lái)計(jì)算軌跡MapGrid地圖中所有點(diǎn)到局部地圖路徑的最短距離。與setLocalGoal函數(shù)的區(qū)別主要在于,首先將局部地圖路徑中所有的點(diǎn)標(biāo)記為已訪問(wèn),然后調(diào)用computeTargetDistance函數(shù)就會(huì)計(jì)算MapGrid地圖中所有點(diǎn)到整個(gè)局部地圖路徑的最短距離,具體算法需要進(jìn)一步了解。

因此,在經(jīng)過(guò)預(yù)計(jì)算后,對(duì)于path_costs_和alignment_costs這兩個(gè)考慮與路徑距離的打分項(xiàng),其內(nèi)部的MapGrid地圖中存儲(chǔ)的是所有點(diǎn)到路徑的最短距離;對(duì)于goal_costs_和goal_front_costs_這兩個(gè)考慮與局部目標(biāo)點(diǎn)距離的打分項(xiàng),其內(nèi)部的MapGrid地圖中存儲(chǔ)的是所有點(diǎn)到局部目標(biāo)點(diǎn)的最短距離。

4.3、找到代價(jià)最低的軌跡

當(dāng)我們有了地圖,有了路徑,下一步自然是要找到一條代價(jià)最低的路徑:

//輸入分別是目前機(jī)器人位置,速度以及加速度限制
Trajectory best = createTrajectories(pos[0], pos[1], pos[2],
        vel[0], vel[1], vel[2],
        acc_lim_x_, acc_lim_y_, acc_lim_theta_);//生成諸多可能軌跡,選取其中打分最高的。這里也就是最關(guān)鍵的一步。
4.3.1、計(jì)算可行的線速度和角速度范圍

這里先對(duì)最大線速度進(jìn)行一個(gè)限制,即保證速度既不超過(guò)預(yù)設(shè)的最大速度限制,也不超過(guò)“起點(diǎn)與目標(biāo)直線距離/總仿真時(shí)間”。

	//compute feasible velocity limits in robot space
    //聲明最大/小線速度,最大/小角速度
    double max_vel_x = max_vel_x_, max_vel_theta;
    double min_vel_x, min_vel_theta;
    //如果最終的目標(biāo)是有效的(全局規(guī)劃不為空)
    //檢查最終點(diǎn)是否是有效的,判斷變量在updatePlan中被賦值
    if( final_goal_position_valid_ ){
      //計(jì)算當(dāng)前位置和目標(biāo)位置之間的距離:final_goal_dist
      double final_goal_dist = hypot( final_goal_x_ - x, final_goal_y_ - y );
      //最大速度:在預(yù)設(shè)的最大速度和
      max_vel_x = min( max_vel_x, final_goal_dist / sim_time_ );
    }
4.3.2、計(jì)算線速度與角速度的上下限

使用的限制是由當(dāng)前速度在一段時(shí)間內(nèi),由最大加減速度達(dá)到的速度范圍,這里進(jìn)行了一個(gè)判斷,即是否使用dwa法:

① 使用dwa法,則用的是軌跡前向模擬的周期sim_period_(專用于dwa法計(jì)算速度的一個(gè)時(shí)間間隔);

② 不使用dwa法,則用的是整段仿真時(shí)間sim_time_

	//should we use the dynamic window approach?
    //是否使用dwa算法,sim_peroid_是1/controller_frequency_,暫時(shí)不清楚sim_period_和sim_time_的區(qū)別
    if (dwa_) {
      //使用dwa窗口法,sim_period_是dwa計(jì)算最大、最小速度用的時(shí)間
      //計(jì)算速度、角速度范圍,引入加速度限制條件(用sim_period_)
      max_vel_x = max(min(max_vel_x, vx + acc_x * sim_period_), min_vel_x_);
      min_vel_x = max(min_vel_x_, vx - acc_x * sim_period_);

      max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_period_);
      min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_period_);
    } else {
      //忽略其中的邏輯,按照不同的規(guī)則生成路徑,調(diào)用的子函數(shù)是generateTrajectory
      //不使用dwa窗口法
      //計(jì)算速度、角速度范圍,引入加速度限制條件(用sim_time_)
      max_vel_x = max(min(max_vel_x, vx + acc_x * sim_time_), min_vel_x_);
      min_vel_x = max(min_vel_x_, vx - acc_x * sim_time_);

      max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_time_);
      min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_time_);
    }

在生成范圍時(shí)注意用預(yù)先給定的速度和角速度范圍參數(shù)進(jìn)行保護(hù)。

4.3.3、計(jì)算采樣間隔以及初始采樣速度

根據(jù)預(yù)設(shè)的線速度與角速度的采樣數(shù),和上面計(jì)算得到的范圍,分別計(jì)算出采樣間隔,并把范圍內(nèi)最小的線速度和角速度作為初始采樣速度。不考慮全向機(jī)器人的情況,即不能y向移動(dòng),故暫不考慮vy上采樣。

	//we want to sample the velocity space regularly
    //計(jì)算速度采樣間隔
    double dvx = (max_vel_x - min_vel_x) / (vx_samples_ - 1);
    double dvtheta = (max_vel_theta - min_vel_theta) / (vtheta_samples_ - 1);//計(jì)算角速度采樣間隔

    double vx_samp = min_vel_x;//x方向速度采樣點(diǎn)
    double vtheta_samp = min_vel_theta;//角速度采樣點(diǎn)
    double vy_samp = 0.0;

    //keep track of the best trajectory seen so far
    Trajectory* best_traj = &traj_one;
    best_traj->cost_ = -1.0;//先初始化一個(gè)最優(yōu)路徑的代價(jià)=-1
    //下面生成的軌跡放到這里,和best_traj作比較,如果生成的軌跡代價(jià)更小,就選擇它
    Trajectory* comp_traj = &traj_two;
    //先初始化一個(gè)當(dāng)前“對(duì)比”路徑的代價(jià)=-1,等會(huì)再用generateTrajectory函數(shù)生成路徑和新的代價(jià)存放在里面
    comp_traj->cost_ = -1.0;
    //用于交換的指針
    Trajectory* swap = NULL;

為了迭代比較不同采樣速度生成的不同路徑的代價(jià),這里聲明best_traj和comp_traj并都將其代價(jià)初始化為-1

4.3.4、軌跡生成

遍歷所有線速度和角速度,調(diào)用類內(nèi)generateTrajectory函數(shù)用它們生成軌跡。二重迭代時(shí),外層遍歷線速度(正值),內(nèi)層遍歷角速度。在遍歷時(shí),單獨(dú)拎出角速度=0,即直線前進(jìn)的情況,避免由于采樣間隔的設(shè)置而躍過(guò)了這種特殊情況。

邊迭代生成邊比較,獲得代價(jià)最小的路徑,存放在best_traj。

	  //【循環(huán)所有速度和角速度、打分】
      //外側(cè)循環(huán)所有x速度
      for(int i = 0; i < vx_samples_; ++i) {
        //x速度循環(huán)內(nèi)部遍歷角速度
        //①角速度=0
        vtheta_samp = 0;
        //first sample the straight trajectory
        //傳入當(dāng)前位姿、速度、加速度限制,采樣起始x向速度、y向速度、角速度,代價(jià)賦給comp_traj
        generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp,
            acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);

        //if the new trajectory is better... let's take it
        //對(duì)比生成路徑和當(dāng)前最優(yōu)路徑的分?jǐn)?shù),如果生成的路徑分?jǐn)?shù)更小,就把當(dāng)前路徑和最優(yōu)路徑交換
        //這里會(huì)將第一次生成路徑的代價(jià)賦給best_traj
        if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
          swap = best_traj;
          best_traj = comp_traj;
          comp_traj = swap;
        }
        //接下來(lái)產(chǎn)生機(jī)器人在原地旋轉(zhuǎn)的軌跡
        vtheta_samp = min_vel_theta;
        //next sample all theta trajectories
        //接下來(lái)迭代循環(huán)生成所有角速度的路徑、打分
        for(int j = 0; j < vtheta_samples_ - 1; ++j){
          generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp,
              acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);

          //if the new trajectory is better... let's take it
          //同樣地,如果新路徑代價(jià)更小,和best_traj作交換
          if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
            swap = best_traj;
            best_traj = comp_traj;
            comp_traj = swap;
          }
          //遍歷角速度
          vtheta_samp += dvtheta;
        }
        //遍歷x速度
        vx_samp += dvx;
      }

這里需要注意到一個(gè)重要的函數(shù)就是generateTrajectory函數(shù),局部路徑規(guī)劃的路徑生成器就是來(lái)自于這里。這個(gè)函數(shù)出要邏輯如下:

4.3.4.1、計(jì)算總體速度
	//compute the magnitude of the velocities
    double vmag = hypot(vx_samp, vy_samp);

C ++hypot()函數(shù)是cmath標(biāo)頭的庫(kù)函數(shù),用于查找給定數(shù)字的斜邊,接受兩個(gè)數(shù)字并返回斜邊的計(jì)算結(jié)果,即sqrt(x * x + y * y)。

4.3.4.2、計(jì)算步長(zhǎng)
	int num_steps;
    if(!heading_scoring_) {
      //sim_granularity_:仿真點(diǎn)之間的距離間隔
      //步數(shù) = max(速度?!量偡抡鏁r(shí)間/距離間隔,角速度/角速度間隔),四舍五入
      num_steps = int(max((vmag * sim_time_) / sim_granularity_, fabs(vtheta_samp) / angular_sim_granularity_) + 0.5);
    } else {
      //步數(shù) = 總仿真時(shí)間/距離間隔,四舍五入
      num_steps = int(sim_time_ / sim_granularity_ + 0.5);
    }

局部路徑規(guī)劃的終點(diǎn)在cost_map的邊界處,一般還是距離比較遠(yuǎn)的。所以需要在路徑上按照一定的角度以及距離閾值采樣。

4.3.4.3、計(jì)算每個(gè)步長(zhǎng)的時(shí)間
	//至少一步
    if(num_steps == 0) {
      num_steps = 1;
    }
    //每一步的時(shí)間
    double dt = sim_time_ / num_steps;
    double time = 0.0;
4.3.4.4、聲明一條軌跡

resetPoints函數(shù)創(chuàng)造一個(gè)新的軌跡,軌跡速度設(shè)置為sample,cost設(shè)置為-1

	//create a potential trajectory
    //聲明軌跡
    traj.resetPoints();
    traj.xv_ = vx_samp;//線速度
    traj.yv_ = vy_samp;
    traj.thetav_ = vtheta_samp;//角速度
    traj.cost_ = -1.0;//默認(rèn)代價(jià)-1.0
4.3.4.5、模擬每一步運(yùn)動(dòng)

在得到具體步數(shù)以及步長(zhǎng)后,接下來(lái)就是計(jì)算每一步機(jī)器人的落點(diǎn)了,這里是通過(guò)一個(gè)for循環(huán)獲取所有的落點(diǎn),主要是執(zhí)行了以下幾個(gè)步驟:

  1. 使用footprintCost計(jì)算出footprint

footprint為負(fù),即在這一步遇到了障礙物,規(guī)劃失敗

 	  double footprint_cost = footprintCost(x_i, y_i, theta_i);

      //if the footprint hits an obstacle this trajectory is invalid
      //若返回一個(gè)負(fù)值,說(shuō)明機(jī)器人在路徑上遇障,直接return
      if(footprint_cost < 0){
        traj.cost_ = -1.0;
        return;

footprint_cost的計(jì)算方式是將當(dāng)前機(jī)器人的footprint投影到costmap下,然后判斷這個(gè)區(qū)域內(nèi)的點(diǎn)的cost。對(duì)于costmap,其中每個(gè)柵格的cost計(jì)算方式如下:

在給定一個(gè)單元格與障礙物之間的距離的情況下,計(jì)算距離的成本值。首先進(jìn)行距離的判斷,如果距離為0,則返回代價(jià)為致命障礙物的代價(jià)值LETHAL_OBSTACLE;再繼續(xù)往下判斷,距離x分辨率是否小于內(nèi)切半徑,是的話,則返回代價(jià)為機(jī)器人內(nèi)切圓膨脹障礙物的代價(jià)值INSCRIBED_INFLATED_OBSTACLE;否的話,則代價(jià)按照歐幾里德距離遞減,歐幾里德距離為距離x分辨率,定義factor因子為指數(shù)函數(shù),最后計(jì)算出cost值。可以看出,距離障礙物越遠(yuǎn),代價(jià)值就越?。?/p>

static const unsigned char NO_INFORMATION = 255;  未知空間
static const unsigned char LETHAL_OBSTACLE = 254;  說(shuō)明該單元格中存在一個(gè)實(shí)際的障礙。若機(jī)器人的中心在該單元格中,機(jī)器人必然會(huì)跟障礙物相撞
static const unsigned char INSCRIBED_INFLATED_OBSTACLE = 253;  說(shuō)明該單元格離障礙物的距離小于機(jī)器人內(nèi)切圓半徑。若機(jī)器人的中心位于等于或高于"Inscribed" cost的單元格,機(jī)器人必然會(huì)跟障礙物相撞
static const unsigned char FREE_SPACE = 0;  沒(méi)有障礙的空間
/** @brief  Given a distance, compute a cost.給定一個(gè)距離,計(jì)算一個(gè)cost代價(jià)
   * @param  distance The distance from an obstacle in cells 單元格與障礙物之間的距離
   * @return A cost value for the distance 距離的成本值
   * 
   * */
virtual inline unsigned char computeCost(double distance) const
  {
    unsigned char cost = 0;
    if (distance == 0)
      cost = LETHAL_OBSTACLE;
    else if (distance * resolution_ <= inscribed_radius_)
      cost = INSCRIBED_INFLATED_OBSTACLE;
    else
    {
      // make sure cost falls off by Euclidean distance 確保成本以歐幾里德距離遞減
      double euclidean_distance = distance * resolution_;
      double factor = exp(-1.0 * weight_ * (euclidean_distance - inscribed_radius_));
      cost = (unsigned char)((INSCRIBED_INFLATED_OBSTACLE - 1) * factor);
    }
    return cost;
  }

而根據(jù)機(jī)器人模型又可以分為兩種情況:

圓形機(jī)器人:對(duì)于圓形機(jī)器人,需要判斷機(jī)器人的圓心是否處于FREE_SPACE就可以了。之所以不用考慮半徑問(wèn)題是因?yàn)樵赾ostmap初始化問(wèn)題時(shí)已經(jīng)考慮了膨脹半徑的問(wèn)題了。一般情況下costmap的膨脹半徑會(huì)略大于機(jī)器人內(nèi)切圓半徑,這樣子如果機(jī)器人處于膨脹層就說(shuō)明機(jī)器人的邊會(huì)碰到障礙物。而這些膨脹層就是上面的INSCRIBED_INFLATED_OBSTACLE。所以對(duì)于圓形的機(jī)器人,不管它的中心點(diǎn)處于NO_INFORMATION、LETHAL_OBSTACLE還是INSCRIBED_INFLATED_OBSTACLE都是不可取的,所以圓形機(jī)器人的代價(jià)值應(yīng)該在0-252。

多邊形機(jī)器人:而多邊形機(jī)器人則需要計(jì)算每一條邊上的點(diǎn),判斷這條邊有沒(méi)有與障礙物重合,這里的邊進(jìn)入膨脹層其實(shí)是可以通行的,因?yàn)榕蛎泴釉诒举|(zhì)上從空間角度來(lái)看是可通行的空間,如果機(jī)器人的邊界都在膨脹層而沒(méi)有入侵到障礙物層,則機(jī)器人本身也不會(huì)與障礙物發(fā)生沖突。所以多邊形機(jī)器人的代價(jià)值范圍應(yīng)該在0-253。

  1. 計(jì)算occcost
	  //更新occ_cost:max(max(occ_cost,路徑足跡代價(jià)),當(dāng)前位置的代價(jià))
      //也就是把所有路徑點(diǎn)的最大障礙物代價(jià)設(shè)置為路徑的occ_cost
      occ_cost = std::max(std::max(occ_cost, footprint_cost), double(costmap_.getCost(cell_x, cell_y)));

costmap_.getCost的作用為給定一個(gè)柵格(mx, my),返回這個(gè)單元的代價(jià)值。每個(gè)柵格具體的代價(jià)值在前面局部地圖初始化的時(shí)候應(yīng)該都計(jì)算好了的(4.2),這里只需要查詢就可以了。所以這里occ_cost的值相當(dāng)于獲取了0、footprint_cost以及getCost(cell_x, cell_y)三者中的最大者。這個(gè)值越大說(shuō)明了機(jī)器人走這條路前往目的地的代價(jià)就越大。對(duì)于圓形機(jī)器人來(lái)說(shuō)footprint_cost應(yīng)該等于getCost(cell_x, cell_y)>0,而矩形機(jī)器人的話則既可能footprint_cost>getCost(cell_x, cell_y)也可能footprint_cost<getCost(cell_x, cell_y)。前者是機(jī)器人footprint空間內(nèi)無(wú)障礙物,所以邊界處離障礙物更近。后者可能是該次仿真后存在一個(gè)障礙物點(diǎn)在機(jī)器人footprint空間內(nèi)部。

  1. 計(jì)算goaldist和pathdist

這里似乎是計(jì)算了兩個(gè)距離值,一個(gè)是到目標(biāo)點(diǎn)的距離,一個(gè)是到全局路徑的距離?這兩個(gè)值越大代表當(dāng)前仿真得到的路徑距離目標(biāo)點(diǎn)的代價(jià)越大。

  1. 判斷是否需要根據(jù)朝向打分

heading_scoring_timestep_是給朝向打分時(shí)在時(shí)間上要看多遠(yuǎn),也就是在路徑上走過(guò)一個(gè)特定時(shí)刻后,才為朝向打分一次。

  1. 軌跡打分

前面4.3.4.2計(jì)算了一個(gè)迭代次數(shù)作為for循環(huán)的次數(shù),在達(dá)到該次數(shù)之前會(huì)一直循環(huán)前面四個(gè)步驟并將每次循環(huán)計(jì)算得到的軌跡點(diǎn)添加到traj.point內(nèi)作為備選路徑點(diǎn),直到達(dá)到循環(huán)次數(shù),此時(shí)機(jī)器人基本上也就位于了這條軌跡的端點(diǎn)。此時(shí)需要計(jì)算并返回這條軌跡的最終代價(jià)值,這個(gè)值最后也會(huì)作為這條軌跡是否被選上的依據(jù):

	double cost = -1.0;
    if (!heading_scoring_) {
      //代價(jià)=路徑距離+目標(biāo)距離+障礙物代價(jià)(乘以各自的比例)
      cost = path_distance_bias_ * path_dist + goal_dist * goal_distance_bias_ + occdist_scale_ * occ_cost;
    } else {
      //代價(jià)=路徑距離+目標(biāo)距離+障礙物代價(jià)+航向角(如果有航偏則會(huì)增大代價(jià))
      cost = occdist_scale_ * occ_cost + path_distance_bias_ * path_dist + 0.3 * heading_diff + goal_dist * goal_distance_bias_;
    }
    //設(shè)置稱當(dāng)前路徑的代價(jià)
    traj.cost_ = cost;

最終的打分原則比較簡(jiǎn)單,就是三個(gè)代價(jià)值再乘以各自的比例,如果考慮角度分的話再加一個(gè)角度分,最后cost存儲(chǔ)到這條軌跡的traj.cost中。至此,一條路徑的規(guī)劃以及這條路徑的分值就確定下來(lái)了。

4.3.5、軌跡選擇

在上一步驟中我們逐步產(chǎn)生了很多條軌跡,但是這些軌跡中實(shí)際使用的只有一條,怎么區(qū)分出我們需要的這一條軌跡,就是根據(jù)traj.cost參數(shù):

if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
            swap = best_traj;
            best_traj = comp_traj;
            comp_traj = swap;
          }

首先這條軌跡的分?jǐn)?shù)得大于零,這是由于cost中我們根據(jù)它的值是否大于零判斷這條路徑是否會(huì)跟障礙物有沖突,對(duì)于小于零的路徑是走不了的,所以一條合適的路徑必須大于零。其次它的代價(jià)值必須足夠小。代價(jià)值越小,說(shuō)明這條路徑離全局路徑越接近,同時(shí)離障礙物越遠(yuǎn),那么從規(guī)劃的角度類說(shuō)這是一條很合適的路徑。因?yàn)殡x全局路徑越接近說(shuō)明實(shí)際要走的路徑越短,離障礙物越遠(yuǎn)說(shuō)明這條路徑很安全不容易出問(wèn)題。

4.3.6、原地旋轉(zhuǎn)軌跡評(píng)分

在軌跡生成中,還需要考慮一種情況就是只有角速度沒(méi)有線速度的情況,考慮一下這種情況什么時(shí)候會(huì)發(fā)生:

1、當(dāng)TrajectoryPlannerROS中,位置已經(jīng)到達(dá)目標(biāo)(誤差范圍內(nèi)),姿態(tài)已達(dá),則直接發(fā)送0速;姿態(tài)未達(dá),則調(diào)用降速函數(shù)和原地旋轉(zhuǎn)函數(shù),并調(diào)用checkTrajectory函數(shù)檢查合法性,直到旋轉(zhuǎn)至目標(biāo)姿態(tài)。而checkTrajectory函數(shù)調(diào)用的是scoreTrajectory和generateTrajectory,不會(huì)調(diào)用createTrajectory函數(shù),所以,快要到達(dá)目標(biāo)附近時(shí)的原地旋轉(zhuǎn),不會(huì)進(jìn)入到這個(gè)函數(shù)的這部分來(lái)處理。

2、由于局部規(guī)劃器的路徑打分機(jī)制(后述)是:“與目標(biāo)點(diǎn)的距離”和“與全局路徑的偏離”這兩項(xiàng)打分都只考慮路徑終點(diǎn)的cell,而不是考慮路徑上所有cell的綜合效果,機(jī)器人運(yùn)動(dòng)到一個(gè)cell上,哪怕有任何一條能向目標(biāo)再前進(jìn)的無(wú)障礙路徑,它的最終得分一定是要比原地旋轉(zhuǎn)的路徑得分來(lái)得高的。

所以,這里的原地自轉(zhuǎn),是行進(jìn)過(guò)程中的、未達(dá)目標(biāo)附近時(shí)的原地自轉(zhuǎn),并且,是機(jī)器人行進(jìn)過(guò)程中遇到障礙、前方無(wú)路可走只好原地自轉(zhuǎn),或是連原地自轉(zhuǎn)都不能滿足,要由逃逸狀態(tài)后退一段距離,再原地自轉(zhuǎn)調(diào)整方向,準(zhǔn)備接下來(lái)的行動(dòng)。一種可能情況是機(jī)器人行進(jìn)前方遇到了突然出現(xiàn)而不在地圖上的障礙。

在處理這種情況時(shí),由于機(jī)器人原地旋轉(zhuǎn)時(shí)的角速度限制范圍要比運(yùn)動(dòng)時(shí)的角速度限制范圍更嚴(yán)格,底盤無(wú)法處理過(guò)小的原地轉(zhuǎn)速,故要注意處理這層限制。同樣調(diào)用generateTrajectory函數(shù),生成路徑。

//循環(huán)所有角速度
    double heading_dist = DBL_MAX;

    for(int i = 0; i < vtheta_samples_; ++i) {
      //enforce a minimum rotational velocity because the base can't handle small in-place rotations
      //強(qiáng)制最小原地旋轉(zhuǎn)速度
      double vtheta_samp_limited = vtheta_samp > 0 ? max(vtheta_samp, min_in_place_vel_th_)
        : min(vtheta_samp, -1.0 * min_in_place_vel_th_);
      //產(chǎn)生遍歷角速度的路徑
      generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp_limited,
          acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);

      //if the new trajectory is better... let's take it...
      //note if we can legally rotate in place we prefer to do that rather than move with y velocity
      //如果新路徑代價(jià)更小
      if(comp_traj->cost_ >= 0
          && (comp_traj->cost_ <= best_traj->cost_ || best_traj->cost_ < 0 || best_traj->yv_ != 0.0)
          && (vtheta_samp > dvtheta || vtheta_samp < -1 * dvtheta)){
        //獲取新路徑的終點(diǎn)(原地)
        double x_r, y_r, th_r;
        comp_traj->getEndpoint(x_r, y_r, th_r);
        //坐標(biāo)計(jì)算
        x_r += heading_lookahead_ * cos(th_r);
        y_r += heading_lookahead_ * sin(th_r);
        unsigned int cell_x, cell_y;

        //make sure that we'll be looking at a legal cell
        //轉(zhuǎn)換到地圖坐標(biāo)系,判斷與目標(biāo)點(diǎn)的距離
        if (costmap_.worldToMap(x_r, y_r, cell_x, cell_y)) {
          double ahead_gdist = goal_map_(cell_x, cell_y).target_dist;
          //取距離最小的,放進(jìn)best_traj
          if (ahead_gdist < heading_dist) {
            //if we haven't already tried rotating left since we've moved forward
            if (vtheta_samp < 0 && !stuck_left) {
              swap = best_traj;
              best_traj = comp_traj;
              comp_traj = swap;
              heading_dist = ahead_gdist;
            }
            //if we haven't already tried rotating right since we've moved forward
            else if(vtheta_samp > 0 && !stuck_right) {
              swap = best_traj;
              best_traj = comp_traj;
              comp_traj = swap;
              heading_dist = ahead_gdist;
            }
          }
        }
      }

      vtheta_samp += dvtheta;
    }
4.3.7、軌跡判斷

在上述幾種情況下的軌跡都考慮完后需要對(duì)整個(gè)軌跡的結(jié)果進(jìn)行判斷:如果說(shuō)軌跡的cost最終大于0,說(shuō)明找到了一條軌跡,則我們會(huì)返回這條軌跡:

//檢查最優(yōu)軌跡的分?jǐn)?shù)是否大于0
    if (best_traj->cost_ >= 0) {
      // avoid oscillations of in place rotation and in place strafing
      //抑制震蕩影響:當(dāng)機(jī)器人在某方向移動(dòng)時(shí),對(duì)下一個(gè)周期的與其相反方向標(biāo)記為無(wú)效
      //直到機(jī)器人從標(biāo)記震蕩的位置處離開(kāi)一定距離,返回最佳軌跡。
      
      //若軌跡的采樣速度≤0
      if ( ! (best_traj->xv_ > 0)) {
        //若軌跡的角速度<0
        if (best_traj->thetav_ < 0) {
          if (rotating_right) {
            stuck_right = true;
          }
          rotating_right = true;
        } else if (best_traj->thetav_ > 0) {
          if (rotating_left){
            stuck_left = true;
          }
          rotating_left = true;
        } else if(best_traj->yv_ > 0) {
          if (strafe_right) {
            stuck_right_strafe = true;
          }
          strafe_right = true;
        } else if(best_traj->yv_ < 0){
          if (strafe_left) {
            stuck_left_strafe = true;
          }
          strafe_left = true;
        }

        //set the position we must move a certain distance away from
        prev_x_ = x;
        prev_y_ = y;
      }

      double dist = hypot(x - prev_x_, y - prev_y_);
      if (dist > oscillation_reset_dist_) {
        rotating_left = false;
        rotating_right = false;
        strafe_left = false;
        strafe_right = false;
        stuck_left = false;
        stuck_right = false;
        stuck_left_strafe = false;
        stuck_right_strafe = false;
      }

      dist = hypot(x - escape_x_, y - escape_y_);
      if(dist > escape_reset_dist_ ||
          fabs(angles::shortest_angular_distance(escape_theta_, theta)) > escape_reset_theta_){
        escaping_ = false;
      }

      return *best_traj;
    }

若找不到有效軌跡,進(jìn)入逃逸狀態(tài),后退、原地自轉(zhuǎn),若找不到下一步的有效路徑則再后退、自轉(zhuǎn),直到后退的距離或轉(zhuǎn)過(guò)的角度達(dá)到一定標(biāo)準(zhǔn),才會(huì)退出逃逸狀態(tài),重新規(guī)劃前向軌跡。其中再加上震蕩抑制。

vtheta_samp = 0.0;
    vx_samp = backup_vel_;//后退速度
    vy_samp = 0.0;
    generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp,
        acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);

    //if the new trajectory is better... let's take it
    /*
       if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
       swap = best_traj;
       best_traj = comp_traj;
       comp_traj = swap;
       }
       */

    //we'll allow moving backwards slowly even when the static map shows it as blocked
    swap = best_traj;
    best_traj = comp_traj;
    comp_traj = swap;

    double dist = hypot(x - prev_x_, y - prev_y_);
    if (dist > oscillation_reset_dist_) {
      rotating_left = false;
      rotating_right = false;
      strafe_left = false;
      strafe_right = false;
      stuck_left = false;
      stuck_right = false;
      stuck_left_strafe = false;
      stuck_right_strafe = false;
    }

    //only enter escape mode when the planner has given a valid goal point
    if (!escaping_ && best_traj->cost_ > -2.0) {
      escape_x_ = x;
      escape_y_ = y;
      escape_theta_ = theta;
      escaping_ = true;
    }

    dist = hypot(x - escape_x_, y - escape_y_);

    if (dist > escape_reset_dist_ ||
        fabs(angles::shortest_angular_distance(escape_theta_, theta)) > escape_reset_theta_) {
      escaping_ = false;
    }


    //if the trajectory failed because the footprint hits something, we're still going to back up
    if(best_traj->cost_ == -1.0)
      best_traj->cost_ = 1.0;

    return *best_traj;

若后退速度生成的軌跡的終點(diǎn)有效(> -2.0),進(jìn)入逃逸狀態(tài),循環(huán)后退、自轉(zhuǎn),并且記錄下的逃逸位置和姿態(tài),只有當(dāng)離開(kāi)逃逸位置一定距離或轉(zhuǎn)過(guò)一定角度,才能退出逃逸狀態(tài),再次規(guī)劃前向速度。逃逸判斷和震蕩判斷一樣,也已在上面多次執(zhí)行,只要發(fā)布best_traj前就執(zhí)行一次判斷。

至此,4.3節(jié)最優(yōu)的路徑就已經(jīng)得到了,這個(gè)路徑會(huì)被返回到TrajectoryPlanner::findBestPath函數(shù),再由這里返回到TrajectoryPlannerROS::computeVelocityCommands函數(shù)中。

4.4、旋轉(zhuǎn)使機(jī)器人到達(dá)目標(biāo)點(diǎn)

前面4.3雖然講了一大通局部路徑規(guī)劃,但是emmmm其實(shí)這里沒(méi)有用到,對(duì)于這種位置到達(dá)但是角度沒(méi)有到的情況,其實(shí)是通過(guò):

rotateToGoal(global_pose, robot_vel, goal_th, cmd_vel)

來(lái)旋轉(zhuǎn)到最終目標(biāo)點(diǎn)的。這個(gè)函數(shù)的原理是:在達(dá)到目標(biāo)點(diǎn)誤差范圍內(nèi),且速度降至極小后,最后一步的工作是原地旋轉(zhuǎn)至目標(biāo)姿態(tài)。它采用一種類似“反饋控制”的思想,通過(guò)計(jì)算當(dāng)前姿態(tài)與目標(biāo)姿態(tài)的差值,通過(guò)這個(gè)差值來(lái)控制下一步的角速度。

5、如果機(jī)器人還沒(méi)有到達(dá)目標(biāo)點(diǎn)附近

5.1 軌跡生成

如果機(jī)器人還沒(méi)有到達(dá)目標(biāo)點(diǎn),那么這里就很簡(jiǎn)單了,肯定是局部規(guī)劃軌跡,也就是前面的4.3節(jié)的全部?jī)?nèi)容:

	//若未到達(dá)目標(biāo)點(diǎn)誤差范圍內(nèi),調(diào)用TrajectoryPlanner類的updatePlan函數(shù),將global系下的全局規(guī)劃傳入,再調(diào)用findBestPath函數(shù),進(jìn)行局部規(guī)劃,
    //速度結(jié)果填充在drive_cmds中,并得到局部路線plan。
    //如果沒(méi)到目標(biāo)“位置”,更新全局規(guī)劃
    tc_->updatePlan(transformed_plan);

    //compute what trajectory to drive along
    //用當(dāng)前機(jī)器人位姿和速度,計(jì)算速度控制命令
    //【核心:TrajectoryPlanner的findBestPath函數(shù)】
    Trajectory path = tc_->findBestPath(global_pose, robot_vel, drive_cmds);

5.2 最優(yōu)軌跡判斷以及速度獲取

然后判斷代價(jià)值,也就是生成出來(lái)的最優(yōu)的代價(jià)值是否大于零,大于零代表是有效的軌跡,這時(shí)候可以給定速度以及角速度:

	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);

這里的drive_cmds來(lái)自于findBestPath中的返回值:

Trajectory TrajectoryPlanner::findBestPath(const geometry_msgs::PoseStamped& global_pose,
      geometry_msgs::PoseStamped& global_vel, geometry_msgs::PoseStamped& drive_velocities)

而它的計(jì)算結(jié)果其實(shí)是來(lái)自于最優(yōu)軌跡:

//找到代價(jià)最低的軌跡,輸入分別是目前機(jī)器人位置,速度以及加速度限制
    Trajectory best = createTrajectories(pos[0], pos[1], pos[2],
        vel[0], vel[1], vel[2],
        acc_lim_x_, acc_lim_y_, acc_lim_theta_);//生成諸多可能軌跡,選取其中打分最高的。這里也就是最關(guān)鍵的一步。
    ROS_DEBUG("Trajectories created");
	  drive_velocities.pose.position.x = best.xv_;
      drive_velocities.pose.position.y = best.yv_;
      drive_velocities.pose.position.z = 0;
      tf2::Quaternion q;
      q.setRPY(0, 0, best.thetav_);
      tf2::convert(q, drive_velocities.pose.orientation);

在createTrajectories中我們對(duì)每一條軌跡采樣時(shí)都有一個(gè)當(dāng)前軌跡使用的速度以及角速度值,它來(lái)自于generateTrajectory的返回:

traj.resetPoints();
    traj.xv_ = vx_samp;//線速度
    traj.yv_ = vy_samp;
    traj.thetav_ = vtheta_samp;//角速度

所以至此我們就得到了一條最優(yōu)軌跡所需要的速度以及角速度。

5.3 發(fā)布局部路徑

在得到速度后,不僅要發(fā)布速度還要發(fā)布局部路徑點(diǎn)位,前面4.3做最優(yōu)路徑規(guī)劃時(shí)得到了最優(yōu)路徑上的連續(xù)點(diǎn)會(huì)在這里被發(fā)布出來(lái),可以作為可視化的一部分便于調(diào)試,判斷機(jī)器人的運(yùn)動(dòng)是否與規(guī)劃的結(jié)果類似。

總結(jié):move_base的局部路徑規(guī)劃主要執(zhí)行了以下內(nèi)容:首先獲取機(jī)器人的全局坐標(biāo),然后生成局部代價(jià)地圖。根據(jù)全局規(guī)劃的結(jié)果判斷當(dāng)前機(jī)器人是否到達(dá)目標(biāo)點(diǎn),這里分兩種情況考慮:如果位置到達(dá)了但是角度沒(méi)有到達(dá),則會(huì)調(diào)用rotateToGoal函數(shù)生成一個(gè)旋轉(zhuǎn)的角速度使機(jī)器人的朝向最終到達(dá)目標(biāo)點(diǎn);如果機(jī)器人連位置都沒(méi)有到達(dá),則會(huì)調(diào)用findBestPath函數(shù)生成出一條前往目標(biāo)點(diǎn)的最小代價(jià)路徑。最小代價(jià)路徑的生成方式主要是通過(guò)對(duì)機(jī)器人的速度以及角速度分別采樣,再給定一定的采樣周期,判斷這一段時(shí)間內(nèi)的機(jī)器人最終運(yùn)動(dòng)的目標(biāo)點(diǎn)的代價(jià)值是否是所有采樣樣本中最小的,代價(jià)值的計(jì)算公式為:路徑距離+目標(biāo)距離+障礙物代價(jià)(乘以各自的比例)。即這條路徑離目標(biāo)點(diǎn)越近,離全局路徑越近,離障礙物越遠(yuǎn)則它的得分越低,則它是最優(yōu)的一條路徑。然后再將這條采樣路徑的速度角度給到最終的發(fā)布器,就完成了computeVelocityCommands全部工作。

參考
1、Base Local Planner局部規(guī)劃-TrajectoryPlanner源碼解讀-2

https://blog.csdn.net/neo11111/article/details/104713086/

2、MapGridCostFunction文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-636079.html

https://www.cswamp.com/post/113

到了這里,關(guān)于move_base代碼解析(四)局部路徑規(guī)劃:TrajectoryPlannerROS::computeVelocityCommands的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • ROS學(xué)習(xí)筆記08、機(jī)器人導(dǎo)航仿真(slam、map_server、amcl、move_base與導(dǎo)航消息介紹)

    ROS學(xué)習(xí)筆記08、機(jī)器人導(dǎo)航仿真(slam、map_server、amcl、move_base與導(dǎo)航消息介紹)

    馬上開(kāi)學(xué),目前學(xué)校很多實(shí)驗(yàn)室都是人工智能這塊,大部分都是和機(jī)器人相關(guān),然后軟件這塊就是和cv、ros相關(guān),就打算開(kāi)始學(xué)習(xí)一下。 本章節(jié)是虛擬機(jī)安裝Ubuntu18.04以及安裝ROS的環(huán)境。 學(xué)習(xí)教程:【Autolabor初級(jí)教程】ROS機(jī)器人入門,博客中一些知識(shí)點(diǎn)是來(lái)源于趙老師的筆記

    2023年04月12日
    瀏覽(25)
  • 模擬退火-粒子群全局路徑規(guī)劃+DWA局部路徑規(guī)劃

    模擬退火-粒子群全局路徑規(guī)劃+DWA局部路徑規(guī)劃

    整理了一個(gè)路徑規(guī)劃demo,當(dāng)然圖是改進(jìn)的效果?demo分別有對(duì)應(yīng)的開(kāi)源 可以在網(wǎng)上搜到,我覺(jué)得已經(jīng)介紹的很詳細(xì)了,所以不做過(guò)多的解釋,傳送門在下面 ( 寫的不好 輕噴 ) 粒子群本質(zhì)是參數(shù)尋優(yōu)問(wèn)題,也就是說(shuō)在運(yùn)用到路徑規(guī)劃這塊需要對(duì)規(guī)劃的路徑進(jìn)行模型建立,這塊

    2024年02月06日
    瀏覽(24)
  • 【路徑規(guī)劃】局部路徑規(guī)劃算法——人工勢(shì)場(chǎng)法(含python實(shí)現(xiàn) | c++實(shí)現(xiàn))

    【路徑規(guī)劃】局部路徑規(guī)劃算法——人工勢(shì)場(chǎng)法(含python實(shí)現(xiàn) | c++實(shí)現(xiàn))

    路徑規(guī)劃與軌跡跟蹤系列算法 基于改進(jìn)型人工勢(shì)場(chǎng)法的車輛避障路徑規(guī)劃研究 基于改進(jìn)人工勢(shì)場(chǎng)法的車輛避障路徑規(guī)劃研究 1986 年 Khatib 首先提出人工勢(shì)場(chǎng)法,并將其應(yīng)用在機(jī)器人避障領(lǐng)域, 而現(xiàn)代汽車可以看作是一個(gè)高速行駛的機(jī)器人,所以該方法也可應(yīng)用于汽車的避障

    2023年04月09日
    瀏覽(23)
  • 【路徑規(guī)劃】局部路徑規(guī)劃算法——貝塞爾曲線法(含python實(shí)現(xiàn) | c++實(shí)現(xiàn))

    【路徑規(guī)劃】局部路徑規(guī)劃算法——貝塞爾曲線法(含python實(shí)現(xiàn) | c++實(shí)現(xiàn))

    路徑規(guī)劃與軌跡跟蹤系列算法 曲線雜談(二):Bezier曲線的特殊性質(zhì) 貝塞爾曲線的特性總結(jié) 貝塞爾曲線于1962年由法國(guó)工程師皮埃爾·貝塞爾( Pierre Bézier)發(fā)表,他運(yùn)用貝塞爾曲線來(lái)為汽車的主體進(jìn)行設(shè)計(jì)。 貝塞爾曲線是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線,由一組稱為

    2024年02月14日
    瀏覽(158)
  • ros中實(shí)現(xiàn)全局/局部避障算法的添加與rviz中規(guī)劃路徑的顯示(上)

    ros中實(shí)現(xiàn)全局/局部避障算法的添加與rviz中規(guī)劃路徑的顯示(上)

    目錄 前言 一、命令行 二、所用到的launch文件、yaml文件等 1.map1_mrobot_laser_nav_gazebo.launch 2.gmapping_demo.launch 3.gmapping.launch 4.move_base.launch 5.nav03_map_server.launch 6.mrobot_teleop.launch 三、rviz中添加path插件 總結(jié) 最近在做ros相關(guān)的作業(yè),故寫下本文留做參考以便日后再次使用或理解,如有

    2024年02月01日
    瀏覽(21)
  • CBS多機(jī)器人路徑規(guī)劃(Conflict-Based Search)

    CBS多機(jī)器人路徑規(guī)劃(Conflict-Based Search)

    多智能體路徑規(guī)劃 (Multi-Agent Path Finding, MAPF) 研究多智能體的路徑規(guī)劃算法,為多機(jī)系統(tǒng)規(guī)劃無(wú)沖突的最優(yōu)路徑. CBS(Conflict-Based Search) 是一種基于沖突的 MAPF 算法, CBS 算法給出 MAPF 問(wèn)題的 全局最優(yōu) 結(jié)果. 參考文獻(xiàn): Conflict-based search for optimal multi-agent path finding CBS是一種中央規(guī)

    2024年02月02日
    瀏覽(50)
  • 【MATLAB源碼-第64期】matlab基于DWA算法的機(jī)器人局部路徑規(guī)劃包含動(dòng)態(tài)障礙物和靜態(tài)障礙物。

    【MATLAB源碼-第64期】matlab基于DWA算法的機(jī)器人局部路徑規(guī)劃包含動(dòng)態(tài)障礙物和靜態(tài)障礙物。

    動(dòng)態(tài)窗口法(Dynamic Window Approach,DWA)是一種局部路徑規(guī)劃算法,常用于移動(dòng)機(jī)器人的導(dǎo)航和避障。這種方法能夠考慮機(jī)器人的動(dòng)態(tài)約束,幫助機(jī)器人在復(fù)雜環(huán)境中安全、高效地移動(dòng)。下面是DWA算法的詳細(xì)描述: 1. 動(dòng)態(tài)窗口的概念 動(dòng)態(tài)窗口法的核心概念是“動(dòng)態(tài)窗口”,這是

    2024年02月05日
    瀏覽(26)
  • 改進(jìn)的 A*算法的路徑規(guī)劃(路徑規(guī)劃+代碼+教程)

    改進(jìn)的 A*算法的路徑規(guī)劃(路徑規(guī)劃+代碼+教程)

    更多視覺(jué)和自動(dòng)駕駛項(xiàng)目請(qǐng)見(jiàn): 小白學(xué)視覺(jué) 自動(dòng)駕駛項(xiàng)目 自動(dòng)駕駛項(xiàng)目大集合-代碼 近年來(lái),隨著智能時(shí)代的到來(lái),路徑規(guī)劃技術(shù)飛快發(fā)展,已經(jīng)形成了一套較為成熟的理論體系。其經(jīng)典規(guī)劃算法包括 Dijkstra 算法、A 算法、D 算法、Field D 算法等,然而傳統(tǒng)的路徑規(guī)劃算法在

    2024年02月03日
    瀏覽(12)
  • 路徑規(guī)劃算法:基于和聲優(yōu)化的路徑規(guī)劃算法- 附代碼

    路徑規(guī)劃算法:基于和聲優(yōu)化的路徑規(guī)劃算法- 附代碼

    摘要:本文主要介紹利用智能優(yōu)化算法和聲算法來(lái)進(jìn)行路徑規(guī)劃。 和聲算法原理請(qǐng)參考:https://blog.csdn.net/u011835903/article/details/118724731 1.1 環(huán)境設(shè)定 在移動(dòng)機(jī)器人的路徑優(yōu)化中,每個(gè)優(yōu)化算法的解代表機(jī)器人的一條運(yùn)動(dòng)路徑。優(yōu)化算法會(huì)通過(guò)優(yōu)化計(jì)算在眾多路徑中找出一條最

    2024年02月08日
    瀏覽(26)
  • 路徑規(guī)劃算法:基于鴿群優(yōu)化的路徑規(guī)劃算法- 附代碼

    路徑規(guī)劃算法:基于鴿群優(yōu)化的路徑規(guī)劃算法- 附代碼

    摘要:本文主要介紹利用智能優(yōu)化算法鴿群算法來(lái)進(jìn)行路徑規(guī)劃。 鴿群算法原理請(qǐng)參考:https://blog.csdn.net/u011835903/article/details/109774886 1.1 環(huán)境設(shè)定 在移動(dòng)機(jī)器人的路徑優(yōu)化中,每個(gè)優(yōu)化算法的解代表機(jī)器人的一條運(yùn)動(dòng)路徑。優(yōu)化算法會(huì)通過(guò)優(yōu)化計(jì)算在眾多路徑中找出一條最

    2024年02月08日
    瀏覽(22)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包