LoginSignup
15
13

More than 3 years have passed since last update.

ROS講座116 navigationを実装する

Last updated at Posted at 2020-04-24

環境

この記事は以下の環境で動いています。

項目
CPU Core i5-8250U
Ubuntu 16.04
ROS Kinetic
Gazebo 7.14.0

インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。

概要

move_baseはパラメーター設定だけで簡単に使えてお手軽ですが、どうしても細部の動作をカスタムしたくなります。
Navigationの動作はアプリケーションによるので、plannerの入れ替えやパラメーターの変更だけでは対応しきれません。
そこでここではmove_base相当のプログラムを実際に実装してみましょう。move_baseはコードが1000行程度と長く複雑なことをしていそうなのですが、プラグインの処理やrecover動作を抜いた一通り動くだけの部分なら以下のように100行程度で実装できてしまいます。
plannerの難しいアルゴリズムはうまくクラスに押し込まれているので、ただ賢く考えて答えを返してくれる関数程度の扱いですみます。

ソースコード

  • move_baseと同様にglobal_costmap、local_costmap、global_planner、local_plannerを持ちますが、recovery_behaviorは持ちません。
  • 外部からPoseStamped型のトピックを受けると、現在位置からゴールのglobal_planを一回だけ解き、そのあと5Hzでlocal_planを解いていきます。異常などの処理はありません。
  • move_baseのデフォルトに則りglobal_plannerはnavfn、local_plannerはbase_local_plannerを使います。
nav_lecture/src/move_navigation.cpp
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <costmap_2d/costmap_2d_ros.h>
#include <base_local_planner/trajectory_planner_ros.h>
#include <navfn/navfn_ros.h>

enum class NavState{
  STANDBY,
  WAIT_PLAN,
  MOVING
};

class MoveNavigation {
public:
  MoveNavigation() : global_costmap_("global_costmap", tf_), local_costmap_("local_costmap", tf_) {
    twist_pub_ = nh_.advertise<geometry_msgs::Twist>("/dtw_robot1/diff_drive_controller/cmd_vel", 10);
    goal_sub_ = nh_.subscribe("/move_base_simple/goal", 10, &MoveNavigation::goalCallback, this);
    global_planner_.initialize("global_planner", &global_costmap_);
    local_planner_.initialize("local_planner", &tf_, &local_costmap_);
    nav_state_ = NavState::STANDBY;
    timer_ = nh_.createTimer(ros::Duration(0.2), &MoveNavigation::timerCallback, this);
  }

  void goalCallback(const geometry_msgs::PoseStamped msg){
    ROS_INFO("recieve");
    last_pose_ = msg;
    nav_state_ = NavState::WAIT_PLAN;
  }

  void timerCallback(const ros::TimerEvent& e){
    if (nav_state_ == NavState::WAIT_PLAN){
      ROS_INFO("PLAN");

      geometry_msgs::PoseStamped source_pose;
      source_pose.header.frame_id="dtw_robot1/base_link";
      source_pose.header.stamp=ros::Time(0);
      source_pose.pose.orientation.w=1.0;

      geometry_msgs::PoseStamped target_pose;
      std::string target_frame="dtw_robot1/map";
      try{
        tf_.waitForTransform(source_pose.header.frame_id, target_frame, ros::Time(0), ros::Duration(1.0));
        tf_.transformPose(target_frame, source_pose, target_pose);
        ROS_INFO("x:%+5.2f, y:%+5.2f,z:%+5.2f",target_pose.pose.position.x,target_pose.pose.position.y,target_pose.pose.position.z);
      }
      catch(...){
        ROS_INFO("tf error");
      }
      geometry_msgs::PoseStamped start = target_pose;

      if (!global_planner_.makePlan(start, last_pose_, last_global_plan_)){
        ROS_WARN("global plan fail");
        nav_state_ = NavState::STANDBY;
        return;
      }
      local_planner_.setPlan(last_global_plan_);
      geometry_msgs::Twist cmd_vel;
      if (local_planner_.isGoalReached()){
        ROS_INFO("reach");
        twist_pub_.publish(cmd_vel);
        nav_state_ = NavState::STANDBY;
        return;
      }
      local_planner_.computeVelocityCommands(cmd_vel);
      twist_pub_.publish(cmd_vel);
      nav_state_ = NavState::MOVING;
    }
    else if(nav_state_ == NavState::MOVING){
      ROS_INFO_THROTTLE(2.0, "MOVING");
      geometry_msgs::Twist cmd_vel;
      if (local_planner_.isGoalReached()){
        ROS_INFO("reach");
        twist_pub_.publish(cmd_vel);
        nav_state_ = NavState::STANDBY;
        return;
      }
      local_planner_.computeVelocityCommands(cmd_vel);
      twist_pub_.publish(cmd_vel);
    }
  }
  ros::NodeHandle nh_;
  tf::TransformListener tf_;
  ros::Publisher twist_pub_;
  ros::Subscriber goal_sub_;
  ros::Timer timer_;

  NavState nav_state_;
  geometry_msgs::PoseStamped last_pose_;
  std::vector<geometry_msgs::PoseStamped> last_global_plan_;

  costmap_2d::Costmap2DROS global_costmap_;
  costmap_2d::Costmap2DROS local_costmap_;
  navfn::NavfnROS global_planner_;
  base_local_planner::TrajectoryPlannerROS local_planner_;
};

int main(int argc, char** argv){
  ros::init(argc, argv, "move_navigation");
  MoveNavigation move_navigatoion;
  ros::spin();
  return 0;
}
  • ノードの動きをNavStateで管理しています。
    • 起動直後はSTANDBY
    • goalがセットされるとWAIT_PLAN
    • 上記のちに最初のtimer_loopでglobal_planが行われ、local_planの実行中はMOVING
    • local_planが終わるとSTANDBYに戻る
  • global_planでは開始点と終了点が必要です。終了点は外部から与えられますが、開始点は自分で決める必要があります。大体は今の現在位置を入れればよいでしょう。
    • makePlan()で開始・終了点をつなぐ点列を得ます。
  • local_planでは最初にglobal planの出力の点列をセットして、その後は一定周期で関数を呼び速度指示値を得ます。
    • setPlan()でglobal planの出力をセットします。
    • computeVelocityCommands()で現在の速度指値を得ます。
    • isGoalReached()でゴールに到着したかを判定します。

ビルド

cd ~/catkin_ws
catkin_make

実行

実行
roslaunch nav_lecture move_navigation.launch 

move_navigation.gif

Rvizの2DNavGoalを使ってPoseStampedトピックを投げるとそこへ移動します。

参考

move_base

目次ページへのリンク

ROS講座の目次へのリンク

15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13