Robotics, ROS

Caffe & GoogLeNet,及其在機器人上的應用

這週的TB Weekly技術週刊會開始一個全新的嘗試,我們會開始產生正體中文的技術專欄(編輯群輪流囉XD),希望可以藉這個project分享更多優質的技術內容給大家,目前暫時先把內容寫在這邊,晚點再port到統一的專欄發表平台上。

首先介紹一下這次要談的內容,是有關使用深度學習的模型來做物體辨識的應用。深度學習在這幾年來變得很火紅,相關的框架也相當多,這次之所以想談caffe,是因為已經有現成的方法可以將它應用到機器人上面。(有位台灣的開發者弄了一個叫做ros_caffe的package來串接ROS(機器人作業系統)跟Caffe,可以將Caffe辨識的結果丟到一個ROS的topic,其他的node就可以自己取用。這使得機器人辨識物體的能力得以大幅增加)

基本的安裝方法可以參考這個連結,假設已經裝成功,那至少就已經有基本的環境可以用(有一個caffe的資料夾被放在你安裝的路徑),接下來需要下載GoogLeNet的model,只要用caffe/scripts資料夾裡的程式幫忙就行了:

$./scripts/download_model_binary.py ./models/bvlc_googlenet

假設已經下載好model,接下來就可以用一個小程式來跑跑看GoogLeNet了:


import numpy as np
import matplotlib.pyplot as plt

# Make sure that caffe is on the python path:
caffe_root = '../' # this file is expected to be in {caffe_root}/examples
import sys
sys.path.insert(0, caffe_root + 'python')
sys.path.append("/usr/lib/python2.7/dist-packages/")

import caffe

# Set the right path to your model definition file, pretrained model weights,
# and the image you would like to classify.
MODEL_FILE = '../models/bvlc_googlenet/deploy.prototxt'
PRETRAINED = '../models/bvlc_googlenet/bvlc_googlenet.caffemodel'
IMAGE_FILE = 'images/cat.jpg'

caffe.set_mode_cpu()
net = caffe.Classifier(MODEL_FILE, PRETRAINED,
 mean=np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1),
 channel_swap=(2,1,0),
 raw_scale=255,
 image_dims=(256, 256))

input_image = caffe.io.load_image(IMAGE_FILE)
plt.imshow(input_image)
plt.show()

prediction = net.predict([input_image])
plt.plot(prediction[0])
plt.show()
print 'predicted class:', prediction[0].argmax()

接下來只要執行(因為程式放在examples資料夾底下):

$python ./examples/googlenet_example.py

就可以看到一隻貓的影像,關掉影像之後就會看到貓的類別被輸出在terminal。

到目前為止算是驗證了可以跑起GoogLeNet。接下來,如果想往下跟ros_caffe的串接可以參考外國鄉民的文章,裡面有完整而詳細的步驟。如果你已經安裝過caffe,可以參考這個issue。另外,需要注意的是,外國鄉民跑的只有global的結果,也就是一張影像中只有一個最顯著的物體會被辨識,如果要辨識一張影像中的各個物體,可能就要自己在中間串接一個負責做segmentation的node,再把各個切出來的區塊餵給ros_caffe來做辨識。

 

Robotics, ROS

Qualcomm也在做機器人!

最近因緣際會,看到相關的訊息,發現Qualcomm已經做出支援ROS的開發板了,核心的處理器是Qualcomm® Snapdragon™ 600處理器(採用ARM指令集架構),支援的開發板包含Inforce 6xxx系列的SBC(Single-Board Computer),例如:Inforce Computing 6410™ Single Board Computer 跟 Inforce Computing 6410 Plus™ Single Board Computer。

先列出一些我自己好奇的問題:

  1. Qualcomm為什麼要做這件事?
  2. ROS怎麼運行的?是不是有porting?
  3. 怎麼寫出可以在Qualcomm SBC上面執行的ROS程式?
  4. Snapdragon™ 600處理器的硬體規格大概是什麼等級?能負荷多少運算量?

接下來是我理出的答案:

1.眾所皆知,Qualcomm是做通訊晶片的IC設計大廠,依賴著智慧型手機市場的大幅增加,Qualcomm的晶片也賣得嚇嚇叫(畢竟手機都需要好的通訊功能啊,尤其是多媒體傳輸需求不斷增加),不過問題是,如果智慧型手機成長停滯了、其他IC設計廠追上來了該怎麼辦?

如果Qualcomm沒有收一收不做了的打算,那就得再尋找下一個會大量用到他們設計的IC的產品,而機器人是其中一個選項。

2.Qualcomm跟OSRF合作,把比較重要的package(450個以上)移植到ARM指令集架構的硬體上(其實就是運行在Ubuntu for ARM的環境之下,可以參考ROS wiki上的UbuntuArm頁面),所以他們確實有做porting,但是我找不到他們的程式碼,不知道他們是怎麼實作的。

3.安裝方面可以參考IFC 6410的guide。而在寫程式方面,因為Qualcomm跟OSRF已經做了porting,所以使用者在撰寫程式的時候並不會跟寫PC上運行的ROS程式有任何差別,看起來在編譯的時候會有些許差別,但資料實在還太少,不太知道詳細步驟,我想2016年應該就會釋出開發套件讓大家玩了。

4.根據這個網頁,如果是用IFC 6410的SBC,都有1.7 GHz Quad Core Qualcomm® Krait™ CPU的運算能力,還可以執行gmapping讓我覺得滿驚訝的,也許在不久的將來真的可以用小型開發板來當作機器人的運算中樞,然後VSLAM、Object Recognition等feature都能讓不同的IC來處理。

ROS

ROS tutorials 系列(11) – Writing a Simple Service and Client

原文: (因為C++版本跟Python版本的內容差不多,還有Examine那篇也只有幾個指令,所以乾脆一併寫,不過我主要只會就C++那篇的內容來說明,因為其他東西大同小異)

http://wiki.ros.org/ROS/Tutorials/WritingServiceClient%28c%2B%2B%29

http://wiki.ros.org/ROS/Tutorials/WritingServiceClient%28python%29

http://wiki.ros.org/ROS/Tutorials/ExaminingServiceClient

延續上一篇的教學,這一篇也cover到程式撰寫的部分,不過這次是使用ROS Service相關的API,
————————————————————————————

1.1 Writing a Service Node

這邊要撰寫的service滿簡單的,他負責作的事情就是,當收到client傳來的兩個數字時,計算這兩個數字相加後的結果,再回傳給client。所以這邊要實現的功能就是讓Service Node可以接收到client傳來的訊息,呼叫一個callback function,在這個callback function裡面把兩個數字相加,然後再回傳給client。

接下來我們就看看怎麼用程式實作這件事情。

首先init跟NodeHandle的部分都跟上一篇的初始化過程相同,比較不同的地方在於,這邊利用NodeHandle產生了一個ServiceServer的物件,這個物件負責的就是提供一個叫做”add_two_ints”的service,並且指定callback function是add(…),這邊要注意的是,在add(…)裡面我們並沒有看到明顯地傳回response(就是res變數)的程式碼,這是因為ROS API實作上幫忙處理掉這件事情,所以我們要做的就只是指定res應該是什麼,不需要多寫一行把res傳出去。

1.2 Writing a Client Node

上一節已經講完Service Node要做的事情,那Client Node要做的事情應該就滿清楚的了。

這邊比較不容易了解的地方大概就是”beginner_tutorials::AddTwoInts srv;”,大家可以把這個物件想成兩個node在實際進行service傳輸時的訊息物件,所以也才會有下面兩行assign req的內容的程式碼,因為指定好request是client的責任。

最後實際執行的部分就由client.call(srv)來做,這邊鼓勵大家去改一下Server的部分,試著亂填res,看看client會發出什麼訊息,有這種自己動手玩的體驗比較容易弄懂整個系統的運作原理。

1.3 Building your nodes

這一小節的編譯大家應該都滿熟悉了,就不額外說明啦。

Again,第三篇(Examine那篇)的原文有cover到怎麼執行剛剛編譯完成的執行檔,大家執行起來應該沒什麼問題。相信在看完這兩篇並動手操作過後,大家都怎麼使用ROS的API有初步的概念了,其實大多數ROS package中使用ROS的部分都不難,難的還是在於裡面的演算法,等你有機會去摸到一些程式碼,你就會了解我的意思了。

ROS

ROS tutorials 系列(15) – Where Next?

原文: http://wiki.ros.org/ROS/Tutorials/WhereNext

這一篇是官方教學文件中,beginner level裡的最後一篇,很高興可以把這些文章都提供一些我自己的學習經驗跟comment,希望這些文章可以幫助更多人入門,然後如果你有興趣,也可以去看看intermediate level的文章(雖然這些文章對於基本功能的使用者應該暫時不會有太大的幫助)。
————————————————————————————

1.1 Launching a Simulator

模擬器是一個很好玩的東西,因為你只要有電腦,就可以模擬自己的一隻機器人,這大幅地降低了學習的成本,此外,就算你有機器人,用模擬器先測試演算法也可以避免損壞硬體,好處多多。

甚至,如果你想做自己的機器人,如果能先模擬機器人的行為,先確保機構設計等等的沒有問題,再進行加工跟組裝,絕對會比作出來才發現硬體設計不良還要節省成本。

1.3 Understanding TF

這一小節的敘述滿中肯的,如果你要做自己的機器人,那你一定要了解tf跟urdf(只要建立好urdf,就可以用robot_state_publisher自動發佈機器人的tf tree)。

1.4 Going Deeper

看到這邊,相信你已經有能力使用ROS來進行一些更深入的學習了,我滿推薦這個問題中Devon Ash的回答,如果有機會推薦可以看看。

ROS

ROS tutorials 系列(14) – Navigating the ROS wiki

原文: http://wiki.ros.org/ROS/Tutorials/NavigatingTheWiki

這篇教學雖然跟寫程式和使用ROS沒有直接關係,但裡面清楚說明了ROS wiki的編排方式,有一些東西是平常我們直接用Google搜尋會忽略的頁面,如果你能夠了解wiki,有時候會比較容易找到自己需要的資料。

————————————————————————————

2. Basics

這邊都只是簡單的敘述,就不說明了,我自己曾經覺得wiki好用的經驗是,如果你需要了解某個package的API,那你只需要到某個package頁面的側邊攔,尋找Code API的link就可以找到,這樣找常常比直接用google搜尋還方便。

不過現在有些新的package的文件都在github上面了,所以還是要兩者搭配一起看比較好。

3. Advanced

比較有趣的是第三小節,這一小節很簡略地說明了要怎麼開啟一個新的ROS wiki。

不過如果你真的需要建立自己的wiki page,可能還是先參考怎麼release一個package跟這一篇教學會比較好。

ROS

ROS tutorials 系列(10) – Writing a Simple Publisher and Subscriber

原文: (因為C++版本跟Python版本的內容差不多,還有Examine那篇也只有幾個指令,所以乾脆一併寫,不過我主要只會就C++那篇的內容來說明,因為其他東西大同小異)

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28python%29

http://wiki.ros.org/ROS/Tutorials/ExaminingPublisherSubscriber

這一篇教學總算cover到比較需要寫程式的部分,主要就是教大家怎麼寫兩個node,並透過topic傳輸。接下來就來看看這個程式要怎麼寫。
————————————————————————————

1.1 Writing the Publisher Node

這邊提到的publisher就是負責把資料丟到topic的那個node。這一小節的重點實際上是在1.1.2小節的詳細解釋裡面,我就比較重要的地方增加一點說明。

基本上,include “ros/ros.h”這一步就是讓你的程式可以成為ROS Node的一個起點,因為要成為ROS Node,你的程式就必須呼叫ROS提供的API,所以你必須先include ros.h,才能開始使用ROS的API。

接下來的問題就是,怎麼使用ROS的API?

原本的tutorial裡面已經講得滿清楚了,其實就是用ros::init()跟ros::NodeHandle來初始化你的node,然後你就可以用NodeHandle初始化publisher的物件,之後再呼叫publisher物件的publish()就可以把資料丟出去了。

看到後面大家可能會有個疑惑,就是不知道為什麼要注意發佈的頻率? 我自己的經驗是,如果publisher跟subscriber指定的頻率不一致,就會漏收很多資料,導致機器人的行為怪怪的(這個bug曾經卡了我一天,因為怎麼看都覺得程式碼沒問題,但機器人都不會按照我要的路徑行走,後來才發現送trajectory跟收trajectory的node頻率不一致)

1.2 Writing the Subscriber Node

這一小節應該比較好理解,程式主要要做的事情就是讓subscriber收到資料之後,呼叫callback function把收到的資料印出來,之所以要這樣設計也很單純,因為你在寫程式的時候根本不可能知道什麼時候會收到資料,所以你當然也就不可能先在main裡面指定好什麼時候要收資料。

Again,1.2.2才是重點,這邊比較要注意的是ros::spin()這個函式,如果你的listener程式裡面沒有這一行,那你一執行listener,程式就會立刻跑完,因為程式該執行的東西都已經執行完了。為了避免這件事情,就需要ros::spin(),他在做的事情有點像是while(ros::ok()),但還是讓你的callback function可以被呼叫。

1.3 Building your nodes

這一小節只是在教大家怎麼進行編譯的步驟,應該就不太需要再多說明了。

最後,第三篇的原文有cover到怎麼執行剛剛編譯完成的執行檔,我想這個應該就不需要再多加註解了。

ROS

ROS tutorials 系列(12) – Recording and playing back data

原文: http://wiki.ros.org/ROS/Tutorials/Recording%20and%20playing%20back%20data

這一篇教學主要是要敎大家怎麼把機器人的狀態和接收到的data等等資訊都先錄下來(錄成bag檔),這樣在測試演算法的時候,就不需要每次都啟動機器人或是模擬的環境,只要把錄好的資料播放出來,就可以讓演算法得到需要的資料。舉例來說,如果我希望測試機器人的建地圖演算法,那我只要讓機器人實際走一次,並在過程中用這篇的技巧把雷射測距儀收到的資料存成bag檔,之後只要重播這個bag檔,得到的data就彷彿是讓這隻機器人再走一次相同的路徑。
————————————————————————————

1.1 Recording data & 1.2 Examining and playing back the bag file

這邊應該是不難,主要就是用rosbag record和rosbag play兩個指令而已,比較要注意的是,在錄的時候只會記錄有實際資料傳輸的topic,所以雖然在範例中有5個topic,但錄起來的bag檔只包含4個topic而已。

1.3 Recording a subset of the data

這邊只想補充一點,如果你懶得改檔名,但只想錄你要的topic,那就直接用下面的方法就好了。

rosbag record <topic 1> <topic 2>

1.4 The limitations of rosbag record/play

這一小節有提到,rosbag錄下來的資料其實還是跟原本的資料會有非常細微的誤差,不過如果你是要測試演算法,只要每次播放出來的資料一樣,那也沒什麼大礙。

ROS

ROS tutorials 系列(9) Creating a ROS msg and srv

原文: http://wiki.ros.org/ROS/Tutorials/CreatingMsgAndSrv

這一篇教學教了兩個滿重要的技巧,怎麼定義自己的message格式跟service格式,還記得我們之前提到不管是topic或是service,node在溝通的時候都需要遵守一個格式,兩個node才能順利溝通。如果你需要用到的msg或srv格式都已經有人寫過,那你可以忽略這篇,但通常沒有這麼好的事情,所以這一篇算是自己要撰寫一個運行在ROS上的複雜系統所必須具備的基本功之一。
————————————————————————————

1.1 Introduction to msg and srv

這一小節是這篇裡面最重要的,因為只要抓到msg跟srv的核心觀念,後面的理解就比較不會有見樹不見林的感覺。

我們要定義一個msg file的基本觀念很簡單,只要新增一個.msg檔,裡面輸入幾個msg要包含的資料型別跟名稱就可以了。重點在於,經過一些後續的步驟之後,ROS的編譯系統會自動把這個.msg檔編譯成header file等等,這樣你的程式裡面才可以使用這個定義好的msg格式。

srv檔的概念跟msg檔是類似的,只是因為service分成request跟response,所以.srv檔中要分成兩塊,我想這個一看1.1中的範例就可以理解了。

1.2 Using msg

這一小節講到的步驟滿重要的,我想對於初學者來說勢必是會有點暈頭轉向,覺得步驟很多很繁複,不過這只是因為沒有理解這些步驟才會覺得混亂,現在來把這些步驟說明一下。

基本上,編譯msg檔和在runtime使用編譯好的檔案都是要用額外的package來處理,所以第一步就是要先在package.xml裡面加上跟message_generation和message_runtime的相依性。這一步還沒結束,你必須在CMakeLists.txt裡面的find_package部分裡面加上message_generation、catkin_package部分加上message_runtime才算把相依性搞定。

那下一步當然就是要讓編譯器知道你想要編譯你的.msg檔,所以先使用add_message_files把想編譯的.msg檔指定好,再用generate_messages跟編譯器說幫我編譯.msg檔。

做完上面這些步驟之後,catkin_make時就會產生該產生的檔案,也才能讓你用rosmsg查看這個格式。

1.3 & After

後面的部分應該都滿簡單的,只是讓你熟悉一下指令,另外srv檔的做法跟msg是差不多的,所以就不另外敘述了。

ROS

ROS tutorials 系列(8) Using rosed to edit files in ROS

原文: http://wiki.ros.org/ROS/Tutorials/UsingRosEd

————————————————————————————

1.1 Using rosed & 1.2 Using rosed with tab completion

rosed可以讓你直接編輯某個package裡面的某個檔案(預設是使用vim),而不需要取得檔案的絕對路徑(或得先切換到檔案所在的資料夾)。這個工具可以節省很多找檔案路徑的時間,對提升生產力滿有幫助的。

1.3 Editor

如果你不熟悉vim,熟悉的編輯器是nano或是emacs,這邊就有提供方法來更改rosed預設會開啟的編輯器。

ROS

ROS tutorials 系列(7) Using rqt_console and roslaunch

原文: http://wiki.ros.org/ROS/Tutorials/UsingRqtconsoleRoslaunch

這一篇講到了兩個重要的工具-rqt跟roslaunch,rqt是debug或是查看系統的一個重要工具,而roslaunch是將node啟動的程序變得很簡單的一個工具,如果要使用ROS來開發東西是一定會用到的兩個重要工具。

————————————————————————————

1.2 Using rqt_console and rqt_logger_level & 1.2.1

rqt_console只是用來查看log的工具,rqt_logger_level則幫助你設定某個node要顯示的訊息是要在什麼層級之上,例如在這篇範例中,我們將rosout的logger level設定到warn以上,那麼重要性比warn還低的訊息就不會被顯示出來。

這個工具在測試機器人的各種細部行為的時候會很有用,因為你可以設定某些條件下,node要發出warn以上的訊息,接下來你就可以大量測試,看看在哪些情況下會收到warn以上的訊息。

1.2.2 & After

這邊之後開始介紹roslaunch的用法,基本上roslaunch是要搭配launch file來使用,launch file是讓你把所有你要啟動的node、啟動這些node需要指定的參數都寫進去,所以當你要啟動這些node的時候,只需要roslaunch你寫的launch file即可,不必用rosrun一個個啟動所有的node(假設你有100個node,你一定也會想寫shell script自動啟動,所以他們提供launch file這個工具)。