以前も同じような志向のものを作ったのですが、その時は駆動部分だけモータードライバを使って、映像の送信にはスマホを使うという変則的なやり方だったため心残りがあったんです。
さすがにESP8266で映像を送信するのは無理があるため、ボードをRaspberry Pi Zero Wに変えてリベンジを果たすことしました。
で、作ったのがこれです。
デモ映像もアップしました。
これから少しずつ作り方を書いていこうと思います。
使用したもの
ハードウェア(値段は2020年3月19日時点)
- Raspberry Pi Zero WH スイッチサイエンス 1,848円(税込)
- Raspberry Pi カメラモジュール V2 秋月電子通商 3,850円(税込)
- Raspberry Pi Zero用カメラケーブル aitendo 500円(税別)
- SparkFun Servo pHAT for Raspberry Pi スイッチサイエンス 1,505円(税込)
- Pan/Tilt 機構作成キット スイッチサイエンス 919円(税込)
- タミヤ ユニバーサルプレート(2枚セット) ヨドバシカメラ 510円(税込)
- タミヤ 32mm径 スプロケット&クローラーセット ヨドバシカメラ 381円(税込)
- 360°連続回転サーボ FS90R 秋月電子通商 540円(税込)2個
- 5mm白色LED OSWT5161A (2個入) 秋月電子通商 50円(税込)
- 多摩電子工業 モバイルバッテリー5200mAh 出力2.4A あきばお~ 1,100円(税込)
- SanDisk micro SDHC 16GB あきばお~ 355円(税込)
- スペーサー、ネジ、ワイヤーなど
パーツの特徴とか
全ての部品が半田付け不要
Raspberry Pi ZeroとServo pHATのヘッダーピンが既に半田付けされて売られているので、自分で半田付けする部品がありません。ぼくは半田付けが好きな方なので実はちょっと残念なんですけどね。基本は部品をはめ込んだりネジを絞めたりすることで 作ることができます。
唯一、タミヤのユニバーサルプレートセットとスプロケット&クローラーセットはプラモデルのように部品がくっついているので、ニッパーで部品を切り離していく地道な作業が必要です。
この作業に使うニッパーが結構重要で、今回タミヤのプラスチック部品の切り取り専用モデラーズニッパーを調達して使ったら、作業がとっても楽でニッパーで切るだけでバリも殆ど残らない優れものでした。
Raspberry Pi Zero WH
Raspberry Pi ZeroはちっちゃくてUSB OTGでPCと繋げてヘッドレスでセットアップができるのでとっても気に入っていますが、USBコネクタがmicro Bだけだったり、HDMIがminiだったりするのがマイナスポイントです。
でも、今回カメラを初めて使って一番の問題はカメラコネクタだということがわかりました。サイズの制約でRaspberry Pi 3や4より一回り小さいカメラコネクタが付いていますが、このカメラコネクタの黒い部品が非常に壊れやすいんです。
試行錯誤中に何度もカメラケーブルを抜き差ししていたら、パキっと黒い部品の留め部分が壊れてしまいました。ネットでも同様の事象があふれていて、カメラをなんとか使えるように修復できましたが少しみっともない状態になっています。
少し厚手の紙をカメラケーブルと同じ幅に切って、カメラケーブルと一緒にコネクタに差し込み、セロテープで両面から留めたら修復完了です。
SparkFun Servo pHAT for Raspberry Pi
ハードウェアでの今回の目玉がこのボードです。
なんと、このボード1枚で16台のサーボモーターがコントロールできるんです。また、このボードに付いているUSB-Cポート経由でRaspberry Pi本体に給電ができるので、電源回りもシンプルにすることができます。
ただ、サーボモーターは結構電力を消費するので、今回用意した出力2.4Aのモバイルバッテリーではサーボモーター2台ぐらいが適当だと思います。と、いうのも、Raspberry Pi Zero WHの代わりにRaspberyy Pi 3A+を付けてサーボモーター2台を動作させてみたら、ハングアップはしないまでも本体の赤い電源ランプが消えるんですよ。ちょっとヒヤッとしました。
もっと多いサーボモーターを制御したいときは、ボードの電源ジャンパープリントを切断して本体とボードに別々に電源を加えるようにします。
このボードについてはSparkfunのPi Servo pHat (v2) Hookup Guideに詳しく書いてあります。
Pan/Tilt 機構作成キット
サーボモーター2個付きで非常にリーズナブルなんですが、Pan/Tilt機構の精度はあまり高くなく、組み立てたものはカメラを取り付ける部分が斜めになってしまっています。
値段相応ということでしょうか。
キットの中に組み立てマニュアルは付いていないので(ぼくが千石電商秋葉原本店3階で買ったものには簡単な説明書が付いていましたが、サイトの方がわかりやすいです。)、Sparkfunの「Assemble the Pan-Tilt Mechanism」の記事を見ながら組み立てましょう。
360°連続回転サーボ FS90R
Servo pHATで扱えるサーボモーターには360°連続回転サーボ(ローテーションサーボ)も含まれていて、クローラーの駆動用に今回初めて使ってみました。
ユニバーサルプレートには、次の写真のように禁断のネジ留めをしてしまっていて、数回の試行錯誤の跡が残る痛々しい状態になっています。
また、ドライブスプロケットの軸の両端の突起を切って、内側にサーボモーターに付いてきた丸い部品を付けてネジ留めしています。
非常に雑なサーボモーターの付け方になってしまっていますが、これだと非常に手早く済ませることができます!お勧めはしません!
カメラモジュール V2と白色LED
カメラモジュール V2は800万画素で非常に画質が良いです。基盤の製造元によってか店により1,000円位の値段差があるようなので要注意ですね。
白色LEDは光度が6000mcdで半減角が60度のものを使いました。Servo pHatのPCA9685というICは16チャネルのPWMコントローラーで、サーボモーター以外のものも制御できるんです。
Pan/Tilt 機構作成キットには写真のような感じでくっつけました。
厚手の紙でカメラモジュールを貼り付ける枠を作って、厚手のすき間テープをPan/Tilt構成キットの間に挟んで両面テープで貼り付けています。
枠にはLED用の穴も空けました。
ソフトウェア
- Raspbian Buster with desktop
- mjpg-streamer (映像配信用)
- Python 3.7.3 (制御プログラム用)
- camtank.py (制御プログラム)
Raspbian
インストールイメージは「Raspbian Buster Lite」ではなく、普通に必要なモジュールが予め入っている「Raspbian Buster with desktop」を使っています。
Raspbianの設定変更
$sudo raspi-config
でRaspbianの設定を幾つか変更します。
- 起動状態をDesktopからCLIに変更
「3 Boot Options」を選び「B1 Desktop / CLI」から「B2 Console Autologin」を選択します。 - カメラモジュールの使用
「5 Interfacing Option」を選び「P1 Camera」をEnableにします。 - I2Cの使用
「5 Interfacing Option」を選び「P5 I2C」をEnableにします。
mjpg-streamer
実は映像配信用のプログラムには最初v4l2rtspserverを使おうとしたのですが、映像を見ながら操作するには遅延が大きすぎて使用を断念しました。映像の品質はとても良かったんですけどね。
mjpg-streamerの遅延は解像度やフレームレート、ネットワーク環境にも影響されますが非常に短いです。今回は解像度640x480、フレームレート15fpsで使用しています。この設定だとスマホのテザリングネットワークでも十分操作が可能でした。
インストールはGitHub上のmjpg-streamerのREADME.mdに書いてあるBuilding & Installationの指示に従いました。
こんな感じです。
$ sudo apt install cmake libjpeg8-dev $ git clone https://github.com/jacksonliam/mjpg-streamer.git $ cd mjpg-streamer/mjpg-streamer-experimental $ make $ sudo make install
次のシェルスクリプトで動かしています。
#!/bin/bash # mjpg-streamer export LD_LIBRARY_PATH=/usr/local/lib/mjpg-streamer/ mjpg_streamer \ -i 'input_raspicam.so -x 640 -y 480 -fps 15 -vs' \ -o 'output_http.so'
これで、ブラウザからhttp://<IPアドレス>:8080/?action=stream
にアクセスすると映像が見れます。
camtank.py(制御プログラム)
使用ライブラリ
PCA9685(SparkFun Servo pHAT for Raspberry Piが使用している制御用IC)用のAdafruit社製のライブラリを使用するため、その前提となるRaspberry PiでCircuitPythonの動作環境を提供するライブラリをインストールします。
$ sudo pip3 install --upgrade setuptools $ pip3 install adafruit-blinka
PCA9685がLED制御用で、ServoKitがサーボモーターの制御用です。
LEDの操作はこんな感じです。
pca.channels[15].duty_cycle = 0xffff
これで15番チャネルに付けたLEDを最大輝度にします。
逆に15番チャネルのLEDを消したいときは、このように書きます。
pca.channels[14].duty_cycle = 0x0000
次にサーボモーターの場合。
7番チャネルのサーボモーターを0度の位置に動かしたい場合はこのように書きます。
kit.servo[7].angle = 0
7番チャネルのサーボモーターを90度にしたい場合はこう
kit.servo[7].angle = 90
7番チャネルのサーボモーターを180度にしたい場合はこう
kit.servo[7].angle = 180
角度をそのまま指定するだけなので非常に簡単です。
最後に360°連続回転サーボ(ローテーションサーボ)の場合。
kit.continuous_servo[0].throttle = 1.0
これで0番チャネルの連続回転サーボが全力で正転します。
逆に0番チャネルの連続回転サーボを全力で逆転させる場合はこう書きます。
kit.continuous_servo[0].throttle = -1.0
0番チャネルの連続回転サーボを停止させる場合はこうです。
kit.continuous_servo[0].throttle = 0.0
throttleには-1.0~1.0までの任意の値を指定できます。簡単ですね。
また、モータードライバでDCモーターをPWM制御する場合と違って、低い値でも確実に動いてくれるため操作が非常に楽です。
ライブラリのインストールは次の2行を実行します。
$ sudo pip3 install adafruit-circuitpython-pca9685 $ sudo pip3 install adafruit-circuitpython-servokit
Blynk
ぼくのブログではすっかりお馴染みになっているスマホ対応IoTプラットフォームのBlynkを今回も使っていて、ラズパイタンクの操作をスマホのBlynkアプリで行っています。
写真のイケメンの方はウクライナ出身のBlynkの共同創業者の方だそうです。
ライブラリのインストールは次のように行いました。
$ git clone https://github.com/vshymanskyy/blynk-library-python.git $ cd blynk-library-python/ $ sudo pip3 install .
プログラム
かなりコメント少なめな塩対応となっていますが、今後コメントを加えるかもしれません。
※2020/8/30 52行目と53行目の不等号の向きが逆になっていたのを修正。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # camtank.py # March 25, 2020 # By Hiroyuki ITO # https://intellectualcuriosity.hatenablog.com/ # This program is licensed in the public domain. BLYNK_AUTH = 'YourAuthToken' from board import SCL, SDA import busio from adafruit_pca9685 import PCA9685 i2c_bus = busio.I2C(SCL, SDA) pca = PCA9685(i2c_bus) pca.frequency = 60 from adafruit_servokit import ServoKit kit = ServoKit(channels=8) joystick_mode = 0 # 0:Motor 1:PanTilt angleX = 0 angleY = 0 import BlynkLib from BlynkTimer import BlynkTimer import os import math blynk = BlynkLib.Blynk(BLYNK_AUTH) timer = BlynkTimer() def limit(value, lower, upper): if value < lower: value = lower elif value > upper: value = upper return value @blynk.on("connected") def blynk_connected(): blynk.sync_virtual(2, 4) blynk.virtual_write(3, 1) @blynk.on("V1") # Joystick def v1_write_handler(value): global joystick_mode, angleX, angleY joystickX = int(value[0]) joystickY = int(value[1]) # PanTilt if joystick_mode: if joystickX > 30: angleX = 4 elif joystickX < -30: angleX = -4 else: angleX = 0 if joystickY > 30: angleY = 4 elif joystickY < -30: angleY = -4 else: angleY = 0 # Motor else: joystickY = joystickY * 0.7 if joystickY >= 0: if joystickX >= 0: throttleL = joystickY + joystickX * 0.3 throttleR = -joystickY + joystickX else: throttleL = joystickY + joystickX throttleR = -joystickY + joystickX * 0.3 elif joystickX >= 0: throttleL = joystickY - joystickX * 0.3 throttleR = -joystickY - joystickX else: throttleL = joystickY - joystickX throttleR = -joystickY - joystickX * 0.3 kit.continuous_servo[0].throttle = throttleL / 100 kit.continuous_servo[1].throttle = throttleR / 100 @blynk.on("V2") # Motor and Pan/Tilt switch button def v2_write_handler(value): global joystick_mode joystick_mode = int(value[0]) if joystick_mode == 0: # Motor kit.servo[6].angle = 90 kit.servo[7].angle = 110 @blynk.on("V3") # Power button def v3_write_handler(value): if int(value[0]) == 0: # Off os.system('sudo shutdown -h now') @blynk.on("V4") # LED ON/OFF button def v4_write_handler(value): if int(value[0]): # ON pca.channels[14].duty_cycle = 0xffff pca.channels[15].duty_cycle = 0xffff else: # OFF pca.channels[14].duty_cycle = 0x0000 pca.channels[15].duty_cycle = 0x0000 def panTiltMove(): global angleX, angleY kit.servo[6].angle = limit(kit.servo[6].angle - angleX, 0, 180) kit.servo[7].angle = limit(kit.servo[7].angle - angleY, 0, 180) # Define Timers timer.set_interval(0.1, panTiltMove) # 100ms while True: blynk.run() timer.run()
ほんの少しだけ説明すると、19行目がServoKit(channels=16)
ではなくServoKit(channels=8)
となっているのは、ぼくが無理やりLEDとサーボモーターを一緒に使おうとしたからです。チャネル0~7までをサーボ用に、チャネル8~15をLED用に割り振るための設定です。公式に認められている使い方じゃなさそうなのですが、一応動いています。
スマホ画面
スマホの画面はAndroidの2画面表示機能を使って、上にブラウザで映像を表示し、下に操作用のBlynkアプリを表示しています。
Blynkにも映像表示用のウィジットが用意されていますが、映像をバッファリングして遅延が発生するため使用を断念しました。
ブラウザだとほぼ遅延なしに映像を表示することができます。今回はFirefoxを使っていますがChromeでも大丈夫だと思います。
Blynkアプリの設定
Blynkアプリの設定画面はこんな感じです。
ラズパイをスマホからシャットダウンできるようにPOWERボタンを付けていますが、操作中に誤って押さないように2画面表示で表示されるエリアの外に配置しています。
各ウィジットの設定はこうなっています。
Joystick(駆動、パン・チルト用)
Styled Button(Joystickの駆動/パン・チルト切替用)
Button(シャットダウン用)
Button(LEDオン/オフ用)
おわり