猫を検出して自動でねこじゃらしを振るロボット、「自動ねこじゃらし」。 今回はそのしくみについて解説していきます。
システム構成
猫を検出してねこじゃらしを振るまでのしくみを解説します。
周囲を撮影し、画像処理 自動ねこじゃらしフロント部にはWEBカメラが付いています。 カメラで周囲の様子を常に撮影し、1枚1枚AIによる画像処理を行っています。 もし画像に猫が映っていた場合、AIから猫の情報が送られてきますので、それを読み取ります。 これらの一連の処理は、ラズパイ(Raspberry Pi)によって行います。
サーボモータの駆動 サーボモータでねこじゃらしを振ります。サーボは回転角度指定ができるモータで、ねこじゃらしを振る角度を自由に変えられます。
猫検出のしくみ
猫を画像から検出するのに、YOLOという物体検出AIを用いています。 物体検出とは、画像のどこに、何の物体があるかを判定するタスクのことです。 この技術は自動運転などにも用いられていて、人や標識、信号などを認識しています。 もちろん猫も検出することが可能ですので、物体検出を使うことにしました。
YOLOをラズパイで動かす
ラズパイでYOLOを動かすには、色々と準備が必要です。以下のサイトを参考に、TensorflowベースでYOLOを動かすことにしました。 https://mlbb1.blogspot.com/2019/12/raspberry-pi-yolo-v3-tiny-yolo-v3.html もともとYOLOはDarknetというフレームワークで動きますが、ラズパイで実行するとCPU使用率が30%程度と低く、処理速度が上がりません。Tensorflowベースで動かすことでCPU使用率は95%程度まで上がり、検出速度が向上します。 しかし、Tensorflowで動かすことによる問題もあります。それは、最新のYOLOに対応していないことです。現在のYOLOはv5までありますが、Tensorflowベースのものはv3までしかありません。(正確には存在しますが、ラズパイが対応していません) 最新のYOLOの方が検出精度が高いためできれば最新を使いたいところですが、今回は処理速度を取ってTensorflowベースのYOLOv3で画像処理することにしました。
YOLOでの処理後のデータを取得する
上記サイトでラズパイに環境構築ができたら、ラズパイ内に「Tensorflow-YOLOv3」というディレクトリが生成されています。その中にある「detect.py」を実行することで物体検出のデモを行えるわけですが、今回はこのサンプルコードを基にサーボモータを動かせるように変更しました。 まずは、この「detect.py」内で猫を検出した際にどこに何の値が格納されるのか調べる必要があります。 「detect.py」65行目に、以下の記述があります
draw_boxes_frame(frame, frame_size, result, class_names, model.input_size)
この、resultという変数に、画像処理した結果が以下のように格納されています。
[{
0: array([], shape=(0, 5), dtype=float32),
1: array([], shape=(0, 5), dtype=float32),
2: array([[ 61.721302 , 190.79712 , 79.95384 , 198.68335 ,0.35832775]], dtype=float32),
3: array([], shape=(0, 5), dtype=float32),
4: array([], shape=(0, 5), dtype=float32),
…
79: array([], shape=(0, 5), dtype=float32)
}]
上記は、detect.pyを実行してresultをprintfしたものの一部です。 辞書型になっていて、キー(左の数字)は0~79まで存在します。YOLOはデフォルトで80種類の物体を検出でき、0~79はそれに対応しています。 上記の結果では2番キーにだけ値が格納されています。つまり、80種類のうち0から数えて3番目の物体が検出されましたよ、ということです。(小数点付きの数字は検出された物体の画像内での座標を示しています。) ちなみに、3番目の物体は「car」でした。 0~79番まである中で、猫は0から数えて16番目です。つまり、15番のキーの値があるかどうかを確認すればいいわけです。 pythonでは、以下のように実装します。
s = result[0]
if s.get(15).size > 0:
servo_move()
データを見て分かるように、配列の中に辞書が入っています。 [{data}]←このようになっている そこで、s =result[0]で辞書だけ抜き出し、
s.get(15).size > 0
で15番キーの値が0以上だったら(猫がいたら)servo_move()を実行する、というようなしくみになっています。
サーボモータの駆動
猫の信号を取り出すことができたら、サーボに信号を送ります。
サーボはパルス波を送ることで駆動でき、そのパルス幅によって回転角度が変化します。
今回使用したサーボはMG996Rという型番です。
以下のサイトを参考に、サーボを振るだけのプログラムを作成しました。
http://pongsuke.hatenablog.com/entry/2017/01/11/123541
def servo_move():
PIN = 21
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN, GPIO.OUT)
servo = GPIO.PWM(PIN, 50) # GPIO.PWM(PIN, [周波数(Hz)])
val = [2.5,3.6875,4.875,6.0625,7.25,8.4375,9.625,10.8125,12]
# サーボ動かすプログラム
servo.start(0.0)
servo.ChangeDutyCycle(val[0])
time.sleep(0.5)
servo.ChangeDutyCycle(val[2])
time.sleep(0.5)
servo.stop(0.0)
50Hzでパルス波を出力し、0.5秒ごとにサーボが動きます。
3Dプリンタでの筐体作成
3Dプリンタにて、筐体を作成します。 3DCADにて設計し、3Dプリンタx-proにて出力しました。 使用した3DCADはInventorです。丸いフォルムにしました。
丸い筐体の片方です。もう片方も半円です。 3Dプリント QIDI Tech社のx-proという3Dプリンタで出力しました。
両面合わせて12時間ほどかかりました。
完成
中にはサーボ、カメラを内蔵し、ラズパイは筐体の外に配置しています。
今回はプロトタイプということでラズパイやサーボの電源を筐体外部に設置していますが、もし製品化する際は全て筐体の中に入れたいです。その際はバッテリーマネジメント、排熱、耐久性などもテストしていく必要がありますね。 以上で、自動ねこじゃらしのシステム解説を終了します。
弊社IoTページ
レフィクシアでは画像処理、IoT機器の開発を行っています!
共同開発、試作などのご相談はレフィクシアまで。
Comments