Navigation原始碼閱讀之base_local_planner
base_local_planner是直接決策小車以何種軌跡執行、下發何種速度的大類,可能由於開發的歷史原因,下面的檔案全部放在這個包裡,顯得有些雜亂,咱們慢慢捋一捋。
首先看看navigation包的nav_core包,nav_core是一個純虛類,裡面對base_local_planner進行了父類的虛擬函式宣告,它使用預設建構函式,initialize函式接受名字、tf、代價地圖進行“構造”,另外三個介面分別是setPlan(用來將全域性路徑傳到區域性規劃器),computeVelocityCommands(計算下發速度),以及isGoalReached(判斷本次導航是否結束)。這就是base_local_planner虛類的宣告。那麼來繼承這個類的,預設是DWAPlannerROS和TrajectoryPlannerROS以pluginlib的形式繼承,而我們一般使用DWA演算法進行速度計算而不是。dwa_local_planner已經作為一個獨立的包放在外面,這一塊就不再贅述。
我們從DWA演算法開始,依次看看呼叫的base_local_planner的函式流程:
一、從DWAPlannerROS::computeVelocityCommands開始:
1.呼叫了getLocalPlan函式,這個函式是LocalPlannerUtil::getLocalPlan,這個類的作用大概是對發過來的全域性路徑進行tf上的轉化以及裁剪,它依次呼叫的兩個函式均位於goal_functions.cpp中,這個檔案下全是工具函式,不從屬於類。下列均為工具函式:
(1)publishPlan這個函式是將區域性路徑發給rviz,我們在rviz中可以同時觀看兩個nav_msgs/Path格式的topic(全域性、區域性)。
(2)prunePlan將全域性路徑裁剪成前方一小段區域性路徑,採用的方法是用迭代器依次判斷與當前位置的距離,小於某距離時跳出迭代器迴圈,將路徑其他部分剔除。這個方法非常暴力,預設的前方距離是1m,這個數字不是可配置的rosparam,讓人費解。
(3)transformGlobalPlan是利用計算區域性代價地圖的範圍,將範圍之外的剔除,在區域性代價地圖內的路徑點的frame轉化為global_frame。
double dist_threshold = std::max(costmap.getSizeInCellsX() * costmap.getResolution() / 2.0, costmap.getSizeInCellsY() * costmap.getResolution() / 2.0);
(4)getGoalPose是將全域性路徑的最後一個元素取出,利用tf中的poseStampedMsgToTF獲取goal_pose的各引數,tf的具體操作方法我們之後再分析。
(5)isGoalReached是判斷兩個因素,一個是位置是否足夠接近終點(容忍度之內),一個是車是否停下(按照里程計反饋)。
2.回到computeVelocityCommands,它接著呼叫了DWAPlanner::updatePlanAndLocalCosts函式,而這個函式是依靠那幾個打分項活著的,檢查一下就可以發現該函式中出現的打分項有:path_costs_,goal_costs_,goal_front_costs_,alignment_costs_,一直在圍繞這些打分項做文章。這幾個打分項,其實都屬於base_local_planner::MapGridCostFunction類。轉到map_grid_cost_function.cpp,這裡詳細講述了打分前的準備工作。
(1)在updatePlanAndLocalCosts函式中呼叫的setTargetPoses介面其實就是將外部處理好的區域性路徑傳到成員變數而已,其他什麼都不做。
(2)其他成員函式都呼叫了MapGrid的物件,先轉到map_grid.cpp看看它的實現方式:
①MapGrid反覆使用了裝有MapCell的vector,再轉到map_cell.cpp(這呼叫層級有點厚)~
private:
std::vector<MapCell> map_; ///< @brief Storage for the MapCells
②MapCell這個類是真的皮,預設建構函式和複製建構函式啥都不做,但是初始化列表裡說明了地圖體素距小車無限遠,暫時先這麼初始化吧。
MapCell::MapCell()
: cx(0), cy(0),
target_dist(DBL_MAX),
target_mark(false),
within_robot(false)
{}
③回到MapGrid類,commonInit函式是將整個柵格地圖初始化(其實就是將每個體素塞進vector都標好位置),而getIndex函式就正好是找到vector的下標。sizeCheck函式是檢查柵格地圖是否填充正確,不對就再來一遍。
④MapGrid::updatePathCell是為了檢查小車前方的check_cell和目前位於的current_cell的距離,如果檢查出這個cell是障礙物那麼距離設為障礙物的型別。
unsigned char cost = costmap.getCost(check_cell->cx, check_cell->cy);
if(! getCell(check_cell->cx, check_cell->cy).within_robot &&
(cost == costmap_2d::LETHAL_OBSTACLE ||
cost == costmap_2d::INSCRIBED_INFLATED_OBSTACLE ||
cost == costmap_2d::NO_INFORMATION))
{
check_cell->target_dist = obstacleCosts();
return false;
}
⑤MapGrid::adjustPlanResolution是為了根據地圖以及代價地圖的解析度對全域性路徑進行填充,防止路徑點過於稀疏。雖然函式看起來很花哨,但是仔細看就很容易發現意圖。
⑥MapGrid::setTargetCells是為了將global_plan經過的cells儲存佇列path_dist_queue中,這個佇列每當經過一個體素(隊頭)就刪除之,把新的體素塞在隊尾。MapGrid::setLocalGoal函式也類似,用來判斷小車下一時刻將通過哪個體素。
⑦MapGrid::computeTargetDistance函式是這個類的關鍵,它把區域性地圖裡的所有體素均和一個佇列中的元素(其實是路徑點)進行距離計算,但是沒有返回任何物件,有點費解。。。
(3)回到map_grid_cost_function.cpp,很多函式就容易理解了。MapGridCostFunction::scoreTrajectory是為了判斷路徑點前方的體素的評分,如果有錯誤返回負數。一個需要注意的點是,aggregationType_是一個列舉,它分別代表返回最後一點的值(如果沒有處於碰撞危險中)、所有點的和,或者所有非零點的乘積。
3.那麼再回到DWAPlannerROS::computeVelocityCommands,接下來呼叫了LatchedStopRotateController的物件,轉到latched_stop_rotate_controller.cpp分析它的實現:
(1)LatchedStopRotateController::stopWithAccLimits以及rotateToGoal中,加速度的上下限是可配置引數,它們是在一個模擬時間週期內計算出所需的線速度與角速度。
(2)至於具體實現的computeVelocityCommandsStopRotate,是通過里程計讀數的類物件odom_helper_的幫助來判斷小車是否停止旋轉。
①轉到odom_helper_ros.cpp,可以看到它是很簡單的通過接收base_controller的里程計讀數,值得注意的是訊息佇列只有一位,這意味著收一個就扔一個~
odom_sub_ = gn.subscribe<nav_msgs::Odometry>( odom_topic_, 1, boost::bind( &OdometryHelperRos::odomCallback, this, _1 ));
二、接著從computeVelocityCommands呼叫DWAPlannerROS::dwaComputeVelocityCommands,再接著呼叫DWAPlanner::findBestPath:
(1)obstacle_costs_是ObstacleCostFunction的物件,進入obstacle_cost_function.cpp,發現它的建構函式上來就要構建一個costmapModel,不明覺厲啊,趕緊轉到costmap_model.cpp:
①在標頭檔案中,我們可以看到footprintCost函式,看過costmap層級定義的朋友明白這些形參其實就是指小車位置、車輪數(圓形或方形)、內切圓半徑、外接圓半徑。
virtual double footprintCost(const geometry_msgs::Point& position, const std::vector<geometry_msgs::Point>& footprint,
double inscribed_radius, double circumscribed_radius);
而這個函式作用是判斷小車模型(圓或者方)最大可能的地圖代價。
②CostmapModel::lineCost是用一條線來檢查小車輪廓邊緣是否侵犯了代價地圖。
(2)ObstacleCostFunction::scoreTrajectory和前面的MapGrid類似,都是錯誤返回負數,並且正確情況分為最後一點的值與所有點的和。getScalingFactor是為了限制小車速度太大。
(3)回到DWAPlanner::findBestPath,設定好obstacle_costs之後,用一個SimpleTrajectoryGenerator初始化計算函式,轉到simple_trajectory_generator.cpp:
①SimpleTrajectoryGenerator::initialise其實就是藉助可配置引數,設定一個可選速度區間,用std::vector<Eigen::Vector3f>儲存。
②SimpleTrajectoryGenerator::generateTrajectory創造了一個時間微分,通過位置與速度計算函式,得到新的軌跡。這時候看一下trajectory.cpp,發現base_local_planner::Trajectory其實就是由三個vector組成,分別儲存X,Y和點之間的夾角。
③SimpleTrajectoryGenerator::computeNewPositions和computeNewVelocities函式均是通過速度區間求解下一個速度與位置。
(4)回到DWAPlanner::findBestPath,接著是SimpleScoredSamplingPlanner類物件scored_sampling_planner_,轉到simple_scored_sampling_planner.cpp,findBestTrajectory是對外呼叫的介面,它通過遍歷all_explored這個儲存Trajectory的vector來尋找最優解,那麼評分的標準在SimpleScoredSamplingPlanner::scoreTrajectory中:
①scoreTrajectory在軌跡上執行所有評分函式,建立正成本的加權和,一旦發現負成本或負成本之和大於正的._traj_cost累積值,就中止。
三、執行到這裡,結果就很明確了,由於result_traj_是由三個vector組成,那分別取其中的xv_,yv_,thetav_,就可以得到本次週期所需要的速度了。當然用一個tf::Pose來返回這個cmd_vel的數值,也是很費解了,明明可以用一個matrix更便於理解,當然可能穩定性稍差。
這就是move_base——dwa_local_planner——base_local_planner的流程,用多層級、分散式的軟體架構實現了導航演算法~
相關推薦
Navigation原始碼閱讀之base_local_planner
base_local_planner是直接決策小車以何種軌跡執行、下發何種速度的大類,可能由於開發的歷史原因,下面的檔案全部放在這個包裡,顯得有些雜亂,咱們慢慢捋一捋。 首先看看navigation包的nav_core包,nav_core是一個純虛類,裡面對base_loc
Navigation原始碼閱讀之dwa_local_planner(DWA動態視窗法)
DWAPlannerROS是封裝類,提供了與move_base的介面,而DWAPlanner是具體實現類,它非常依賴costmap(當然不指望讓小車動態避障的話就無所謂了),因此我們在使用時需要保證代價地圖的膨脹度以及實時更新頻率。btw:在此類程式碼中,基本上下反覆使用的變
Promise原始碼閱讀之建構函式+then過程
前言 Promise是非同步程式設計的一種方案,ES6規範中將其寫入規範標準中,統一了用法。 考慮到瀏覽器的相容性,Vue專案中使用promise,就具體閱讀promise原始碼,看看內部的具體實現。 具體分析 通過具體例項來閱讀promise原始碼的實現,例項如下: new
Netty 原始碼閱讀之初始環境搭建
推薦 netty 系列原始碼解析合集 http://www.iocoder.cn/Netty/Netty-collection/?aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R6c18xMDQxMjE4MTI5L2FydGljbGUvZGV0YWlscy83OD
jdk原始碼閱讀之——arraylist
首先看一下他的建構函式: public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 其實arraylist還有其他的建構函式,可以指定陣列的長度,這裡先從最基本的入
netty原始碼閱讀之效能優化工具類之Recycle異執行緒獲取物件
在這篇《netty原始碼閱讀之效能優化工具類之Recycler獲取物件》文章裡面,我們還有一個scavenge()方法沒有解析,也就是在別的執行緒裡面回收物件。下面我們開始介紹,從這個方法開始進入: boolean scavenge() { // con
我的原始碼閱讀之路:redux原始碼剖析
前言 用過react的小夥伴對redux其實並不陌生,基本大多數的React應用用到它。一般大家用redux的時候基本都不會單獨去使用它,而是配合react-redux一起去使用。剛學習redux的時候很容易弄混淆redux和react-redux,以為他倆是同一個
netty原始碼閱讀之解碼值基於固定長度解碼器分析
固定長度解碼器FixedLengthFrameDecoder比較簡單,我們看下它類的註釋: /** * A decoder that splits the received {@link ByteBuf}s by the fixed number * of bytes.
netty原始碼閱讀之解碼之基於長度域解碼器引數分析
這篇文章我們放鬆一點,只分析基於長度域解碼器的幾個引數, lengthFieldOffset :長度域的偏移量,也就是長度域要從什麼地方開始 lengthFieldLength:長度域的長度,也就是長度域佔多少個位元組 lengthAdjustment:長度域的值的調整
netty原始碼閱讀之解碼之基於長度域解碼器分析
基於長度域解碼器LengthFieldBasedFrameDecoder我們主要分析以下三點: 1、計算需要抽取的資料包的長度 2、跳過位元組邏輯處理 3、丟棄模式下的處理 首先原始碼還是LengthFieldBasedFrameDecoder的decode方法:
netty原始碼閱讀之編碼之MessageToByteEncoder
MessageToByteEncoder的write過程,我們分析以下幾步: 1、匹配物件 2、分配記憶體 3、編碼實現 4、釋放物件 5、傳播資料 6、釋放記憶體 原始碼在這裡: @Override public void write(Cha
netty原始碼閱讀之效能優化工具類之FastThreadLocal的使用
先說明FastThreadLocal使用的效果。 1、比jdk原生的ThreadLocal的快 2、不同執行緒之間能保證執行緒安全 這是我們的使用者程式碼: public class FastThreadLocalTest { private static F
netty原始碼閱讀之效能優化工具類之FastThreadLocal的建立
建立的話我們直接從FastThreadLocal的構造方法進入: public FastThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex(); } 可見他是現
JAVA原始碼閱讀之java.util—List
List List被宣告為一個介面,程式碼量很少,只聲明瞭方法。 public interface List<E> extends Collection<E> { int size(); boolean isEmpty(); boo
netty原始碼閱讀之效能優化工具類之Recycler獲取物件
Recycler獲取物件主要分為以下幾部分: 1、獲取當前執行緒的Stack 2、從Stack裡面彈出物件 3、如果彈出物件為空,那就建立物件並且繫結到Stack裡面 我們從Recycler的get方法進入,就是這個原始碼: @SuppressWarnin
jdk原始碼閱讀之Object類
Object的作用 Object是java所有類的基類,定義了所有類的基礎方法 。這個類所定義的方法也不多,大部分是native方法。 什麼是native方法 native關鍵字標識的java方法為本地方法,底層是有c/c++編寫的程式編譯後dll檔案,jav
JDK原始碼閱讀之ArrayList
ArrayList簡介 List 介面的大小可變陣列的實現。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 介面外,此類還提供一些方法來操作內部用來儲存列表的陣列的大小。(此類大致上等同於 Vector 類,除了此類是不同步
[kafka掃盲]---(7)kafka原始碼閱讀之生產者客戶端緩衝池
Author:趙志乾 Date:2018-10-21 Declaration:All Right Reserved!!! BufferPool.java 1、檔案位置: 該檔案在原始碼中的位置:kafka-2.0.0-src/clients/src/main/java/
JDK原始碼閱讀之HashMap
HashMap簡介 基於雜湊表的 Map 介面的實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。(除了非同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證對映的順序,特別是它不保證該
原始碼閱讀之Ordering
注重greatestOf 的演算法實現 舉個栗子: List<Integer> listInt = Lists.newArrayList(4, 2, 0, 1, 3); List<String> listString = Lists.newArrayList("abc