ことの経緯
以前、raspberry piにて猫カメラ(にゃんCAM)を構築しました。
普段はTapoを使っていたので全然出番が無かったのですが、久々に起動しようとしたら起動を繰り返す変な動作をしていました(デスクトップ表示→左上にアンダーバー点滅→再起動の繰り返し)
直そうと適当にやっていたらSDカードを割ってしまいました(物理的に)。
というわけで、再構築しようとしたのですが環境が変わってつまずいたので新たに記事にしました。
教訓
SDカードは色んな意味ですぐ死ぬ。クローンを作っておくこと
ハード
- raspberry pi 3B(3B+ではない)
- 32GBのSDカード
- 5.1V出力のACアダプタ
- これはlow voltage errorの対策です
機能、ファイルの置き場など
OpenCVでUSBカメラ(webカメラ等)2台を制御し、30分ごとにLINEで撮影画像を送信します
2台用でプログラムを書いてあるため、2台以外(1台or3台〜)ではプログラムがエラーになり、動作しません。(具体的にはプログラムエラー→再起動のループ))
1台分にコメントアウトすれば1台でも動作します
saikidou.serviceとsaikidou.timerによってラズパイを定期再起動させますが、ラズパイの安定性向上やUSBが死んでカメラが認識しなくなることが偶にあり、それを復帰させるために実施します
USBが死ぬ(例えば、カメラが1台しか繋がってないような状態になります)と上記した通りnyancam.pyがエラーになり、nyancam.serviceによってプログラムの再起動を繰り返しますが、少なくとも定期再起動のタイミング(6時間ごと)で復帰するため長期にカメラが機能しなくなることはなくなります
死亡したときはプログラムの再起動が繰り返されるため、USBカメラのLEDが点滅します(LEDは撮影時に点灯)
ラズパイのそばで対応できるなら再起動を待たずともUSBの抜き差しで再認識します(USBが復帰し、nyancam.serviceによる再起動でカメラが2台認識します)
GPIO26に接続したスイッチを単押しするとテスト撮影、5秒以上長押しするとラズパイをシャットダウンします
Filename | Path | Note |
---|---|---|
nyancam.py | /home/USERNAME/Desktop/ | プログラム本体 |
nyancam.service | /lib/systemd/system/ | nyancam.pyを自動起動する エラー時にnyancam.pyを再起動する |
saikidou.service | /lib/systemd/system/ | ラズパイを定期再起動する |
saikidou.timer | lib/systemd/system/ | saikidou.serviceとセット |
githubにあげてあります
プログラムの実行結果
nyancam.pyを実行した結果例を載せます
カメラ検索でワーニングが出ますが、動作に問題ないためそのままにしています。
camera number 0 Find!
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video1): can't open camera by index
camera number 1 None
camera number 2 Find!
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video3): can't open camera by index
camera number 3 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video4): can't open camera by index
camera number 4 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video5): can't open camera by index
camera number 5 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video6): can't open camera by index
camera number 6 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video7): can't open camera by index
camera number 7 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video8): can't open camera by index
camera number 8 None
[ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video9): can't open camera by index
camera number 9 None
接続されているカメラは 2 台です
カメラ0 : 0 番
カメラ1 : 2 番
CamNum0 : 0
CamNum1 : 2
### にゃんCAM 起動 ###
rev:7
30.0分ごとに撮影しまーす!
接続カメラ : 2 台
2023-09-09 11:38:42.525746
カメラ0
うp残 : 46 / 50
カメラ1
3BはUSBブートがめんどいらしい
SDカードを割ってしまったので、もっと(物理的に)丈夫なUSBメモリでbootさせることを考えました
しかし、3B+はデフォルトでUSBブートができるのですが、3Bはできないようです
正確には色々変更すればできるようですが面倒なので見送りました
SDカードに書き込みから
まずは環境構築していきます
Raspberry Pi Imager v1.7.5を使い、Raspberry Pi OS(32-bit)を書き込みます
SSHとVNCで操作するので、“SSHを有効にする"をチェックしておきます
他の設定はお好みで
SSHで繋いで、VNCを有効化する
VNCはデフォルトでは無効化されているので、セットアップ時のraspi-configで有効化させます
まず、SSH接続します
ssh username@ip-address
VNCはrealvncが元々入っていますのでバージョンを確認してみます
$ dpkg -l real*
realvnc-vnc-server 7.0.1.49073
raspi-configからVNCを有効化します
sudo raspi-config
3 → VNC を有効化でオッケ
SSHで繋いでいても、VNCは繋がるようです
最初にやるアプデとか
色々乱立していますが、現在まとめたのは以下です
上から順に実行します
どのラズパイでも共通です
パッケージリストの更新
sudo apt update
アップグレード
sudo apt full-upgrade
ここまではguiでやるアプデなどと内容は一緒らしいです
続いて、要らないファイルの削除
sudo apt autoremove
これは sudo apt full-upgrade
を実行した後に表示されます
キャッシュの削除
sudo apt clean
再起動
sudo reboot
ここで注意したいのは sudo rpi-update
はやらないことです
OpenCVのインストール
これは調べるとたくさん記事が出てきますが、みんな苦労しているようです
numpyのバージョンが古いとエラーが出るようで、私も同じパターンでした
今回OSに同封されていたpython3はpython 3.9.2でした(前回は3.7.3)
$ python3 -VV
Python 3.9.2 (default, Mar 12 2021, 04:06:34)
[GCC 10.2.1 20210110]
Raspberry Pi3 ModeB+ Python3 OpenCV構築 - Qiita
上記を参考にして、まずライブラリのインストールをします
sudo apt-get install libhdf5-dev libhdf5-serial-dev libatlas-base-dev -y
続いて、OpenCVをバージョン指定でインストールします
のちほどroot権限でsystemdを実行するので、sudo
でインストールします
python3にインストールするため、pip3
と明示します
バージョンは以前の記事で紹介したものと同じです
sudo pip3 install opencv-python==4.5.1.48
バージョン指定しないでインストールは最初にやってみたのですが、インストールにとても時間がかかり止まっているようだったので中断しました
バージョン指定したらすぐ終わったのでやはり止まっていたようです
numpyがエラー
OpenCVはnumpyが必要です
numpyはデフォルトでインストールされていますが、OpenCVの要求に対してバージョンが古いためimport cv2
がエラーになり動きません
そこで、numpyをアップグレードします
こちらもrootでsystemdを実行するため、sudo
でアップグレードします
sudo pip3 install numpy --upgrade
更新前は 1.19.5 でしたが、更新後は 1.25.0 となりました
$ python3
Python 3.9.2 (default, Mar 12 2021, 04:06:34)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> numpy.__version__
'1.25.0'
>>> numpy.__file__
'/usr/local/lib/python3.9/dist-packages/numpy/__init__.py'
numpyを更新したら、OpenCVもインポートできるようになります
>>> import cv2
>>> cv2.__version__
'4.5.1'
>>> cv2.__file__
'/usr/local/lib/python3.9/dist-packages/cv2/cv2.cpython-39-arm-linux-gnueabihf.so'
systemdを使ったプログラムの自動起動、再起動、ラズパイの定期再起動
.serviceファイルを今回は /lib
以下に作成しました/lib
以下はシステム関連が多いため、汚したくないという理由で /etc
以下に作るのがお作法らしいです
それは後から知ったので、今回はそのまま /lib
以下にしています
まず、nyancam.pyを自動起動、再起動するサービスをsudo
で作成します
sudo nano /lib/systemd/system/nyancam.service
続いて、ラズパイを定期再起動するサービスを作成します
sudo nano /lib/systemd/system/saikidou.service
sudo nano /lib/systemd/system/saikidou.timer
次に、サービスをスタート、有効化しますenable
で有効化することで、次回起動時に自動でサービスが開始されます
sudo systemctl start nyancam.service
sudo systemctl enable nyancam.service
sudo systemctl enable saikidou.timer
saikidou.service
はenable
する必要はありません
正常にサービスが起動しているか、ステータスを確認します
例えば定期再起動のサービスの確認は以下です
sudo systemctl status saikidou.timer -l
より詳細なログは以下で確認できます
journalctl -u nyancam.service -r | less
Wi-Fiのスリープ回避
ラズパイのパワーセーブ機能でWi-Fiのスリープ機能があります。
30分ごとに動作する間にスリープしてしまうとエラーになりますので機能を停止します。
ただ、実際にどのくらいでスリープするかは確認していないので一応やっただけです。
以下でパワー・マネージメントモードの状態を確認します
デフォルトではオンです。
$ iw dev wlan0 get power_save
Power save: on
パワー・マネージメントの無効化をします
sudo nano /etc/dhcpcd.exit-hook
dhcpcd.exit-hook
の中身はGitHubにあげています。
ここまでで一応終わりです
以下は詰まった点などを記載します
ターミナルでは動くのに、systemdでは動かない
nyancam.pyはターミナルやThonnyでは動くのに、systemdでは動きませんでした
原因
numpyをアップデートする際にsudo
をつけなかった
pip3 install numpy --upgrade
対策
sudo
をつける
sudo pip3 install numpy --upgrade
sudoありなしでは、pythonパッケージのインストール先が異なる
sudoありの場合は/usr/local/lib/python3.9/dist-packages
にインストールされますが、sudoなしの場合は/home/USERNAME/.local/lib/python3.9/site-packages
にインストールされます
systemdはsudo
で実行しますが、デフォルトでは/home/USERNAME/.local/lib/python3.9/site-packages
を見に行きません
OpenCVはsudo
でインストールしたため/usr/local/lib/python3.9/dist-packages
にインストールされたので問題無かったのですが、numpyはsudo
なしでアップグレードしたため、/home/USERNAME/.local/lib/python3.9/site-packages
にインストールされていました(ver 1.25.0)
numpyはプリインストールされており、そちらは/usr/local/lib/python3.9/dist-packages
にあります(ver 1.19.5)
よって、バージョン違いのnumpyが2つあることになります
これらより、systemdで実行した場合はOpenCVが古いnumpy(ver 1.19.5)を見に行ったためエラーとなりました
numpyの更新をsudo
で実施すれば、プリインストールされたnumpyが更新されます
ターミナルやThonnyで実行する場合は/home/USERNAME/.local/lib/python3.9/site-packages
も見に行くためエラーにならず、気づきづらいです
以下はターミナルで実行した場合の、インポートするpythonパッケージのロケーション一覧です。/home/USERNAME/.local/lib/python3.9/site-packages
が含まれていることがわかります
systemdをsudo
で実行すると含まれません
$ python3
>>> import sys
>>> sys.path
['',
'/usr/lib/python39.zip',
'/usr/lib/python3.9',
'/usr/lib/python3.9/lib-dynload',
'/home/USERNAME/.local/lib/python3.9/site-packages',
'/usr/local/lib/python3.9/dist-packages',
'/usr/lib/python3/dist-packages',
'/usr/lib/python3.9/dist-packages']
以前のにゃんCAMではなぜ問題なかったのか
以前はOpenCVはsudo
でインストールし、numpyはプリインストールされたものを使用しています
numpyはアップグレードせずともOpenCVは動作していたため、今回のようなインストール場所の問題が起きず使用できていたと考えられます
対策の別案
新しいnumpyがインストールされている場所を示す方法があります
これはsys.path.insert(0, "/home/USERNAME/.local/lib/python3.9/site-packages")
で、パッケージリストの先頭に/home/USERNAME/.local/lib/python3.9/site-packages
を追加しています
これによりsystemdをsudo
で実行しても/home/USERNAME/.local/lib/python3.9/site-packages
内を探してくれるようになります
また、pythonはパッケージリストの上から検索していき、同じパッケージが複数ある場合は先に見つかったものから適用するらしいので、先頭に追加して古いnumpyが適用されることを防いでいます
import sys
import pprint
import os
print(sys.executable)
print("before path add")
pprint.pprint(sys.path) # before path add
print(os.getenv('PYTHONPATH'))
sys.path.insert(0, "/home/USERNAME/.local/lib/python3.9/site-packages")
print("after path added")
pprint.pprint(sys.path) # after path added
結果↓
/usr/bin/python
before path add
['/home/USERNAME/Desktop',
'/usr/lib/python39.zip',
'/usr/lib/python3.9',
'/usr/lib/python3.9/lib-dynload',
'/home/USERNAME/.local/lib/python3.9/site-packages',
'/usr/local/lib/python3.9/dist-packages',
'/usr/lib/python3/dist-packages',
'/usr/lib/python3.9/dist-packages']
None
after path added
['/home/USERNAME/.local/lib/python3.9/site-packages',
'/home/USERNAME/Desktop',
'/usr/lib/python39.zip',
'/usr/lib/python3.9',
'/usr/lib/python3.9/lib-dynload',
'/home/USERNAME/.local/lib/python3.9/site-packages',
'/usr/local/lib/python3.9/dist-packages',
'/usr/lib/python3/dist-packages',
'/usr/lib/python3.9/dist-packages']
after path added以降は/home/USERNAME/.local/lib/python3.9/site-packages
が2箇所記載されていますが、これはターミナルで実行したからであり、systemdで実行すれば先頭に1箇所だけになります。
この方法は一応は動きましたが、別の動作でエラーが発生したため、より簡単なsudo pip3 install numpy --upgrade
で対応しました
本来はsudo pip3 install
はシステムを汚すためタブーらしいのですが、OpenCVもsudo
で入れてしまったし、numpyもプリインストールされてるものなのでまぁ良いかなと思っています
他にも、OpenCV、numpyをsudo
なしでインストールし、systemdをユーザで実行する方法もありますが、そちらは未着手です
以上