こんにちわ!B2のすめしです。
本記事は、FUN Advent Calendar 2022 part2の12/7の記事です。
アドカレ初めて&&プログラムよわよわなのでお手柔らかにお願いします。
はじめに
物騒なタイトルをしていますが、動物虐待ではありません。「かわいい弊ハムスターをいつでも見たい!」ということで開発したペットカメラの記事になります。技術的な話は後半にあるので、「前座はいらねぇ!早く見せろという方はここまで飛ばしてください。」
ハムスターについて
11月3日にブルーサファイアハムスターのぷち(♂)をお迎えしました。ハムスター買いたいねなどと話しており、ペットショップに向かうといろんな種類のハムスターがいました。種類はブルーサファイアハムスターに決定し、2匹いたうちのどちらを選ぶか決める段階まで行ったのですが、どちらも顔が見えなかったため顔を見せてもらいました。すると!1匹目の子が!とても!!かわいかったため!その子をお迎えすることにしました。
構想
ハムスターをお迎えしてすぐ「ペットカメラが欲しい!」と思ったところで、「せっかくなら作ろう!」とエンジニア(まだまだひよっこですが...)の性が出てしまったので、作ることに決定。ちょうど余っていたRaspberryPiZeroWHを使って作ることにしました。
RaspberryPiにUSBカメラを付けて、取得した画像をDiscord.pyで送る簡単なプログラムを書く予定でした。が、しかし、様々な壁にぶち当たり最終的にはこうなりました。
RaspberryPiにコンテナを立ててその中で上記のプログラムを書くことにしました。
様々な壁たち
- Rasbianに必要なライブラリをインストールできない
OpenCV、Discord.pyを実行するに当たり、依存パッケージをインストールする必要があるのですが、Ubuntuのパッケージリポジトリ?にあるパッケージをインストールせねばならないのになぜかソースリストにそのリポジトリを追加できない現象に悩まされRasbian上で動かすことを断念しました。 - armのアーキテクチャ問題
この現象にこの開発中に2回見舞われる事になりました。
1回目は、Rasbian上で動かすことを諦めたとき「Ubuntuでやればいいじゃん!」と思いつきUbuntuをインストールしました。しかし、Ubuntuを起動しようとしても4色のウォーターマーク画面でフリーズし、その先に進むことはありませんでした。解決方法として、GPUチェックを飛ばす、電圧が低下してないか確認するの2点があり、確認しましたが解決しなかったのでRasbianに戻しました。
2回目は、Docker内のイメージでUbuntuを採用し、x86環境で実行した際には現れなかったエラーです。そこには、「armv7のイメージがarmv6のマシンで動くわけねぇだろ!(意訳)」と書いてありました。調べた結果、Ubuntuはarmv7以降のイメージしか提供していなかったため、RaspberryPiZeroでは動かないことがわかりました。結局、Debianのarmv5イメージを採用することで事なきを得ました。
この勘違いを生んだ要因が二つあり、- 高校時代に何もわからないながらRaspberryPi「3B+」にubuntuをインストールしたことがあったこと。(3B+はarmv7)
- RaspberryPiの公式イメージにUbuntuがあったこと。(Zeroについて記述はなく、記述のあったZero2(armv8)はzero(armv6)と異なるアーキテクチャだったため勘違いが加速した。)
- あとは些細なDockerとLinuxの沼にハマりまくっていた。
実際のコード
Docker
FROM debianのあとにバージョンなどを記載しないことで最適なアーキテクチャを指定してくれる。(今回の場合はarmv5)
コメントアウトしているところは結局コンテナに入ってから実行しないと正常に動作しなかったため、メモの意味で残している。
FROM debian
USER root
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN apt-get update -y && apt-get upgrade -y && \
apt-get install -y nano \
python3 \
python3-dev \
python3-pip \
python3-setuptools \
libopencv-dev \
libgl1-mesa-dev \
libglib2.0-0 \
libsm6 \
libxrender1 \
libxext6 \
libffi-dev \
libnacl-dev && \
# python3 -m pip install --upgrade pip \
# setuptools && \
# python3 -m pip install numpy \
# opencv-python \
# discord.py && \
mkdir /home/workspace
カメラをコンテナに認識させる、working_dirを移動させる、ディレクトリを同期させる、といったコマンドをdocker runで書くのは面倒なのでdocker-composeで書きました。便利です。
version: "3"
services:
puti:
restart: always
build: .
container_name: "puti_camera"
working_dir: "/home/workspace"
tty: true
volumes:
- ./workspace:/home/workspace
devices:
- /dev/video0:/dev/video0:mwr
Python
OpenCVで画像を取得し、jpgとして出力するほぼ最小構成のコードです。
画像取得部分と、出力部分を関数化することによって、モジュールとして呼び出せるようにしました。
import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,720)
def get_img():
ret,frame = cap.read()
print(ret)
cv2.imwrite("image.jpg",frame)
cap.release
取得した画像をDiscordに送るbotを制御するコード。
これもほぼ最小構成というか、ほぼチュートリアルのコードと変わっていません。
import discord
import discord_token
import puti_camera as pc
intents = discord.Intents.all()
intents.messages = True
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f'We have logged in as {client.user}')
@client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('?puti'):
await message.channel.send('Please wait a minite...')
pc.get_img()
await message.channel.send(file=discord.File('./image.jpg'))
client.run(discord_token.get_token())
結果
学んだこと
- armのアーキテクチャについて && x86以外でやることの難しさ
- Discord.py、OpenCVの簡単な使い方
- Docker周りの仕様をより深く知れた
(ZeroじゃなければDockerの特徴である、環境の再現性を実感するいい機会になったんだけどな...)
まとめ
とにかく実行部分のコードよりもインフラの面で苦労したところが多かったが、学んだことがとても多かったので作ってよかったなと思っています。あと、「もう一台カメラを設置する+暗いと何も映らないので写真取るときにうっすら電気をつける+温湿度計をつける」をやりたいなと思っています。
最後に、やる気を復活させてくれてありがとう、ぷち。
最後に
明日(12/8)はAtriaさんとNickさんですね!楽しみにしてます!