機器人操作系統簡介:終極機器人應用框架
已發表: 2022-03-11機器人操作系統(ROS) 不是一個實際的操作系統,而是一個框架和一組工具,可在異構計算機集群上提供操作系統的功能。 它的用途不僅限於機器人,而且提供的大多數工具都專注於使用外圍硬件。
ROS 分為 2000 多個包,每個包都提供專門的功能。 連接到框架的工具數量可能是它最大的力量。
為什麼要使用機器人操作系統?
ROS 為硬件抽象、設備驅動程序、多台機器上的進程之間的通信、測試和可視化工具等提供功能。
ROS 的關鍵特性是軟件的運行方式和通信方式,允許您在不知道某些硬件如何工作的情況下設計複雜的軟件。 ROS 提供了一種將進程(節點)網絡與中央集線器連接的方法。 節點可以在多個設備上運行,並且它們以各種方式連接到該集線器。
創建網絡的主要方式是提供可請求的服務,或定義與其他節點的發布者/訂閱者連接。 兩種方法都通過指定的消息類型進行通信。 一些類型由核心包提供,但消息類型可以由單獨的包定義。
開發人員可以通過連接小問題的現有解決方案來組裝一個複雜的系統。 該系統的實施方式允許我們:
即時更換具有相似接口的組件,無需停止系統進行各種更改
將多個組件的輸出複用到另一個組件的一個輸入中,允許並行解決各種問題
只需在消息系統中實現適當的連接器,即可連接以各種編程語言製作的組件,通過連接來自不同開發人員的現有模塊,可以輕鬆開發軟件
在設備網絡上創建節點,無需擔心代碼在哪裡運行並實現進程間通信 (IPC) 和遠程過程調用 (RPC) 系統
通過使用前兩個要點,直接從遠程硬件按需連接到源,而無需編寫任何額外的代碼
我們計劃通過迭代開發一個簡單的解決方案來展示它的有用性。 與其他方法相比,有幾個關鍵優勢。 ROS 具有多平台支持,並允許通過在後台處理的點對點連接在多個設備上的進程之間進行連接。 該設計允許通過包裝 C++ 通信類或手動開發語言接口類來支持任何語言。
ROS 是由它自己的社區製作的,是為了它的社區。 幾年後,由於系統的架構,這導致了大量易於集成的可重用包。
MRPT、CARMEN、LCM、Player、Microsoft RDS 等替代方法提供了其中一些功能,但不是全部。 大多數情況下,設計失敗是語言支持限制、進程之間未優化的通信或缺乏對各種設備的支持,這可以說是最難解決的問題。
我們要建造什麼?
由於我們的重點是框架而不是特定問題的實際算法,因此給定的問題將相當簡單。 我們的目標是為車載計算機構建軟件,使我們能夠遠程控制和監控通過 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、Raspbian 和 Android 版本。
我們將在桌面上完成 Ubuntu 14.04 的安裝過程。 所有支持的版本和平台的流程都可以在官方網站上找到。 也可以使用安裝了 ROS 的虛擬機。
安裝取決於平台(並且大多數平台都提供了軟件包),但所有平台的工作區配置都是相同的。
在 Ubuntu 上安裝
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 支持indigo
和jade
。
在桌面上安裝基礎包有以下三個選項之一:
sudo apt-get install ros-indigo-ros-base
進行最小安裝sudo apt-get install ros-indigo-desktop
以獲得基本的附加 GUI 工具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
安裝外部依賴項,它作為一個軟件包提供並通過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
的主要作用是告訴節點它們應該連接到哪些其他節點,以及以何種方式(無論是通過網絡端口還是共享內存)。 目標是讓節點只關心他們想要知道的數據,而不是他們想要連接的節點,同時最大限度地減少執行所有通信所需的時間和帶寬。
rqt
運行roscore
後,我們可以啟動 ROS 的主要 GUI 工具: rqt
。 我們看到的是非常平淡無奇的——一個空窗。 rqt
擁有各種各樣的插件,可以配置為任何可視化配置和任意數量的預定義視圖。
首先,讓我們在Plugins > Robot Tools > Robot Steering
中選擇它來運行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
以 10 Hz 的平均速率表示。 好吧,我可以通過慢速 Wi-Fi 連接運行多少這樣的主題? rostopic bw /cmd_vel
檢測到平均 480 B/s。
現在一切都很好,但是我們談到了消息類型。 這些數據對人類有好處,但應用程序需要原始數據,並且需要知道消息類型以便解釋數據。 類型可以用rostopic type /cmd_vel
來確定,告訴我們它是一個geometry_msgs/Twist
。 所有不帶任何參數調用的 ROS 終端工具都會返回標準幫助消息。
ROS Wiki 足以對這個字符串進行網絡搜索,從而在 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 矢量組成,分別代表 3D 空間中的線速度和角速度。
如果我們想要一個節點連接到哪些主題, rosnode info <node-name>
將為我們提供有關該節點的詳細數據。 rostopic
、 rosmsg
和rosnode
工具是檢查原始 ROS 功能的主要工具。 ROS 有更多的 GUI 和終端工具,但這些超出了我們本次介紹的範圍。
運行 ROS 節點的主要工具是rusrun
和roslaunch
。 rosrun
可以通過rosrun <package_name> <node_name>
運行節點,而roslaunch
基於啟動文件運行節點,我們會稍微熟悉一下,因為它們是 ROS 自動化中最複雜的元素。
我們可以關閉我們運行的所有東西,開始處理我們的第一個代碼。 為了將來參考,不用說運行任何與 ROS 相關的東西都需要一個活動的roscore
實例。 你遇到的很多問題都可以通過關閉運行roscore
的終端窗口來解決,然後打開一個新窗口重新啟動它。 這會更新bash
和roscore
中需要重新加載的所有依賴項。
創建遊戲手柄遠程操作
我們的第一個目標是通過創建一個節點來模仿Robot Steering
的功能,該節點根據遊戲手柄輸入將geometry_msgs/Twist
數據發佈到/cmd_vel
。 我們的第一站是joy
套餐。
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 rosrun toptal_tutorial teleop.py
將導致teleop.py
的終端輸出充滿 Joy 消息。
讓我們檢查一下代碼的作用。
首先,我們導入 rospy,它託管用於與 ROS 框架交互的庫。 每個定義消息的包都有一個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
,它向cmd_vel
發布Twist
類型的消息。 我們將該發布者綁定到回調,並使其在每個輸入上發布一條 Twist 消息,其中速度由前兩個軸表示。 這段代碼符合我們的預期,我們可以通過rostopic echo /cmd_vel
查看結果輸出。
我們還有一個問題。 /joy
主題可以以很高的速度發布。 如果我們監控rostopic hz /cmd_vel
並將模擬搖桿繞圈移動,我們可以看到大量消息。 這不僅會導致大量通信,而且接收這些消息的進程必須處理每一個消息。 沒有必要如此頻繁地發布這些數據,我們最好以 10 Hz 的穩定速率發布。 我們可以用下面的代碼來完成。
#!/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
主題以 10 Hz 獲得速度命令,模仿Robot Steering rqt
插件的輸出。
運行模擬系統
模擬世界
我們的第一個目標是創建一個可以模擬我們想要實現的場景的環境。 stage_ros
包中的節點stageros
允許我們在通過圖像定義的 2D 階段內運行一個機器人。 在世界文件的stage_ros
包中描述了完整的語法以及如何生成它們。 這相當簡單,但超出了我們的範圍。 幸運的是,該軟件包附帶了幾個演示世界。 首先,讓我們運行以下命令進入文件目錄:
roscd stage_ros cd world
在文件夾中有幾個文件。 讓我們運行一個。
rosrun stage_ros stageros willow-erratic.world
這創造了幾個話題。 它們中的每一個的含義也記錄在包中。 重要的是它有cmd_vel
。
在顯示的舞台內,有一個藍色方塊,代表您控制的機器人。 通過使用我們的代碼或Robot Steering ,我們可以控制這個機器人。 試試看。
通過啟動文件設置我們的系統
讓我們在我們的包中創建一個launch
文件夾,並在其中創建一個名為teleop.launch
的文件。 最終的文件夾結構應如下所示:
toptal_tutorial/ ├── CMakeLists.txt ├── launch │ └── teleop.launch ├── package.xml ├── scripts │ └── teleop.py └── src
在teleop.launch
文件中,我們將定義一組節點及其互連。
<launch> <arg name="world_file" default="$(find stage_ros)/world/willow-four-erratics-multisensor.world" /> <node pkg="stage_ros" type="stageros" name="simulated_world" args="$(arg world_file)"></node> <group ns="robot_0"> <node pkg="joy" type="joy_node" name="joy_input"></node> <node pkg="toptal_tutorial" type="teleop.py" name="joy_convert"></node> </group> </launch>
新世界由四個機器人組成,每個主題都有一個前綴robot_<n>
。 因此,0 號機器人有一個名為robot_0/cmd_vel
的速度命令主題。 這就是為什麼我們將控件放在名為robot_0
的命名空間中,以將它們的名稱調整為新形式。 從這個意義上說,您可以將主題名稱視為文件系統中的文件夾。
要運行啟動文件,不需要roscore
。 從某種意義上說, roscore
只是一個什麼都不做的啟動文件的特例。 如果缺少roscore
,則只有啟動的第一個啟動文件將運行核心,而其餘的將連接到它。 現在,我們使用以下命令運行啟動:
roslaunch toptal_tutorial teleop.launch
如果一切正確,這將產生一個有 4 個機器人的模擬器,其中一個由我們的遊戲手柄或操縱桿控制。 這個世界比以前的世界有更多的東西。 四個機器人中的每一個都具有:
/robot_<n>/base_pose_ground_truth /robot_<n>/base_scan_0 /robot_<n>/base_scan_1 /robot_<n>/camera_info_0 /robot_<n>/camera_info_1 /robot_<n>/cmd_vel /robot_<n>/depth_0 /robot_<n>/depth_1 /robot_<n>/image_0 /robot_<n>/image_1 /robot_<n>/odom
我們將<n>
替換為 0、1、2 或 3。這將我們帶到了最後一個主題。
使用rqt
查看我們的數據
我們沒有深入rqt
,但它是查看更複雜數據的完美工具。 您可以嘗試所有主題,但我們將重點關注image_0
、 image_1
、 depth_0
和depth_1
主題。
讓我們啟動rqt
並刪除所有打開的插件。 現在我們將打開 4 個圖像可視化工具( Plugins > Visualization > Image View
),並將它們以 2x2 網格形式放置。 最後,在每個視圖的左上角,讓我們為robot_0
選擇四個陳述的主題之一。
我們得到的是具有深度感知的立體視覺,帶有低分辨率相機。 請記住,如果沒有輸入系統,我們甚至可以得到這個結果。 如果我們只是運行它(從stage_ros/world
文件夾中):
rosrun stage_ros stageros willow-four-erratics-multisensor.world
並添加帶有名為/robot_0/cmd_vel
主題的機器人轉向插件,如果控件是屏幕上的滑塊,我們會得到相同的結果。
將結果應用於實際系統
許多硬件都完全支持 ROS,通常由第三方志願者提供。 許多機器人平台具有生成這些類型消息的驅動程序,而 ROS 具有接收網絡攝像頭並發布圖像饋送的節點。
雖然最後一個結果是我們想要實現的模擬,但同樣可以通過以下修改來實現:
- 在機器人的板載計算機上安裝 ROS
- 為將 ROS 連接到底層平台和所有高級傳感器(如相機、激光測距儀等)的車載計算機創建一個啟動文件。 所需的節點可能已經存在,或者可以通過在一側創建 ROS 的發布者/訂閱者和在另一側創建串行通信驅動程序來實現
- 讓啟動文件在啟動時運行
- 在您的遠程計算機上,將
export ROS_MASTER_URI=http://<robot_hostname>:11311/
添加到您的 bash 啟動中,使遠程計算機在給定的主機名和端口上查找roscore
- 啟動
rqt
和/或任何用於監視和控制機器人的腳本
這真正歸結為只是在遠程設備上導出正確的環境變量,其餘的自行處理。 在計算機集群上運行 ROS 只需要為每台機器完成一個步驟。
結論
我們已經展示瞭如何通過很少的編碼就可以擁有一個複雜的變量系統,您可以隨心所欲地對其進行操作。 簡單的發布者/訂閱者係統允許您快速開發在計算機集群中處理數據的軟件管道,而無需擔心某些元素的底層實現。
雖然我們使用了一個簡單的模擬器,但更複雜的模擬器(如gazebo
)(也包含在完整的桌面版本中)允許您使用物理和復雜的傳感器創建 3D 世界,並且可以在最終結果和產品開發之前就讓您體驗它。
這個介紹是一個非常基本的介紹,但希望您對使用這個多功能框架更加感興趣。