カツオノカンムリ人生ブログ

アクセスカウンタ

zoom RSS ピースケ君(インコ)追尾監視カメラ

<<   作成日時 : 2017/01/31 00:20   >>

トラックバック 0 / コメント 2

たまに2、3日旅行などで家を空ける際、うちのインコちゃん達がどうしているか気になります。
そんな時のために先日オーディオで使ったラズベリーパイを今度はピースケ君追尾カメラとして利用することを考えました。
自動追尾より手動で見たいところにカメラを向ける方が間違いなく便利ですが…。便利かどうかではなく、画像解析に自律動作のほうがロボットぽくてカッコいい、ただそれだけです。

装置はやっつけで以前に作ったに2軸陣場を流用しました。
画像

2軸の左右方向移動は、台に両面テープとホットボンドでくっつけてます。
画像

上下方向は、左右用のサーボモーターの軸に凹形に曲げた薄いステンレス平板を曲げ、一番上に横向きにサーボモーターをつけてます。そのサーボモーターの軸に更に凹形を逆さまにしたものをネジで固定してカメラ台部分の出来上がりです。下の凹は左右に動き、カメラを載せる上下逆さまの凹形が前後に動きます。
サーボモーターが1000円くらい。金属板は100円ショップかなんかで買いました。
画像


カメラは、ラズパイ標準カメラ(右)で追尾し、外出先へのストリーミングはUSBカメラ(左)を使います。カメラを分けたのは成り行きですが、追尾用カメラは画像解析処理の関係から、320*240と解像度が低くせねばなりません。一方、ストリーミング用は処理を気にする必要がないので1280*720などHD画質でストリーミングできます。ですので分けるのは意味があるかなと思いました。

先ずは追尾の部分の実装からです。
結果このようになりました。

ラズパイもバージョン3になり、動きも滑らかになりましたね。

因みに黄色にも、多少好みがあるみたいです。濃いめの黄色が好きらしい。両方見せると、薄い黄色のほうはチラ見しかしません(笑)
単なる色設定の問題なのですが、まるで生き物みたいな動きになるのが面白いです。


プログラムは一番下に記載しました。
下記の本(実例で学ぶRaspberry Pi電子工作 (ブルーバックス))がベースというかほとんどです。内容もホントに良くて、今回の画像認識ロボも容易にできます。ただ色認識での追尾は乗ってません。それにしても以前C言語とかArduino組み合わせたりしてかなり悩みつつ作った顔追尾が、Pythonとラズベリーパイ3とこの本でいとも簡単にできてしまいました・・・。驚きです。
※ちなみにC言語のほうが速いと思いましたが、サーボモータの制御はPythonで全く問題なく、結局OpenCVの処理がほとんどのようでした。OpenCV部分はCでもPythonでも処理をするのはOpenCVのようで(多分)、別にC言語にこだわる必要はないのかな、と思いました。
ちょっと設定変えただけで毎回コンパイルとか、ほんと面倒です。Pythonではコンパイル地獄から解放され実験するのがずっと楽でした。

また色認識部分についてはこちら(http://tony-mooori.blogspot.jp/2015/10/python_27.html)を参考というかそのまま使わせていただき、本の円認識追尾のプログラムにマージしました。
よくわからないままマージしていますのでまだまだ改善の余地は沢山あると思います。

USBカメラのストリーミングについてはこちらのサイトそのままで出来きました。
http://make.bcde.jp/raspberry-pi/usb%E3%82%AB%E3%83%A1%E3%83%A9%E3%81%A7%E7%9B%A3%E8%A6%96%E3%82%AB%E3%83%A1%E3%83%A9/

ありがたいことです。

今回のものは、本を参考にサーボモータとカメラをラズパイに接続し、下記ソースと上記ストリーミングを同時に起動させれば実現できます。

面倒な2軸ジンバルについては、安くて良さげなものがアマゾンに売っていました…。自分で作ってもいいのですが、こちらのほうがむしろ安くつくと思いますし簡単かつ綺麗にできます。というか私もこれから買います。



以下ソース---------------------------------------
# -*- coding: utf-8 -*-
import picamera
import picamera.array
import cv2
import math
import wiringpi2 as wiringpi
import numpy as np

def getServoDutyHw(id, val):
val_min = 0
val_max = 4095
# デューティ比0%を0、100%を1024として数値を入力
servo_min = 36 # 50Hz(周期20ms)、デューティ比3.5%: 3.5*1024/100=約36
servo_max = 102 # 50Hz(周期20ms)、デューティ比10%: 10*1024/100=約102
if id==1:
servo_min = 53
servo_max = 85
duty = int((servo_min-servo_max)*(val-val_min)/(val_max-val_min) + servo_max)
# 一般的なサーボモーターはこちらを有効に
#duty = int((servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_min)
if duty > servo_max:
duty = servo_max
if duty < servo_min:
duty = servo_min
return duty

PWM0 = 18
PWM1 = 19

# wiringPiによるハードウェアPWM
wiringpi.wiringPiSetupGpio() # GPIO名で番号を指定する
wiringpi.pinMode(PWM0, wiringpi.GPIO.PWM_OUTPUT) # 左右方向のPWM出力を指定
wiringpi.pinMode(PWM1, wiringpi.GPIO.PWM_OUTPUT) # 上下方向のPWM出力を指定
wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS) # 周波数を固定するための設定
wiringpi.pwmSetClock(375) # 50 Hz。18750/(周波数) の計算値に近い整数
# PWMのピン番号とデフォルトのパルス幅をデューティ100%を1024として指定。
# ここでは6.75%に対応する69を指定
wiringpi.pwmWrite(PWM0, 69)
wiringpi.pwmWrite(PWM1, 69)

prev_x = 160
prev_y = 120
prev_input_x = 2048
prev_input_y = 2048

with picamera.PiCamera() as camera:
with picamera.array.PiRGBArray(camera) as stream:
camera.resolution = (320, 240)

while True:
# stream.arrayにBGRの順で映像データを格納
camera.capture(stream, 'bgr', use_video_port=True)

# 映像データをグレースケール画像HSVに変換
hsv = cv2.cvtColor(stream.array, cv2.COLOR_BGR2HSV)
# HSVによる色範囲指定
lower_yellow = np.array([20, 110, 110])
upper_yellow = np.array([40, 255, 255])
ex_img = cv2.inRange(hsv, lower_yellow, upper_yellow)

# ガウシアンぼかしを適用して、認識精度を上げる→イマイチで不採用
#ex_img = cv2.GaussianBlur(ex_imgorg, (9,9), 0)
# 要素がすべて1の縦ベクトル
vvec = np.ones((ex_img.shape[1],1))

# 要素がすべて1の横ベクトル
hvec = np.ones((1,ex_img.shape[0]))

# 1次元の縦ベクトル・横ベクトルを掛ける
hvec = np.dot(hvec,ex_img)
vvec = np.dot(ex_img,vvec)

# 積の中で最も大きい座標を抽出
circle_x = np.argmax(hvec)
circle_y = np.argmax(vvec)
#print circle_x, circle_y

#座標の処理→ちょっと良くわからない計算
mindist = 320+240
minindx = 0
indx = 0

dist = math.fabs(circle_x-prev_x) + math.fabs(circle_y-prev_y)
if dist < mindist:
mindist = dist
minindx = indx
indx += 1

# 現在の円の位置に赤い円を元の映像(system.array)上に描画
cv2.circle(stream.array, (circle_x, circle_y),12, (0,0,255),3)

# 抽出した座標に丸を描く
img_color = cv2.bitwise_and(stream.array, stream.array, mask=ex_img)

dx = circle_x-160 # 左右中央からのずれ
dy = circle_y-120 # 上下中央からのずれ

# サーボモーターを回転させる量を決める定数
ratio_x = 3
ratio_y = 3

duty0 = getServoDutyHw(0, ratio_x*dx + prev_input_x)
wiringpi.pwmWrite(PWM0, duty0)

duty1 = getServoDutyHw(1, ratio_y*dy + prev_input_y)
wiringpi.pwmWrite(PWM1, duty1)

# サーボモーターに対する入力値を更新
prev_input_x = ratio_x*dx + prev_input_x
if prev_input_x > 4095:
prev_input_x = 4095
if prev_input_x < 0:
prev_input_x = 0
prev_input_y = ratio_y*dy + prev_input_y
if prev_input_y > 4095:
prev_input_y = 4095
if prev_input_y < 0:
prev_input_y = 0

# 以前の円の位置を更新
prev_x = circle_x
prev_y = circle_y

# system.arrayをウインドウに表示
#cv2.imshow('frame', img_color)
#cv2.imshow('frame', stream.array)

# "q"を入力でアプリケーション終了
if cv2.waitKey(1) & 0xFF == ord('q'):
break

# streamをリセット
stream.seek(0)
stream.truncate()

cv2.destroyAllWindows()
以上--------------------------------------------------

テーマ

関連テーマ 一覧


月別リンク

コメント(2件)

内 容 ニックネーム/日時
はじめまして!
トマトと申します。。

その後、ご体調の方はいかがですか?

私も手術しますので
現在の状況など教えてくださると嬉しいです。
トマト
2017/02/13 11:36
トマトさん、
コメントありがとうございます。
見ていただける方がいるだけで励みになります。
コメント欄では書ききれない気がしましたので、ご参考になるかわかりませんが、ブログに現状等報告させて頂きました。
そあとれ
2017/02/13 14:58

コメントする help

ニックネーム
本 文
ピースケ君(インコ)追尾監視カメラ カツオノカンムリ人生ブログ/BIGLOBEウェブリブログ
文字サイズ:       閉じる