/** * 原文出處:https://www.toptal.com/robotics/introduction-to-robot-operating-system * @author dogstar.huang <chanzonghuang@gmail.com> 2016-03-05 */
機器人操做系統(ROS)不是一個真實的操做系統,而是一個框架以及一系列爲運行在異質計算機集羣的操做系統提供基礎功能的工具。 它的用處不侷限於機器人,還包括大量關注與外設一塊兒工做的工具。html
ROS 分紅2000多個包,每一個包提供特定的功能。連續框架的工具數量多是它的最大功率。node
ROS提供了硬件抽象,設備驅動,多機器進程間的通訊,測試和可視化的工具等功能。 python
ROS的關鍵特性是軟件運行和溝通的方式,使得你能夠設計複雜的軟件而不須要知道相關的硬件如何工做。ROS提供了一種利用中央樞紐鏈接進程(節點)網絡的方式。 節點能夠運行在衆多設備上,而且能夠經過各類途徑與中央樞紐進行鏈接。git
建立網絡的主要方式有提供可請求的服務,或者定義與其餘節點的發佈/訂閱者鏈接。這兩種方法都是經過指定消息類型來通訊的。 一些類型由核心包提供,而消息類型則可由用戶包來定義。github
對於小型問題,開發人員能夠經過鏈接已存在的解決方案來集成一個複雜的系統。這種方式的系統已經實現,它容許咱們:算法
在fly上用熟悉的接口替換組件,以便爲各類變化移除中止系統的須要編程
爲另外一個組件把衆多組件的輸出混合成一個輸出,以便並行解各種問題ubuntu
只要實現消息系統對應的合適鏈接器就能夠鏈接各類各樣編程語言創造的組件,使得經過鏈接來自大量開發人員已存在的模塊進行軟件開發很容易bash
建立基於設備網絡的節點,而不用關心代碼在哪裏運行以及實現進程通訊(IPC)和遠程過程調用(RPC)系統網絡
根據遠程硬件的須要,經過部署前兩個要點便可直接鏈接饋送,而不用編寫任何額外的代碼
咱們在計劃演示經過迭代開發一個簡單方案是多麼地有用。相比於其餘方式,這裏有幾個關鍵的好處。ROS有多平臺支持以及容許經過隱藏於表面的點對點鏈接進行多設備的進程鏈接。 此設計容許支持任何包裝了C++通訊類的任何語言,或者手動爲語言接口開發類。
ROS由咱們的社區建立,意味着它也迴歸於社區。多虧於這個系統架構,通過若干年後,這種氛圍致使了大量可重用並易於集成的包的涌現。
一些變種如:http://www.mrpt.org/,http://carmen.sourceforge.net/intro.html, http://lcm-proj.github.io/,http://playerstage.sourceforge.net/,https://msdn.microsoft.com/en-us/library/bb648760.aspx 等提供了其中一些特性,但不是所有。 大多數時候,設計的衰落在於語言支持的侷限,未優化進程通訊,或者按理說是最難修復的問題 -- 缺乏對大量設備的支持。
既然咱們的關注點是框架而不是針對特定問題的具體算法,因此給定的問題是至關簡單的。咱們的目標就是爲一個車載的計算機構建經過Wi-Fi鏈接的軟件, 從而能夠經過使用電腦上的遊戲把柄和安裝在機器人上的攝像機來遠程控制和監控機器人。
首先,爲了示範ROS的基本原則,咱們將會建立一個簡單的程序來鏈接一個簡單的模擬。咱們會把遊戲手柄綁定到電腦上而且嘗試爲能把遊戲手柄輸入轉換成機器人控制信息而設計一個好的控制方案。
編寫ROS代碼的只要語言是C++和Python,首選C++是由於它的性能。因爲代碼中更少的樣板和不須要明確的構建,咱們將會經過Python來解釋示例。
ROS版本由名稱來組成。如如今這個日期,最新的發佈版本是_Jade Turtle_,而最新的LTS版本是_Indigo Igloo_。最好是使用LTS版本,由於ROS不保證向前兼容性,因此所有的示例將使用_Indigo_這個版原本編寫。
ROS可用於大量的*NIX平臺,官方支持的版本運行於Ubuntu。OS X,Arch Linux,Debian,Raapbain 和Android版本則由社區支持。
咱們將經歷在Ubuntu 14.04桌面的安裝過程。所有支持的版本和平臺的過程均可在官網得到。帶ROS的虛擬機也有。
安裝是平臺依賴性的(大部分平臺都有本身提供的包),而工做區間的配置對於所有平臺來講都是同樣的。
ROS提供了本身的倉庫。第一步就是要添加它們。
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net --recv-key 0xB01FA116 sudo apt-get update
而後會有針對你的Ubuntu版本的所有ROS的所有託管包。例如,Ubuntu 14.04支持indeigo和jade。
在桌面上安裝基本的包能夠三選一:
最小化安裝:sudo apt-get install ros-indigo-ros-base
帶基本額外GUI工具的:sudo apt-get install ros-indigo-desktop
全量安裝,即有所有官方特性的,包含了大量模擬器以及導航和知覺類庫:sudo apt-get install ros-indigo-desktop-full
爲了得到最好的工做體驗,推薦全量安裝。對於僅僅是用於運行節點的設備安裝,基本版本則足夠了。不和你選擇的是哪一個選項,你均可以安裝任何須要的package_name包經過執行:
sudo apt-get install ros-indigo-<package-name>
最終的名字將會把下劃線替換成橫線,因此stage_ros在這個包裏
ros-indigo-stage-ros
下一步是安裝rosdep。在ROS的包能夠聲明他們依賴的包。rosdep容許你編譯這些包而不用過多地人工維護依賴處理。爲了安裝它,調用:
sudo rosdep init rosdep update
ROS有幾個被它的工具使用的環境變量。默認的安裝,bash腳本在/opt/ros/indigo/setup.bash初始化他們。在每一個bash的會話中都須要初始化這些變量,因此最好的解決方案是把他們添加到~/.bashrc。
echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc source ~/.bashrc
一些包經過rosinstall來安裝擴展的依賴,rosinstall可做爲一個包來得到並可經過sudo apt-get install python-rosinstall安裝。
這裏到了在Ubuntu上安裝的最後。接下來是安裝工做區間的簡短介紹。
自從_Groovy Galapagos後_,ROS工做區間經過catkin來管理。咱們須要爲所有託管的負定義一個目錄。在這個目錄裏建立一個src目錄,並在裏面調用catkin_init_workspace。 這將會建立大量的連接到當前ROS版本源的符號。下一步是把這個工做音區也添加到環境變量。
爲了演示整個工做區間的配置,選擇一個空的目錄並執行如下命令:
mkdir src cd src catkin_init_workspace cd .. catkin_make echo "source $(pwd)/devel/setup.bash" >> ~/.bashrc source ~/.bashrc
如今你已經建立了一個能夠在裏面建立本身ROS包的工做區間。
立刻建立代碼是一個很大的跨越。讓咱們先來熟悉一些運行在屏幕後的系統。第一步是運行基本的GUI並看下它生成了什麼消息。
爲了在ROS中運行一些東西,須要加載一個核心的進程。這一點很簡單,只要在一個新的終端窗口並輸入:
roscore
在你的整個鏈接設備網絡中,roscore僅須要在爲通訊分發託管中央樞紐的設備上加載一次。
roscore的主要角色是告訴節點他們應該鏈接哪一個節點,以及經過哪一種方式(不論是經過網絡端口仍是共享內存)。這樣的目的是爲了在最小化時間和運行所有通訊所須要的帶寬時, 讓節點只關心他們想知道的數據,而不是他們想要鏈接的節點。
運行roscore後,能夠爲ROS加載主要的GUI工具:rqt。咱們會看到很通常的東西 -- 一個空白的窗口。rqt託管了很是大量的能夠在可視化配置中進行配置的插件 以及任何數量的預約義視圖。
爲了可以開始,先運行插件_Robot Steering_,能夠經過Plugins > Robot Tools > Robot Steering來選擇它。咱們會看到有兩個滑塊,表明着咱們的機器人將會擁有的線性和旋轉運動。 在插件的頂部能夠看到一個有/cmd_vel的輸入框。咱們能夠任意進行重命名。它表明了此操舵發佈的主題。終端工具是看到在後臺運行了什麼的最好地方。
ROS有幾個很是有用的工具來檢查系統中正在發生什麼。第一個咱們將會介紹的工具是rostopic。 它容許咱們檢查節點能夠評閱和發佈的主題。運行```rostopic list···將會產生:
/cmd_vel /rosout /rosout_agg
後兩個主題一般都會運行而且與中央ROS系統相關。/cmd_vel主題則由咱們的操舵發佈。在操舵中重命名這個主題也會在這裏重全名。如今,咱們對於在這個主題裏面發生的東西感興趣。 運行rostopic echo /cmd_vel將看不到什麼東西(除非你對滑塊做了調整)。這個進程會一直執行,直到咱們取消它。讓咱們把垂直滑塊移到20 m/s。看着輸出,咱們會看到如下一次又一次重複的內容:
linear: x: 0.2 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0
這個消息重複的頻率是多少?rostopic hz /cmd_vel說平均頻率是10Hz。好吧,經過我很慢的Wi-Fi鏈接能夠運行多少像這樣的主題?rostopic bw /cmd_vel代表平均是480B/s。
到目前爲止一切運行良好,但咱們將要討論消息類型。這種數據對於人類是友好的,但應用須要raw格式的數據,而且須要知道消息類型以便它能解析數據。 類型能夠經過rostopic type /cmd_vel來決定,告訴咱們這是一個geometry_msgs/Twist。任何ROS終端的工具無參數調用時都會返回一個標準的幫助消息。
ROS的wiki很是贊,在網頁搜索這個字符串會找到一系列的解釋,從它包括了什麼到它的結構是怎樣的應有盡有。可是咱們不用依賴於它。rosmsg是針對消息類型的通用工具。 運行rosmsg show geometry_msgs/Twist會返回:
geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z
這個消息包含了兩個3D向量,表示在三維空間中的線性和旋轉速度。
若是咱們想知道一個節點鏈接了什麼主題,rosnode info <node-name>會給出關於節點的詳細數據。rostopic,rosmsg和rosnode是檢查raw ROS功能的主要工具。 ROS有大量的GUI和終端工具,但這些已超出了此次簡介的範疇。
運行ROS節點的主要工具是rosrun和roslaunch。rosrun經過rosrun <package_name> <node_name>來運行節點,而roslaunch則基於加載 因爲是ROS自動化中最爲複雜的元素因此咱們知之甚少的文件來運行節點。
咱們能夠關閉運行的所有東西以開啓咱們最初的代碼。在下文中,咱們將省略說明運行任何和ROS相關的東西都須要一個活躍的roscore實例。你遇到的不少問題均可以經過關閉 運行roscore對應的窗口來解決,而且新開一個窗口從新加載。這樣的話會更新所有須要從新加載的依賴,包括在bash和在roscore中。
咱們第一個目標是經過建立一個發佈geometry_msgs/Twist數據給基於遊戲柄輸入的/cmd_vel來模擬Robot Steering的功能。第一步的產出是joy包。
這個joy包爲操縱桿和遊戲柄提供了通用的ROS驅動。它沒有包含在默認的安裝,因此須要這樣進行安裝:
sudo apt-get install ros-indigo-joy
安裝完成後,能夠運行rosrun joy joy_node。這會把咱們和默認的操縱桿或者遊戲鏈接起來。運行rostopic list能夠看到有一個叫/joy的主題。經過rostopic echo能夠 看到如下格式的消息(請注意你須要使用遊戲柄或操縱桿解析待發布的消息)。
header: seq: 4156 stamp: secs: 1450707466 nsecs: 204517084 frame_id: '' axes: [0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0] buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
如今你能夠忽略頭部。除此以外,axes和buttons恰到好處地解析了他們表明什麼。移動軸和按下在控制器上的按鈕會致使這些數字發生變化。使用咱們的工具,能夠決定這個消息類型 是sensor_msgs/Joy而且對應的格式是:
std_msgs/Header header uint32 seq time stamp string frame_id float32[] axes int32[] buttons
編寫代碼的第一步是建立一個包。在工做區間的src目錄裏,運行:
catkin_create_pkg toptal_tutorial rospy joy geometry_msgs sensor_msgs
在這裏咱們聲明瞭將要建立的包名,緊跟隨後的是所要依賴的包。無須擔憂,依賴能夠稍候手動更新。
如今咱們有了toptal_tutorial目錄。在這個目錄裏,建立一個將會放置所有Python腳本的scripts目錄。
如今來建立一個teleop.py文件,並在裏面放置:
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy def joy_callback(data): print data def main(): rospy.init_node('teleop') rospy.Subscriber('joy', Joy, joy_callback) while not rospy.is_shutdown(): pass if __name__ == '__main__': main()
咱們還須要設置chmod +x teleop.py以便腳本能被執行。在某個終端上運行rosrun joy joy_node並有另外一個終端上運行rosrun toptal_tutorial teleop.py將會致使運行teleop.py的終端被輸出的Joy消息填充。
讓咱們來檢查一下代碼作了什麼。
首先,導入托管了與ROS框架交互的類庫的rospy。每個定義了消息的包都有一個帶消息定義的msg子包。咱們導入了Joy以即可以處理輸入。 這裏不須要導入嵌入式的消息類型(如來自在Joy消息中的std_msgs.msg的Header),除非咱們想明確地說起他們。
第一步是初始化一個指定名字的節點(在這裏,咱們把它叫做「teleop」)。而後建立一個訂閱者來訂閱sensor_msgs.msg.Joy類型的「joy」主題,而且經過 回調joy_callback函數來處理每個消息。回調接收一個參數,即來自消息的數據。訪問這個數據的成員很是簡單。若是想打印第一個軸的狀態,或者想 從新調用這個消息類型,能夠調用print data.axes[0],而且是一個浮點類型。在最後的循環會一直循環直到ROS關閉。
下一步是處理未知的數據。咱們會建立一個根據輸入而改變的Twist消息,而後會把它發佈到cmd_vel主題。
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist # new from functools import partial # new def joy_callback(pub, data): # modified cmd_vel = Twist() # new cmd_vel.linear.x = data.axes[1] # new cmd_vel.angular.z = data.axes[0] # new pub.publish(cmd_vel) # new def main(): rospy.init_node('teleop') pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000) # new rospy.Subscriber('joy', Joy, partial(joy_callback, pub)) # modified while not rospy.is_shutdown(): pass if __name__ == '__main__': main()
首先,添加Twist消息,而且爲經過functools.partial綁定函數參數添加支持。而後建立一個發佈者,pub,來把一個Twist消息類型發佈給cmd_vel。咱們把這個發佈者和回調函數進行綁定,並使它爲每個由前兩個軸表明的速度的輸入都發佈一個Twist消息。 這些代碼作了咱們指望的它作的事情,並且能夠經過rostopic echo /cmd_vel看到結果的輸出。
但咱們還有一個問題。joy主題發佈的速度會很大。若是監控rostopic hz /cmd_vel並在圓圈中移動模擬遙杆,能夠看到超級大量的消息。 這不只僅致使了大量的通訊,還致使了接收這些消息的進程須要一個個地處理他們。其實並不須要如此頻繁地發佈這些數據,最好是以一個穩定的速率如10Hz來發布。 能夠經過如下代碼來實現。
#!/usr/bin/env python import rospy from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist from functools import partial def joy_callback(cmd_vel, data): # modified cmd_vel.linear.x = data.axes[1] cmd_vel.angular.z = data.axes[0] # moved pub.publish(cmd_vel) to main loop def main(): rospy.init_node('teleop') cmd_vel = Twist() # new pub = rospy.Publisher('cmd_vel', Twist, queue_size=1000) rospy.Subscriber('joy', Joy, partial(joy_callback, cmd_vel)) # modified rate = rospy.Rate(10) # new while not rospy.is_shutdown(): pub.publish(cmd_vel) # new rate.sleep() # new if __name__ == '__main__': main()
咱們修改了回調函數以便接收可變的Twist對象而且在循環中做了修改。來自rospy.Rate的sleep函數維護了一個穩定的輸出頻率。
最後的代碼將會致使/cmd_vel主題以10Hz來得到速度命令,以模擬_Robot Steering_ ···rqt```插件的輸出。
------------------------
本做品採用知識共享署名-非商業性使用-相同方式共享 3.0 未本地化版本許可協議進行許可。
本文翻譯做者爲:dogstar,發表於艾翻譯(itran.cc);歡迎轉載,但請註明出處,謝謝!