48.在ROS中實現(xiàn)local planner(1)- 實現(xiàn)一個可以用的模板

有了之前45.在ROS中實現(xiàn)global planner(1)- 實現(xiàn)一個可以用模板global planner的經驗, 現(xiàn)在再去創(chuàng)建一個local planner的包就容易多了

1. 創(chuàng)建包

  • 創(chuàng)建
cd ~/pibot_ros/ros_ws/src  # 這里可以使用自己的ros workspace
catkin_create_pkg sample_local_planner
  • 添加類
    我們需要實現(xiàn)一個從nav_core::BaseLocalPlanner繼承的類, nav_core::BaseLocalPlanner接口類定義在這里base_local_planner.h#L50)可以看到

  • 修改編譯
    修改CMakeLists.txt,添加相關編譯參數和選項

  • 添加bgp_plugin.xml文件
    指定導出的類名稱

<library path="lib/libsample_local_planner">
  <class name="sample_local_planner/LocalPlanner" type="sample_local_planner::LocalPlanner" base_class_type="nav_core::BaseLocalPlanner">
    <description>
      A sample implementation of a grid local planner 
    </description>
  </class>
</library>

目錄結構這樣

? tree sample_local_planner
sample_local_planner
├── bgp_plugin.xml
├── CMakeLists.txt
├── include
│   └── sample_local_planner
│       └── planner_node.h
├── package.xml
└── src
    └── planner_node.cpp
PLUGINLIB_EXPORT_CLASS(sample_local_planner::LocalPlanner, nav_core::BaseLocalPlanner)

2. 接口實現(xiàn)

2.1 接口

base_local_planner.h#L50)可以看到接口類

namespace nav_core {
  class BaseLocalPlanner{
    public:
      virtual bool computeVelocityCommands(geometry_msgs::Twist& cmd_vel) = 0;
      virtual bool isGoalReached() = 0;
      virtual bool setPlan(const std::vector<geometry_msgs::PoseStamped>& plan) = 0;
      virtual void initialize(std::string name, tf2_ros::Buffer* tf, costmap_2d::Costmap2DROS* costmap_ros) = 0;
  };
};  // namespace nav_core

通過命名大概就知道其定義邪驮,

  • initialize
    初始化接口,給我們傳相關功能接口的,如tf妓羊, costmap
  • setPlan
    規(guī)劃控制接口,給我們提供一個plan续崖,這個應該是global planner的輸出者甲,通過move_base轉了一手給到我們,后面可以看下move_base源碼
  • computeVelocityCommands
    計算速度喉钢,傳入的參數是一個引用,應該是輸出函數良姆,我們把計算好的速度填進去就可以
  • isGoalReached
    獲取是否以及到達目標點

2.2 不同ros版本接口差異

BaseLocalPlannerros kinetic 中的initialize接口稍有差異 見base_local_planner.h#L78


// kinetic
virtual void initialize(std::string name, tf::TransformListener* tf, costmap_2d::Costmap2DROS* costmap_ros) = 0;

// melodic&noetic
virtual void initialize(std::string name, tf2_ros::Buffer* tf, costmap_2d::Costmap2DROS* costmap_ros) = 0;

后面我們以melodic&noetic實現(xiàn)

2.3 實現(xiàn)

主要代碼如下肠虽,stopwatch_為計時器,我們在setPlan調用后玛追,設置變量税课,computeVelocityCommands接口中設置固定的速度,在時間到達后痊剖,輸出0韩玩,同時isGoalReached接口返回true

void LocalPlanner::initialize(std::string name, tf::TransformListener *tf,
                                  costmap_2d::Costmap2DROS *costmap_ros)
    {
        ROS_INFO("LocalPlanner initialize");
    }

    bool LocalPlanner::computeVelocityCommands(geometry_msgs::Twist &cmd_vel)
    {
        ROS_INFO("LocalPlanner computeVelocityCommands");
        if (start_flag_) {
            cmd_vel.linear.x = 0.2;
            cmd_vel.linear.y = 0;
            cmd_vel.angular.z = 0.8;
        } else {
            cmd_vel.linear.x = 0;
            cmd_vel.linear.y = 0;
            cmd_vel.angular.z = 0;
        }
        
        return true;
    }

    bool LocalPlanner::setPlan(const std::vector<geometry_msgs::PoseStamped> &orig_global_plan)
    {
        ROS_INFO("LocalPlanner setPlan");

        if (!start_flag_) {
            start_flag_ = true;
            stopwatch_.reset();
        }

        return true;
    }

    bool LocalPlanner::isGoalReached()
    {
        if (stopwatch_.elapsed(std::chrono::seconds(2)))
        {
            ROS_INFO("LocalPlanner GoalReached");
            return true;
        }

        return false;
    }

通過查看move_base源碼,上面幾個接口是在同一個線程被調用陆馁,所有后續(xù)不需要考慮資源競爭找颓,即變量無需加鎖

3. 測試

3.1 編譯

cd ~/pibot_ros/ros_ws
catkin_make

3.2 測試

修改~/pibot_ros/src/pibot_simulator/move_base_params.yaml

# base_local_planner: "dwa_local_planner/DWAPlannerROS"
base_local_planner: sample_local_planner/LocalPlanner

dwa_local_planner/DWAPlannerROS ----> sample_local_planner/LocalPlanner

  • 啟動模擬器
pibot_simulator
  • 查看當前的local_planner
? rosparam get /move_base/base_local_planner
sample_local_planner/LocalPlanner  # 輸出sample_local_planner/LocalPlanner表示插件已經被正確加載
  • 啟動rviz發(fā)送點位,選點導航測試
pibot_view

3.3 測試結果

[ INFO] [1676647988.863610652]: make plan start:[0.000000 0.000000], goal:[-2.986773 4.282055]
[ INFO] [1676647989.063781836]: LocalPlanner setPlan
[ INFO] [1676647989.064015702]: LocalPlanner computeVelocityCommands
[ INFO] [1676647989.263707871]: LocalPlanner computeVelocityCommands
[ INFO] [1676647989.463771479]: LocalPlanner computeVelocityCommands
[ INFO] [1676647989.663754028]: LocalPlanner computeVelocityCommands
[ INFO] [1676647989.863583610]: LocalPlanner computeVelocityCommands
[ INFO] [1676647989.864067517]: make plan start:[0.000000 0.000000], goal:[-2.986773 4.282055]
[ INFO] [1676647990.063701815]: LocalPlanner setPlan
[ INFO] [1676647990.063874092]: LocalPlanner computeVelocityCommands
[ INFO] [1676647990.263710418]: LocalPlanner computeVelocityCommands
[ INFO] [1676647990.463773749]: LocalPlanner computeVelocityCommands
[ INFO] [1676647990.663630163]: LocalPlanner computeVelocityCommands
[ INFO] [1676647990.863635728]: LocalPlanner computeVelocityCommands
[ INFO] [1676647990.864087581]: make plan start:[0.000000 0.000000], goal:[-2.986773 4.282055]
[ INFO] [1676647991.063713670]: LocalPlanner setPlan
[ INFO] [1676647991.063894899]: LocalPlanner computeVelocityCommands
[ INFO] [1676647991.263639509]: LocalPlanner GoalReached

通過日志可以看出

  • 在全局規(guī)劃(make plan start是我們前面文章新增的astar planner輸出)后LocalPlanner的接口setPlan被調用
  • computeVelocityCommands函數沒0.2s被調用一次, 期間機器人也在做圓周運動
  • 全局規(guī)劃再次被調用(move_bsae里配置了規(guī)劃頻率1hz叮贩,這里可以看到間隔1s全局規(guī)劃一次)击狮,重復前面的
  • 直到超時GoalReached返回true完成

4. 總結

本文簡單實現(xiàn)了一個local planner的插件佛析,顯然實際沒啥用,不過可以作為一個模板彪蓬,基于該模板實現(xiàn)自己的算法寸莫。后面我們將基于該模板實現(xiàn)可用的局部規(guī)劃控制。

本文代碼見sample_local_planner

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末档冬,一起剝皮案震驚了整個濱河市膘茎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捣郊,老刑警劉巖辽狈,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呛牲,居然都是意外死亡刮萌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門娘扩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來着茸,“玉大人,你說我怎么就攤上這事琐旁$员#” “怎么了身坐?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我扩氢,道長劈猪,這世上最難降的妖魔是什么捡需? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任愈魏,我火速辦了婚禮,結果婚禮上掰伸,老公的妹妹穿的比我還像新娘皱炉。我一直安慰自己,他們只是感情好狮鸭,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布合搅。 她就那樣靜靜地躺著,像睡著了一般歧蕉。 火紅的嫁衣襯著肌膚如雪灾部。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天惯退,我揣著相機與錄音赌髓,去河邊找鬼。 笑死,一個胖子當著我的面吹牛春弥,可吹牛的內容都是我干的。 我是一名探鬼主播叠荠,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼匿沛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了榛鼎?” 一聲冷哼從身側響起逃呼,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎者娱,沒想到半個月后抡笼,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡黄鳍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年推姻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片框沟。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡藏古,死狀恐怖,靈堂內的尸體忽然破棺而出忍燥,到底是詐尸還是另有隱情拧晕,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布梅垄,位于F島的核電站厂捞,受9級特大地震影響,放射性物質發(fā)生泄漏队丝。R本人自食惡果不足惜靡馁,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炭玫。 院中可真熱鬧奈嘿,春花似錦、人聲如沸吞加。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衔憨。三九已至叶圃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間践图,已是汗流浹背掺冠。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人德崭。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓斥黑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眉厨。 傳聞我的和親對象是個殘疾皇子锌奴,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內容