1. 程式人生 > >ROS slam gmapping不能畫地圖

ROS slam gmapping不能畫地圖

    最後啟動KobukiRos物件的update()函式,用於檢測底盤的各種狀態,比如底盤的連線狀態,電池狀態,輪子是否接觸地面等等。

Turtlebot的執行機制總結如下:

    啟動minimal.launch之後,啟動Kobuki_nodelet節點,新建兩個類物件,一是用於底盤和ROS通訊類KobukiRos(橋樑),一個是底盤的驅動類Kobuki(心臟)。

通訊類中定義了用於接收ROS命令話題的接收者,接收如/cmd_vel等話題。接收者接收到話題訊息後,通過回撥函式,將ROS通用格式的資料轉換為底盤可用格式的資料,並呼叫驅動類的函式介面,通過串列埠給底盤單片機發送資料,實現對底盤的操控。

通訊類中還定義了用於釋出底盤狀態話題的釋出者,如傳送/sensors/imu_data的話題。通訊類通過驅動類的函式介面獲得原始感測器資料,將其轉換為ROS通用資料格式,由釋出者傳送給ROS上層,來實現反饋功能。

最後,Kobuki_nodelet啟動了一套完整的監測機制,用於監測底盤的各種異常狀態。

參考原始碼:

kobuki_nodelet.cpp:(實現Kobuki_nodelet類)

kobuki_ros.hpp:

kobuki_ros.cpp:(實現KobukiRos類,相當於橋樑)

subscriber_callbacks.cpp:(實現回撥函式,ROS資料格式和底盤資料格式的轉換)

kobuki.hpp:

kobuki.cpp:(實現Kobuki類,驅動類,相當於心臟)

詳細跳轉機制:(以/cmd_vel為例)

在Kobuki_nodelet.cpp中,首先新類KobukiRos物件。然後呼叫KobukiRos的init()初始化函式,在這個函式中,聲明瞭許多釋出者和訂閱者,下面以/cmd_vel話題為例,繼續深入。

    velocity_command_subscriber= nh.subscribe(std::string("commands/velocity"), 10,&KobukiRos::subscribeVelocityCommand, this);

    當有/cmd_vel話題釋出時,這個釋出者的回撥函式自動被呼叫,即進入KobukiRos::subscribeVelocityCommand()中。

    在回撥函式中,其將資料傳送給驅動類Kobuki的函式中:

    kobuki.setBaseControl這個函式通過呼叫diff_driver類(差速輪驅動)的setVelocityCommands()函式,將/cmd_vel話題的Twist型別資料轉換為左右輪速,並儲存在vector容器中。

    注意,diff_driver::setVelocityCommands()並沒有直接將左右輪速通過串列埠傳送給底盤,而是儲存在vector<double>型別的私有成員point_velocity之中。

    為什麼沒有直接通過串列埠傳輸呢,那傳輸步驟在哪呢?我們接著來看,以上的步驟其實僅僅是一個執行緒,我們再來看看另外的執行緒。

    繼續回到KobukiRos的init()初始化函式,除了聲明瞭釋出者和訂閱者用於接收發布資料,同時也呼叫了驅動類Kobuki的init()初始化函式。

    在驅動類Kobuki的init()初始化函式中,初始了串列埠,並設定了一些必要資訊,最後進入了Kobuki::spin()迴圈函式。

    在spin()函式中,首先判斷了是否打開了串列埠,如果沒開啟就開啟並設定串列埠。確認完畢之後,先做讀操作,然後做寫操作。在寫操作中,呼叫了sendBaseControlCommand(),在這個函式中,先呼叫了上面講過的diff_driver::velocityCommands()函式,將轉換好的資料設定在Kobuki類的vector<double>型別的私有成員velocity_commands中。最後呼叫sendCommand()函式,終於在裡面看到了串列埠寫操作的相關程式碼!!!

4.運動規劃 (Motion Planning): MoveIt! 與 OMPL

最近有不少人詢問有關MoveIt!與OMPL相關的話題,但是大部分問題都集中於XXX功能怎麼實現,XXX錯誤怎麼解決。表面上看,解決這些問題的方法就是提供正確的程式碼,正確的編譯方法,正確的執行步驟。 

然而,這種解決方法只能解決這個特定的問題,而且解決之後我們也無法學到一些實際的東西。要想徹底明白,需要從源頭入手,也就是說,不要問“MoveIt! 怎麼把機械手從空間一個點移到另一個點?“而是要問”MoveIt!  為什麼能把機械手從空間一個點移到另一個點?“這一點明白之後,遇到類似的問題,才能從容應對。同理,這不僅適用於MoveIt,也同樣適用於其他任何ROS功能。 

所以,下文中我們會見到一些具體的例子,但整體上,更傾向於巨集觀的概念和一些基礎的方法,希望對大家能有所幫助。這裡的幫助指的是增強對運動規劃和Moveit, OMPL的整體理解,而非侷限於完成某一個功能,編譯執行某一個檔案。 


我嘗試用最簡單,最通俗的表達方式來解釋這些問題,其中不免會有一些學術上的錯誤用詞和解釋,請專業的朋友們見諒,也歡迎指出錯誤。 



一. 基礎概念 


首先,我們要了解一些基礎的概念,瞭解各個名詞的意義和區別。 


1.1. 運動規劃 (Motion Planning)
我們這裡講的運動規劃,有別於軌跡規劃 (Path Planning)。一般來說,path planning用於無人車/無人機領域,而motion planning主要用於機械臂,類人機器人領域。當然了,這兩者沒有本質的區別,理論上說MoveIt!和OMPL同樣可以用於無人車無人機的規劃,但不免有些殺雞用牛刀的感覺。兩者規劃的空間維度不同,導致他們的難易程度不同。舉例說明,如果不考慮速度加速度,只考慮位置的話,無人車軌跡規劃維度是3 (x,y,和角度), 無人機是6 (x,y,z,和另外3個量確定空間的旋轉角度)。確定3D空間的一個姿勢(pose)需要6個變數,而對於關節數大於6的機械臂結構,它的規劃空間維度就大於6,成為冗餘系統(redundant system),從而使規劃問題變得更為複雜。所謂冗餘系統,就是說,存在多種關節角度配置能夠使得終端達到相同的位姿,存在無數的解。這是達到的最終姿勢有無數個解,那麼如何到達這個最終姿勢,整個運動的軌跡,更是存在無數個解。 


既然存在無數的解,那麼問題來了。很明顯,存在兩種不同的方向,一種是找到最好的那個解,另一種是快速的找到一個有效的解。前者,大部分演算法使用最優規劃 (Optimization-based Planning),後者使用取樣規劃 (Sampling-based Planning)。具體的區別和演算法,不在這裡贅述。 


1.2. 開源運動規劃庫 (OMPL).
接上文,而OMPL (Open Motion Planning Library), 開源運動規劃庫,就是一個運動規劃的C++庫,其包含了很多運動規劃領域的前沿演算法。雖然OMPL裡面提到了最優規劃,但總體來說OMPL還是一個取樣規劃演算法庫。而取樣規劃演算法中,最出名的莫過於  Rapidly-exploring Random Trees (RRT)  和 Probabilistic Roadmap (PRM)了,  當然,這兩個是比較老的,還有很多其他新演算法。 

  • OMPL能做什麼? 簡單說,就是提供一個運動軌跡。給定一個機器人結構(假設有N個關節),給定一個目標(比如終端移到xyz),給定一個環境,那麼OMPL會提供給你一個軌跡,包含M個數組,每一個數組長度是N,也就是一個完整的關節位置。沿著這個軌跡依次移動關節,就可以最終把終端移到xyz,當然,這個軌跡應當不與環境中的任何障礙發生碰撞。
  • 為什麼用OMPL? 運動規劃的軟體庫和演算法有很多,而OMPL由於其模組化的設計和穩定的更新,成為最流行的規劃軟體庫之一。很多新演算法都在OMPL開發。很多其他軟體(包括ROS/MoveIt)都使用OMPL做運動規劃。


1.3. 逆運動學 (Inverse Kinematics)

  • 什麼是逆運動學(IK)?簡單說,就是把終端位姿變成關節角度,q=IK(p)。p是終端位姿(xyz),q是關節角度。
  • 為什麼要用IK?OMPL是取樣演算法,也就是要在關節空間取樣。 這與無人車的規劃有一個最明顯的區別,無人車的目標就是在取樣空間, e.g. 目標是(x,y), 取樣空間也是(x,y). 但是對於機械臂,目標是終端空間位置(xyz), 但取樣空間卻是關節空間(q0,q1,...qN)。有了IK之後,我們就可以把三維空間的目標p轉化為關節空間的目標q。那麼這樣就會讓取樣演算法能算的更快,具體方法不贅述,這樣的演算法有RRT-Connect,BKPIECE等等雙向取樣演算法。




問:我不想看也看不懂OMPL和各種演算法,但是我想讓機械臂動起來,怎麼辦?
答:那這正是MoveIt!的設計初衷。Move It!讓它動起來!
OMPL是運動規劃的“規劃”部分,而MoveIt!是OMPL的ROS介面。當然這不完全準確,OMPL有單獨的ROS介面,但依舊很繁雜,而MoveIt是OMPL ROS介面的介面。。。而且MoveIt!還結合了其他一些功能,總之MoveIt!就是個大介面。。 

  • MoveIt!能做什麼?一句話,MoveIt!就是一個模組化的介面,讓你在最短時間內,不用自己寫太多程式碼,就能配置出一個ROS Package來為你的機械臂做運動規劃。

二. 建立MoveIt! Package 

2.1 準備URDF package
首先我們要準備一個機械臂的urdf,如果你已有URDF,可以使用自己的urdf模型。若手頭沒有現成的URDF,可以從此處下載一個庫卡LWR簡化模型URDF,這是一個固定底座7自由度的機械臂。 
從該連線處依次進入examples/sovlers/ik_solver_demo/resources,下載裡面的lwr_simplified.urdf。 

複製程式碼

1 2 3 4 cd path_to_catkin_ws/src catkin_create_pkg lwr_description cd lwr_description mkdir urdf

將下載好的lwr_simplified.urdf放入urdf資料夾中,這樣一個urdf package便建立好了。 


2.2 MoveIt!配置助手 (MoveIt! Setup Assistant)
2.2.1 開啟MoveIt! Setup Assistant

複製程式碼

1 roslaunch moveit_setup_assistant setup_assistant.launch
 

MoveIt! Setup Assistant 是一個圖形介面,可以讓我們不用寫程式碼看程式碼,直接用滑鼠點選就可以配置機器人的運動規劃所需要的資訊。點選Create New MoveIt Configuration Package來建立新的配置包,選擇剛剛下載的urdf,然後點選Load Files 載入檔案。
2.2.2 建立碰撞免檢矩陣(ACM) 

點選Setup Assisant的左邊第二項'Self-Collisions',在這裡我們將建立碰撞免檢矩陣(Avoid Collision Matrix, ACM)。再次強調,怎麼建立很簡單,點選一下'Regenerate Default Collision Matrix'就可以了,問題是,為什麼?ACM是做什麼的?
我們知道,碰撞檢測是非常複雜的運算過程。對於多關節機械臂或者類人機器人來說,機械結構複雜,肢體多,碰撞檢測需要涉及很多的空間幾何計算。但是對於剛體機器人來說,有些肢體之間是不可能發生碰撞的,比如原本就相鄰的肢體,比如類人機器人的腳和頭。這裡生成的ACM就是告訴我們,這個URDF所描述的機器人,哪些肢體之間是不會發生碰撞的。那麼在之後的碰撞檢測演算法中,我們就可以略過對這些肢體之間的檢測,以提高檢測效率。

2.2.3 建立虛擬關節 (Virtual Joints)
在Setup Assistant 第三項Virtual Joints裡面,我們要建立所謂的虛擬關節。這個虛擬關節,可以理解為一個連線機器人和世界的關節。



一般來說,Virtual Joint Name我們命名為‘world_joint’,而'Child Link'指的是我們要把‘世界’和機器人的那個部位連線,那麼很顯然我們選擇基座'base'。‘Parent Frame Name’,是世界座標的名字,在ROS中一般叫'world_frame'。關節型別 Joint Type, 很顯然這裡我們選擇固定Fixed. 代表機器人相對於世界是固定的。而另外兩種, Planar指的是平面移動底座(xy平面+角度),用於移動機器人比如PR2;
還有一種Floating, 指的是浮動基座(xyz position+orientation),比如類人機器人。 2.2.4 建立規劃群 (Planning Groups) 建立Planning Group是MoveIt的核心之一。首先,點選Add Group, 我們會看到一個介面,如下圖,
那麼這些都是什麼呢?
  • Group Name: 不用多說,名字。。。我們就叫Arm。
  • Kinematic Solver: 運動學求解工具,這個就是負責求解正向運動學(Forward Kinematics)和逆運動學(IK, 見1.3節)的。 一般我們選用KDL, 。這是一個運動學與動力學的庫,可以很好的解決6自由度以上的單鏈機械結構的正逆運動學問題。當然你也可以用其他IK Solver, 比如SRV或者<span \"="" style="box-sizing: border-box;">IK_FAST,甚至你可以自己開發新的Solver然後插入進來,如果有空,我以後會發帖講解如何建立新的運動學求解庫並插入到MoveIt。
  • Kin. Search Resolution: 關節空間的取樣密度
  • Kin. Search TImeout: 求解時間
  • Kin. Solver Attempts: 求解失敗嘗試次數,一般來說這三項使用預設值就可以。你也可以根據具體需要做出適當調整。
接下來,我們要正式建立這個組群,有很多不同的方法,Add Joints, Add Links, Add Kin. Chain, Add Subgroups。我們這裡選擇'Add Kin. Chain',這樣我們可以清楚的看到整個機器人的機械機構,

在正中方我們可以看到這個機械臂的結構,一個link接著一個link。下方我們可以看到有'Base Link'和'Tip Link',我們選擇'lwr_arm_0_link'作為Base,選擇'lwr_arm_7_link'作為Tip. 然後點選Save,這樣一個規劃組群就建立好了。同樣的,我們可以再建立一個手的組群(Hand),這一次我們用Add Links,然後選擇'lwr_arm_7_link'。



2.2.5 建立機器人預設位姿 (Robot Poses)

在Setup Assistant 第五項, ‘Robot Poses’,我們建立預設的機器人位姿。點選‘Add Pose’,我們為機械臂建立一個向上直立的位姿UpRight,選擇Planning Group為Arm。可以看到很多滾動條,全設為0就是垂直向上的位姿。然後點選儲存。當然,你可以根據需要設定其他不同位姿。


2.2.6 配置終端控制器(End Effectors)

終端控制器,就是機械臂的手,以後用來在工作環境中直接控制的部位。我們新增一個叫做HandEff的終端控制器,End Effector Group選擇之前建立好的Hand,Parent_Link選擇機械臂的最後一個肢體lwr_arm_7_link。Parent Group選擇Arm。


2.2.7 配置被動關節(Passive Joints)

所謂被動關節,就是指現實中不配置電機的關節,也就是不會出現在機器人的Joint State Msg裡,以避免MoveIt與JointState出現匹配錯誤。這裡我們的LWR機械臂並沒有此類被動關節,所以可以直接跳過。

2.2.8 生成配置檔案(Configuration Files)
最後一步,在Configuration Package Save Path裡面選擇一個儲存地址,一般我們把他放在path_to_catkin_ws/src/lwr_moveit_config然後點選Generate Package,這樣一個完整的MoveIt Configuration Package就建立好了!先不要急著執行,我們先來看看都生成了哪些東西,還有一些重要的配置引數都是在哪定義的。
三. MoveIt 配置包詳解
開啟剛剛建立好的lwr_moveit_config資料夾,我們發現有config和launch兩個資料夾。3.1 MoveIt! 配置檔案先看config,裡面有

  • fake_controllers.yaml:這是虛擬控制器配置檔案,方便我們在沒有實體機器人,甚至沒有任何模擬器(如gazebo)開啟的情況下也能執行MoveIt。
  • joint_limits.yaml:這裡記錄了機器人各個關節的位置速度加速度的極限,這些都會被用於以後的規劃中。
  • kinematics.yaml:這裡就是上一章2.2.4裡面設定的東西,用於初始化運動學求解庫
  • lwr.srdf:這個是一個重要的MoveIt配置檔案,我們將在下一節詳解。
  • ompl_planning.yaml:這裡是配置OMPL各種演算法的各種引數。

3.2 SRDF檔案
SRDF是moveit的配置檔案,配合URDF使用。開啟lwr.srdf,


我們可以看到這是一個xml格式的配置檔案,根是robot,並有一個屬性值name='lwr'。下面各個專案應該很明顯,就是我們剛剛在Setup Assistant裡面所設定的東西,包含了組群,位姿,終端控制器,虛擬關節,以及碰撞免測矩陣ACM的定義。理論上,只要有了srdf和urdf,我們就可以完全定義一個機器人moveit資訊。 3.3 Launch檔案 

下面,我們看看launch資料夾,一打開發現有很多檔案,瞬間不想看了。。不要急,我們來看看幾個重要的檔案。 


3.3.1 demo.launch
demo是執行的總結點,開啟我們可以看到他include了其他的launch檔案。其中第14行說,如果有需要,釋出靜態的tf。比如說,你的機器人基座不在世界座標的原點,你可以釋出一個靜態tf來描述機器人在世界座標中的位置。第17-21行,就是我們釋出虛擬機器器人狀態的地方了,當然,如果你有實體機器人或者有gazebo之類的模擬器,你需要去掉這一部分,有其他相應的節點來發布機器人狀態。26-32行運行了另一個moveit重要的節點,move group。

3.3.2 move_group.launch
顧名思義,move group的功能是讓一個規劃組群動起來。怎麼動,那就要做運動規劃了,在move_group.launch第24-26行定義了運動規劃庫的使用,我們可以看到,預設的是使用ompl運動規劃庫。同樣的,如果以後有時間,我會發帖詳解如何建立新的運動規劃庫外掛並讓moveit使用其他的運動規劃演算法。其他的都是設定一些基本引數,暫時可以略過。

3.3.3 planning_context.launch
這裡我們可以看到,定義了所使用的urdf和srdf檔案,以及運動學求解庫。不建議手動更改這些,但是如果你需要使用不同的urdf,srdf,可以在這裡更改。

3.3.4 setup_assistant.launch
如果你需要更改一些配置,那麼可以直接執行

複製程式碼

1 roslaunch lwr_moveit_config setup_assistant.launch

這樣就可以基於當前設定做更改,而不是重新設定。 
四. 執行MoveIt!

4.1 Launch Demo
現在我們可以來嘗試執行moveit了!

複製程式碼

1 roslaunch lwr_moveit_config demo.launch

等待幾秒,當看到 All is well! Everyone is happy! You can start planning now! 的時候,就代表啟動成功了。我們可以看到一個Rivz視窗,左下角有一個運動規劃MotionPlanning模組。

 

第一個進入視野的就是Planning Library, OMPL。沒錯,這裡告訴你當前用的是OMPL運動規劃演算法。在中間的下來選單裡面有很多的具體演算法,之後你可以嘗試不同的演算法,看看他們的區別。 4.2 選擇目標位姿 如果如上文第二章中設定,你會在rviz主視窗中看到一個互動標記位於機械臂終端位置。移動這個標記到另外一個地方,你可以看到一個橙色的目標位姿(每一次移動標記,就運行了一次逆運動學IK求解過程)。

同樣的,你也可以在MotionPlanning模組下的Planning子模組寫的Query子模組裡面設定隨機的或者預設的目標位置。



4.3 運動規劃終於,到了運動規劃的時候了。。在Planning子模組中單擊Plan,一個運動軌跡就會出現與Rviz視窗中並迴圈播放。你可以在Display->MotionPlanning->Planned Path裡面設定各種顯示引數。


同時,在terminal裡面我們可以看到一些輸出
複製程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [ INFO] [1453481861.884163555]: LBKPIECE1: Attempting to use default projection. [ INFO] [1453481861.884336258]: LBKPIECE1: Attempting to use default projection. [ INFO] [1453481861.884489778]: LBKPIECE1: Starting planning with 1 states already in datastructure [ INFO] [1453481861.884523826]: LBKPIECE1: Attempting to use default projection. [ INFO] [1453481861.884547702]: LBKPIECE1: Starting planning with 1 states already in datastructure [ INFO] [1453481861.884564358]: LBKPIECE1: Attempting to use default projection. [ INFO] [1453481861.884587404]: LBKPIECE1: Starting planning with 1 states already in datastructure [ INFO] [1453481861.884604829]: LBKPIECE1: Attempting to use default projection. [ INFO] [1453481861.884626253]: LBKPIECE1: Starting planning with 1 states already in datastructure [ INFO] [1453481861.905034917]: LBKPIECE1: Created 99 (46 start + 53 goal) states in 88 cells (45 start (45 on boundary) + 43 goal (43 on boundary)) [ INFO] [1453481861.905633020]: LBKPIECE1: Created 87 (33 start + 54 goal) states in 76 cells (31 start (31 on boundary) + 45 goal (45 on boundary)) [ INFO] [1453481861.913846457]: LBKPIECE1: Created 126 (76 start + 50 goal) states in 115 cells (75 start (75 on boundary) + 40 goal (40 on boundary)) [ INFO] [1453481861.914639489]: LBKPIECE1: Created 220 (72 start + 148 goal) states in 201 cells (70 start (70 on boundary) + 131 goal (131 on boundary)) [ INFO] [1453481861.948016518]: ParallelPlan::solve(): Solution found by one or more threads in 0.063719 seconds
我們可以看到,本次運動規劃使用了LBKPIECE演算法,並且使用了4執行緒並行規劃,規劃時間為0.063719秒。你也可以在OMPL演算法裡選擇其他規劃演算法比如RRT,RRT-Connect,PRM,EST等等等等。。看看他們之間的軌跡區別和速度區別。 4.4 避障運動規劃 Collision Free Motion Planning 上面這個簡單的運動規劃,環境中並沒有障礙物。下面我們來為環境中增加幾個物體。建立一個demo.scene檔案,將以下程式碼複製進去(你可以自己設計不同的scene)
複製程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113