PythonとArduinoで検出物体を追従するカメラを作ってみた::Deep Learningの応用1

G.W.は時間があるのでブログが進みます。
今日は、PythonとArduinoを連携させ、検出した物体を追従するカメラを作ったので紹介します。
ポイントはDeep learningによる物体検出(SSD)とArduinoを連携させたことです。
単なる形状認識や特徴認識とは異なります。
動画紹介
まずは次の動画をみてください。
物体検出にはSSDを使用しています。
SSDって何?って思った方は過去記事を参照してください。ちなみに、Solid State Drive ではありません!
物体が検出されると、PythonからArduinoにカメラを動かす命令がでて、その通りにWebカメラが動きます。
追従させたい物体をあらかじめDeep learningで学習させておけば、後はWebカメラが自動で追いかけてくれます。原理的に学習するほど精度が上がるため追従精度も向上します。
概要
- 追従させたい物体をDeep Learningで学習
- 物体検出にはSSDを使用
- 転移学習を利用し検出精度を上げたモデルを作成
- ArduinoはNANOを使用(ブレッドボードに挿せてコンパクトに設計できるため)
- Webカメラは組み込み用の軽いものを使用(カメラが重いと動かすの大変)
使用部材
使用部材一覧を下表にまとめます。
部品 | 数量 | 値段(参考) |
---|---|---|
Arduino NANO | 1 | 3,050 |
ブレッドボード | 1 | 270 |
ジャンパーワイヤ(単線タイプ) | 1 | 517 |
ジャンパーワイヤ(フレキシブルワイヤ) | 1 | 583 |
Webカメラ(ELP フルHD 2MP) | 1 | 7,325 |
サーボモータ(SG-90) | 2 | 880 |
SG90サーボ用2軸アングル | 1 | 699 |
基板(タミヤ楽しい工作シリーズ) | 1 | 464 |
DC-POWER-JACK基板 | 1 | 439 |
ACアダプタ(5V/2A、外形5.5mm/内径2.1mm) | 1 | 980 |
USBケーブル 0.9m (2.0タイプAオス – マイクロBケーブル) ブラック | 1 | 631 |
合計 | 12 | 15,838 |
部品単体は安いですが、合計すると結構高いですね。
主に、Arduino NANOとWebカメラの値段です。ここを安くできればコストダウンできます。
Webカメラ外観
以下に外観を示します。


Arduino NANOとDC-POWER-JACK基板はブレッドボードに直接刺すことができます。
Arduino NANOは5Vで動作します。出力電圧は5Vと3.3Vです。
サーボモータが1~2個程度ならArduinoからの電力供給でサーボモータを動かすことができます。
ただし、Arduinoの動作が不安定になったり、最悪壊れることがあるためサーボモータは外部電源で動作させた方がよいと思います。
このため、DC-POWER-JACK基板を使って外部電源でサーボモータを動かすようにしています。
プログラム
以下のコードをみてください。
Arduino側
Arduino側はArduino IDEで作成します。ちなみに、ソースコードのことをスケッチと呼びます。
#include <VarSpeedServo.h>
VarSpeedServo mServo;
VarSpeedServo mServo1;
void setup() {
mServo.attach(6); // 6pin使用 回転台用
mServo.write(90,10,true); // サーボを90度まで速度10で動かし完了までまつ
mServo1.attach(9); // 9pin使用 カメラ用
mServo1.write(90,10,true); // サーボを90度まで速度10で動かし完了までまつ
delay(500); // 待ち時間 ms
Serial.begin(9600); // 通信速度 bps
}
void loop() {
int val[2] = {0}; // 受信データが複数ある場合はリストを作っておく
if (Serial.available() > 0){
delay(10);
for (int i = 0; i < 2; i++){
val[i] = Serial.parseInt();
}
// 受信バッファクリア
while (Serial.available() > 0){
char t = Serial.read();
}
mServo.write(val[0],50,true); // 回転台
mServo1.write(val[1],50,true); // カメラ
}
}
はじめに、ライブラリを読み込みます。
#include<VarSpeedServo.h>の部分です。VarSpeedServo がArduino IDEにまだインスト―ルされていないときはGitHubから入手してライブラリをインストールしてください。インスト―ル方法もGitHubに詳しく記載されています。
このライブラリを使用するとサーボの速度調整ができます。Arduinoの標準ライブラリにもServoが入っていますが、こちらは速度調整ができないのでカメラが滑らかに動きません。
次に、サーボモータを2個動かす必要があるので、変数として mServo と mServo1 の2つを定義しておきます。
Void setup( ) { } の { } の中に信号を出力する pin 番号やサーボの速度、通信速度を指定しておきます。
Void loop( ) { } の中にPythonからの命令を受け取るコードを記載します。
複数の命令を受け取りたいときは、val[ ] = { } のようにリストを作っておきます。
今回はカメラの角度を受け取る命令が1つ、回転台の角度を受け取る命令が1つで合計2つの入れ物が必要になります。
Serial.available( ) > 0 の意味は、信号がある場合は { } の中の処理を行いなさいということです。
つまり、val[0] とval[1] にPythonからの命令、この場合は角度の数値が入ります。
その後、受信バッファをクリアしておきます。
mServo.write( ) および mServo1.write( ) の部分で各サーボを指定の角度に動かします。
PythonからArduinoに命令が送られている間は、この動作を続けます。
Python側
以下のコードをみてください。
# pythonからarduinoに命令を送る
ser = serial.Serial('COM5', 9600, timeout=0.5)
time.sleep(2)
pre_center_x = 0
pre_center_y = 0
pre_angle_x = 90
pre_angle_y = 90
### arduinoに命令 ###
# 物体初期のセンター位置を調べる
center_x = int(pt[0]+((pt[2]-pt[0])//2))
center_y = int(pt[1]+((pt[3]-pt[1])//2))
# 画面センターと物体センターのズレ量をもとにサーボの回転量を決める
pt_dx_a = int((450-pre_center_x)*0.02)
pt_dy_a = int((-450+pre_center_y)*0.02)
# サーボを回転させる
ser.write((str(pre_angle_x+(pt_dx_a))+'\n').encode('utf-8')) # 回転台の角度 Val[0]に入る
ser.write((str(pre_angle_y+(pt_dy_a))+'\n').encode('utf-8')) # カメラの角度 Val[1]に入る
# サーボが回転した後の物体のセンター位置を調べる
center_x = int(pt[0]+((pt[2]-pt[0])//2))
center_y = int(pt[1]+((pt[3]-pt[1])//2))
# サーボ回転後のセンター位置を次のセンター位置とする
pre_center_x = center_x
pre_center_y = center_y
if pre_angle_x > 170:
pre_angle_x = 170 + pt_dx_a
elif pre_angle_x < 20:
pre_angle_x = 20 + pt_dx_a
else:
pre_angle_x = pre_angle_x + pt_dx_a
if pre_angle_y > 120:
pre_angle_y = 120 + pt_dy_a
elif pre_angle_y < 60:
pre_angle_y = 60 + pt_dy_a
else:
pre_angle_y = pre_angle_y + pt_dy_a
はじめに、serial.Serial( ) で通信ポートの番号、通信速度、timeoutの時間を設定します。
通信ポートはArduino NANOとの通信に使用しているポート番号を入力します。
通信速度は、さきほどArduino IDEのスケッチで設定した通信速度(9600)を記載します。この数値を合わせておかないと通信エラーとなります。
次に、pt[ ] と記載された変数にはSSDで物体検出した際の物体のx座標(x0, x1)とy座標(y0, y1)の数値が入ります。
その座標データを元に物体の中心と画面の中心を求めサーボを動かします。
なお、今回はWebカメラの映像サイズを900x900px としているため、画面中心の座標は(450, 450)です。
もし映像サイズを変える場合は、画面中心の座標も映像サイズに合わせ変える必要があります。
ちなみに、サーボの回転角度は以下のように求めています。
1.検出した物体の最初の中心座標を求めます。
2.次に画面の中心座標と物体の中心座標のズレ量を元にサーボの回転角度を求めます。
3.求めた回転角度でサーボを回転させます。
4.サーボが回転した後の物体の中心座標を求めます。
5.その中心座標を物体の最初の中心座標とします。
上記1~5を繰り返すことでWebカメラに映った物体が常に画面の中心に来るようサーボが動くことになります。
まとめ
- PythonとArduinoを連携させることで、電子工作の幅が広がる
- SSDとArduinoの連携により、Deep learning+IoTが簡単に実現できる
ディスカッション
コメント一覧
まだ、コメントがありません