今月のやること.ROS Navigation Stack
おはようございます.昨日宣言した通り,これから毎月/毎週の目標と結果を書いていきます.
昨年のつくばチャレンジでは,画像処理の実装に時間がかかりすぎて制御周りが間に合わなかったので,今年はまずここから始めたいと思います.ロボットの足回りはとりあえずそれとなく動くものができているので,今月はロボットの足回りを ROS の Navigation Stack を用いて動かすようにします.各週の細かいTODOはやりながらおいおい更新します.
1. Week of 2017/2/6
Navigation StackのWiki読み.システム構成の理解.ロボット側のインターフェースの実装.
2. Week of 2017/2/13
Navigation Stackを用いたロボットの前進・右折・左折
3. Week of 2017/2/20
Odometryフレーム内でのロボットの制御
4. Week of 2017/2/27
??
というとこで,乞うご期待!
Road To つくばチャレンジ2017 ~ 完走へ向けて!~
ということで,「2016年を振り返って」という若干後ろ向きなエントリを書いた数分後に,「完走へ向けて」なんていう無謀なエントリを書いてしまいます....
2017年が明けてもう一か月が経過しようとしているのですが,年始に会社の研修発表会があった関係でしばらくロボットはお預けになってました.先週末からようやく時間が取れ始めたので,またロボットいじりを始めたのですが,早速一つ壁にぶち当たりました....
「モチベーションが上がってこない...」
我ながら困っているのですが,本番まで9ヶ月ある現在,ちょっと時間がありすぎでやる気がいまいちわいてきません...が,昨年いやというほど思い知ったのですが,この時期からコツコツ積み上げないと,とてもじゃないが11月の本番に間に合わないのもこれまた事実で...やる気がいまいちわいてこない理由を自分なりに考えてみたのですが,
いまさらですが,「自作で自律走行するロボットを作成する.」というハードルが思った以上に高く,何から手を付けていいかわからない状態になっていることが原因なんだろうなと思います.知り合いには,「ちゃんと計画立てないからだよ.」と言われたのですが,全体像がすっきり見えてないものに対して,計画立てるのって結構しんどいんですよね...アルゴリズム実装するにしても,予想してたほど精度が出なくて,ボツになったりすると,速攻で計画くるってしまうし...
ということで,今年の進めかたを考えました.
- 月ごとに目標・プランニングをしてブログで公表する.
- 週ごとに達成内容をブログで残す.(備忘録・整理目的)
自分の知識不足もあり,年単位のプランニングがやっぱり難しいので,月ごとに取り組む内容を決めて,ブログで公表します.で,週ごとの成果をブログで公表します.
- まず,できる限りありものを使って,走るところまで持っていく.
フレームワークとかライブラリとかの使い方を勉強するのがめんどくさくて,昨年はもっとシンプルなものを自分で作れないかと思っていたのですが,まずはありものをできる限りパクる・再利用するという方針で作り上げていきたいと思います.
ということで,3日坊主になる可能性もありそうですが,,温かく見守ってください.次のエントリで書きますが,今月はROSのNavigationスタックの勉強・実装をします.
2016年を振り返って...
現在,朝7時...今年は早寝・早起きを習慣にしようととりあえず毎朝5時に起きてます.昨日から(笑)
2016年は転職から始まり,つくばチャレンジ2016参加,SI2016でのつくばチャレンジ取り組み発表,会社の研修・発表会等,いろいろありました.ばたばたといろいろ動きましたが,反省点も多々あり,,,というか,振り返ると反省点ばかりだったような気がします.
結局のところ,2016年もやっぱりつくばチャレンジが一番のメインイベントだったのですが,なかなか思うようにいかず...参加2年目ということで,「2016年は完走するぞ!」と,ガチで(年初は)意気込んでいたのですが,結果的には全然間に合わず2015年の内容とほぼ同じになってしまいました.
ハード作成の時は,トライアンドエラーでとにかく手を動かして,無理くりいろんなものをくっつけていったらなんとなく動くところまでは行ったのですが,ソフト作成に関しては思ったようにいきませんでした.まず,ROSとかOpenCVなんかのライブラリ・フレームワークのキャッチアップをして,それからアルゴリズムの勉強をして,,,,という感じで,「作る」前に「理解」することが前提で,全然間に合いませんでした.
2017年は取り組み方をもう少し工夫しないといけないなと思ってます.この辺は,次の投稿で考えをまとめられれば!今年は,一週間に最低一つは記事を書きます!(書くよう努力します(笑))下のスライドにも書いてありますが,「素人がGoogle先生だけを頼りに,0からロボットを作ったらどうなるか?」をテーマに今年も頑張りますので,一つよろしくお願いします.<(_ _)>
下記,SI2016での発表資料です.今回は何か技術的に中身のある発表にしたかったのですが,結局取り組み紹介で終わってしまいましたが...
写真は,SI2016で訪れた札幌の写真.
以上,旧正月の挨拶でした!
パソコン環境設定 2017
今年もつくばチャレンジ2017に向けて、まずは環境のセットアップということで早速準備。
備忘録として、インストール一覧をメモメモ。
Windows
Chrome
Visual Studio 2015, Community
ITunes
Office
Anaconda
さくらエディタ
- sudo apt-get update
- sudo apt-get install libgconf2-4 libnss3-1d libxss1
で、インストールした Chrome を実行。
ibus-mozc
- sudo apt-get install ibus-mozc
で、再起動後に Text Entry Setting であらたに追加された Mozc を選択。
日本語キーボード対応
のlayoutの部分を変更する。
default -> jp
ROS Kinetic
下記のチュートリアル通りに。
kinetic/Installation/Ubuntu - ROS Wiki
JDK
Eclipse を動かすための JAVA 環境のインストール。
- sudo apt-get install default-jre
Eclipse
下記から Eclipse IDE for C/C++ Developers のインストール。Neonってのが一番新しいらしい。
http://www.eclipse.org/downloads/eclipse-packages/
Eclipse を /opt/ 以下に移動して、実行ファイルのパスを通す。
PATH="$PATH:/opt/eclipse/"
turtle-bot のシミュレーション関係一式
turtlebot/Tutorials/indigo/Turtlebot Installation - ROS Wiki
sudo apt-get install ros-kinetic-turtlebot ros-kinetic-turtlebot-apps ros-kinetic-turtlebot-interactions ros-kinetic-turtlebot-simulator ros-kinetic-kobuki-ftdi ros-kinetic-ar-track-alvar-msgs ros-kinetic-turtlebot-gazebo
Python リストのソートの仕方
機械学習の勉強にPythonを使ってるんですが,ちょっとしたデータ処理したりするの,めっちゃ使いやすいですね.
結構大きいデータ使ってるんですが,そこそこの速度で動いてくれるし.
ちょっと,Pythonのリストをソートする方法を調べてたんですが,備忘録としてメモしておきます.
例えば,名簿のリストがあったとします.
デフォルトでは,アルファベット順で並んでますが,同時に各人の身長のデータも持っていて,身長順にリストをソートしたいとします.
この時には,sorted関数を使えます.
['crues', 'hanks', 'mike', 'samp', 'tifanny', 'tom']
下記,名前が与えられたらその人の身長を返すメソッドだとします.
def orderFunc(k): if (k == "sam"): return 175 elif (k == "mike"): return 180 elif (k == "tom"): return 150 elif (k == "hanks"): return 175 elif (k == "crues"): return 190 elif (k == "tifanny"): return 200 return 0
で,上記のメソッドの名前を sorted 関数に渡してあげます.
In [180]: sorted(namelist, key=orderFunc, reverse=True) Out[180]: ['tifanny', 'crues', 'mike', 'hanks', 'sam', 'tom']
めっちゃ便利ですね...
Pandas の使いかた
およそ半年ぶりの更新ですが,何事もなかったかのように平然とエントリをし始めましたが...
(つくばチャレンジにも参加しました.結果は残念でしたが...また取り組みをアップします.)
最近会社で機械学習の勉強をしていて,その際に Pandas というライブラリを使う必要があったので,概要と使い方を備忘録としてまとめておきます.
勉強に使ったのは下記の本です.一年半くらい前に買って,積読状態になっていたので,活用できる機会ができてよかった...
Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (17件) を見る
- pandas のデータ構造
大まかにいうと,Series と DataFrame という二種類のデータ構造がある.シリーズというのは,一次元の配列らしいが,Numpyの配列と異なるところは,インデックスというデータラベルの配列がつくところ.まず,IPython を使って Series で遊んでみます.
ライブラリのインポート.
from pandas import Series, DataFrame import pandas as pd
- シリーズオブジェクトの作成
In [131]: series = Series([1, 10, 100, 1000]) In [132]: series Out[132]: 0 1 1 10 2 100 3 1000 dtype: int64
上の出力を見てみると,Numpyの配列とは違って,勝手にインデックスが付与されていることがわかります.
また,このインデックスに関しては,自分で好きなものを付与することもできます.
- インデックスラベルを指定したシリーズオブジェクトの作成
In [134]: series_w_index_specified = Series([1, 10, 100, 1000], index=['JPN', 'USA', 'GER', 'GBR']) In [135]: series_w_index_specified Out[135]: JPN 1 USA 10 GER 100 GBR 1000 dtype: int64
- データフレームオブジェクトの作成
シリーズが一次元の配列であるのに対し,こちらは二次元の配列というかテーブル風のデータ構造.
In [151]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], ...: 'year' : ['2000', '2001', '2002', '2001', '2002'], ...: 'pop' :['1.5', '1.7', '3.6', '2.4', '2.9']} In [152]: frame = DataFrame(data) In [153]: frame In [154]: Out[153]: pop state year 0 1.5 Ohio 2000 1 1.7 Ohio 2001 2 3.6 Ohio 2002 3 2.4 Nevada 2001 4 2.9 Nevada 2002
- データセットのマージ
これが使いたかったんです.以前 SQL を使ってデータベースの処理をしていたことがあったんですが,テーブルの結合とかと等価の処理を自分で作るのは時間もかかるしめんどくさいし..ということで,pandas を使ってデータセットをマージする方法をメモ.
pandas.merge : データフレームに含まれる行を一つ以上のキーでマージできる.
pandas.concat : 複数の列のオブジェクトを貼り合わせたり,積み上げたりすることが可能.
-
- pandas.merge を使ったデータフレームのマージ
In [9]: df1 = DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], ...: 'data1': range(7)}) In [10]: df2 = DataFrame({'key': ['a', 'b', 'd'], ...: 'data2': range(3)}) In [11]: df1 Out[11]: data1 key 0 0 b 1 1 b 2 2 a 3 3 c 4 4 a 5 5 a 6 6 b In [12]: df2 Out[12]: data2 key 0 0 a 1 1 b 2 2 d
上で作成したデータを,pandas.merge を使ってマージします.
超簡単ですね.ありがたやありがたや.
In [15]: pd.merge(df1, df2, on = 'key') Out[15]: data1 key data2 0 0 b 1 1 1 b 1 2 6 b 1 3 2 a 0 4 4 a 0 5 5 a 0
結合する列の名前が左右のテーブルで異なるときもあると思いますが,こちらに関しても下記のようにすれば結合できるみたいです.
両方にキーが存在する行のみがマージされた結果に残されている様子が見て取れます.この結合を内部結合といい,結果に表れるキーは両者の共通集合になります.
In [22]: df3 = DataFrame({'lkey':['b', 'b', 'a', 'c', 'a', 'a', 'b'], ...: 'data1': range(7)}) In [23]: df4 = DataFrame({'rkey':['a', 'b', 'd'], ...: 'data2': range(3)}) In [24]: pd.merge(df3, df4, left_on='lkey', right_on='rkey') Out[24]: data1 lkey data2 rkey 0 0 b 1 b 1 1 b 1 b 2 6 b 1 b 3 2 a 0 a 4 4 a 0 a 5 5 a 0 a
一方で,どちらか片方に存在するキーも消したくない場合もあると思います.この場合には,外部結合を実施すれば結合できます.
ただし,ペアとなるキーが相手方に存在しない場合はそのデータはNaNとなります.下記の実行結果を見てもわかる通り,結合ペアのキーのない箇所はデータが NaN になっています.
In [25]: pd.merge(df3, df4, left_on='lkey', right_on='rkey', how='outer') Out[25]: data1 lkey data2 rkey 0 0.0 b 1.0 b 1 1.0 b 1.0 b 2 6.0 b 1.0 b 3 2.0 a 0.0 a 4 4.0 a 0.0 a 5 5.0 a 0.0 a 6 3.0 c NaN NaN 7 NaN NaN 2.0 d
OpenCV Stereo Matching の性能比較 1
前回の投稿から少し時間が空いてしまいました...が,ブログ更新が飽きたというわけではなく,ちょっと更新するほどのまとまったネタができなかったわけです.というわけで,今日はつくばチャレンジで使うステレオマッチングメソッドの比較・選定を行いました.とりあえず,OpenCVでロジックが提供されている下記の3つのアルゴリズムを比較検討しました.
- Block Matching Method
- Stereo Block Matching Method
- Stereo Graph Cut Method
0. 実験に用いたベースデータ
実験には,手軽に手に入る Tsukuba の視差画像を用いました.
左画像 サイズ 384 x 288
右画像 サイズ 384 x 288
1. Block Matching Method
一番オーソドックスなアルゴリズムです.実験結果は下記の通り.
実行時間 3.3464ms (334.646 / 100)
2. Semi Global Matching Method
一番良く使われているアルゴリズム?ではないでしょうか.
実行時間 16.6549ms (1665.49 / 100)
3. Stereo Graph Cut Method
実行時間が結構長いです..が,結果も一番きれいです.解説にあるとおり,オフラインでの使用前提ですね.
実行時間 6664.27ms (66642.7 / 10)
実際には,パラメータを適切に調整して比較したわけではないので純粋に比較できているわけでは無いです.次のエントリではこの3つの手法を自分が撮影した画像に適用して見たいと思います.下記,実験に使ったソースコードをおいておきます.
1. Block Matching Method
#include <iostream> using namespace std; #include "opencv2/opencv.hpp" int main() { const char *rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm"; const char *leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm"; // const char *rightImg = "/home/koichi/Desktop/Stereo Pair/pentR.bmp"; // const char *leftImg = "/home/koichi/Desktop/Stereo Pair/pentL.bmp"; IplImage *imgR, *imgL, *dst; IplImage *dispLeft, *dispRight; int disp = 32; // CvStereoGCState *state = cvCreateStereoGCState(disp, 4); CvStereoBMState *state = cvCreateStereoBMState(CV_STEREO_BM_BASIC, disp); state->SADWindowSize = 9; // . Load Image. imgR = cvLoadImage(rightImg, CV_LOAD_IMAGE_GRAYSCALE); imgL = cvLoadImage(leftImg, CV_LOAD_IMAGE_GRAYSCALE); CvSize size; size.width = imgR->width; size.height = imgR->height; // . Allocate Memory dispLeft = cvCreateImage(size, IPL_DEPTH_16S, 1); dispRight = cvCreateImage(size, IPL_DEPTH_16S, 1); dst = cvCreateImage(size, IPL_DEPTH_8U, 1); { cv::TickMeter meter; meter.start(); for (int i = 0; i < 100; i++) { // Calcuate Disparity cvFindStereoCorrespondenceBM(imgL, imgR, dispLeft, state); } meter.stop(); std::cout << "Average Time for Calculation : " << meter.getTimeMilli() / 100.0 << std::endl; } double min, max; cvMinMaxLoc(dispLeft, &min, &max); cvConvertScale(dispLeft, dst, 255 / (max - min), 255 / min); // cvNormalize(dispLeft, dst, 1, 0, CV_MINMAX); const char *winName = "Stereo Correspondence"; cvNamedWindow("Right Image", CV_WINDOW_AUTOSIZE); cvNamedWindow("Left Image", CV_WINDOW_AUTOSIZE); cvNamedWindow("Disparity", CV_WINDOW_AUTOSIZE); cvShowImage("Right Image", imgR); cvShowImage("Left Image", imgL); cvShowImage("Disparity", dst); cvWaitKey(0); // cvReleaseStereoGCState(&state); cvDestroyAllWindows(); cvReleaseImage(&imgR); cvReleaseImage(&imgL); return 0; }
2. Semi Global Matching Method
#include <iostream> #include <string> using namespace std; #include "opencv2/opencv.hpp" int main() { string rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm"; string leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm"; cv::Mat imgR = cv::imread(rightImg, CV_8UC1); cv::Mat imgL = cv::imread(leftImg, CV_8UC1); cv::Mat dispMat(imgR.rows, imgR.cols, CV_8UC1); int disp = 32; int minDisparity = 0; int numDisparities = 32; int SADWindowSize = 3; int P1 = 0; int P2 = 0; int disp12MaxDiff = 0; int preFilterCap = 0; int uniquenessRatio = 0; int speckleWindowSize = 0; int speckleRange = 0; bool fullDp = false; cv::StereoSGBM sgbm(minDisparity, numDisparities, SADWindowSize, P1, P2, disp12MaxDiff, preFilterCap, uniquenessRatio, speckleWindowSize, speckleRange, fullDp); { cv::TickMeter meter; meter.start(); for (int i = 0; i < 100; i++) { sgbm(imgL, imgR, dispMat); } meter.stop(); std::cout << "Average Time for Calculation : " << meter.getTimeMilli() / 100.0 << std::endl; } double min, max; cv::minMaxLoc(dispMat, &min, &max); cv::convertScaleAbs(dispMat, dispMat, 255 / (max - min), 255 / min); cv::namedWindow("Right Image", CV_WINDOW_AUTOSIZE); cv::namedWindow("Left Image", CV_WINDOW_AUTOSIZE); cv::namedWindow("Disparity", CV_WINDOW_AUTOSIZE); cv::imshow("Right Image", imgR); cv::imshow("Left Image", imgL); cv::imshow("Disparity", dispMat); cvWaitKey(0); cvDestroyAllWindows(); return 0; }
3. Stereo Graph Cut Method
#include <iostream> using namespace std; #include "opencv2/opencv.hpp" int main() { const char *rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm"; const char *leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm"; // const char *rightImg = "/home/koichi/Desktop/Stereo Pair/pentR.bmp"; // const char *leftImg = "/home/koichi/Desktop/Stereo Pair/pentL.bmp"; IplImage *imgR, *imgL, *dst; IplImage *dispLeft, *dispRight; int disp = 32; CvStereoGCState *state = cvCreateStereoGCState(disp, 4); // . Load Image. imgR = cvLoadImage(rightImg, CV_LOAD_IMAGE_GRAYSCALE); imgL = cvLoadImage(leftImg, CV_LOAD_IMAGE_GRAYSCALE); CvSize size; size.width = imgR->width; size.height = imgR->height; // . Allocate Memory dispLeft = cvCreateImage(size, IPL_DEPTH_16S, 1); dispRight = cvCreateImage(size, IPL_DEPTH_16S, 1); dst = cvCreateImage(size, IPL_DEPTH_8U, 1); // Calcuate Disparity { cv::TickMeter meter; meter.start(); int repeat = 10; for (int i = 0; i < repeat; i++) { cvFindStereoCorrespondenceGC(imgL, imgR, dispLeft, dispRight, state, 0); } meter.stop(); std::cout << "Average Time for Calculation : " << meter.getTimeMilli() / repeat << std::endl; } cvConvertScale(dispLeft, dst, -256 / disp); const char *winName = "Stereo Correspondence"; cvNamedWindow("Right Image", CV_WINDOW_AUTOSIZE); cvNamedWindow("Left Image", CV_WINDOW_AUTOSIZE); cvNamedWindow("Disparity", CV_WINDOW_AUTOSIZE); cvShowImage("Right Image", imgR); cvShowImage("Left Image", imgL); cvShowImage("Disparity", dst); cvWaitKey(0); // cvReleaseStereoGCState(&state); cvDestroyAllWindows(); cvReleaseImage(&imgR); cvReleaseImage(&imgL); return 0; }