こんにちは。Kです。
Raspberry Pi 4Bでのハードウェア編に続きまして、ソフトウェア編ということで、1回では内容が纏められず、解説は2回に渡ります。
顔認識デバイスでは主に、3つの主機能に分かれます。
- 首振り機能(Pan-Tilt Hatライブラリ)
- 通信機能(Bluetooth通信)
- カメラ+画像解析機能
今回は周辺機器に関する首振り機能と通信機能について解説します。
セットアップ方法について
セットアップについては簡単に紹介していきます。
OSはRaspberry Pi OSを使用します。
各パッケージのインストール方法は下記よりご参照ください。
Arducam IMX298
URL:https://github.com/ArduCAM/MIPI_Camera
PanTiltHat
URL:https://github.com/pimoroni/pantilt-hat
首振り機能(Pan-Tilt Hat)
ソースコード解説に入る前に首振りの大まかな仕様を説明します。
このパーツでは横方向(Pan)と縦方向(Tilt)に対して二つのサーボモータを使って制御しています。
横方向に対して左右180°と少しだけ遊びがある程度まで動かす事ができます。
カメラの中心位置を0°とした場合、横振りの場合 −90°は左向き、90°は右向きが最大稼働範囲となります。
上記を踏まえてカメラの中心位置を手動でカメラの可動域まで動かしながら確認します。
パーツに使用しているサーボモーターは小型で非常に壊れやすいため、慎重に動かして調整します。

中心位置をずらすにはサーボ下部のマイナスネジ4箇所の固定ネジを外すと底部が外れます。フラットケーブルを外してから底部ネジの中心にあるネジを外せばサーボが外れます。この位置をずらして嵌め込む事で中心位置を変更出来ます。

縦方向に対して下方向がフラットケーブルが干渉する関係上、上下おおよそ150°程度まで動かす事ができます。
同じ要領で縦振りに対しても調整を行ってください。
性能上サーボモーターは稼働範囲外まで設定する事が出来ますが、故障の原因になりますので、稼働範囲外へ設定することは避けてください。
下記にソースコードを抜粋しました。
import pantilthat
:
# 首振りの角度
panvalue = 0
tiltvalue = -50
:
pantiltalive = True
:
def pantilt():
sleep_time = 0.05
change = 5
while pantiltalive:
global panvalue
global tiltvalue
time.sleep(sleep_time)
# from spp
if spp.mode != sppconnection.Mode.AUTO:
panvalue = (180 - spp.vx) - 90
tiltvalue = (180 - spp.vy) - 90
if panvalue > 90:
panvalue = 90
elif panvalue < -90:
panvalue = -90
if tiltvalue > 60:
tiltvalue = 60
elif tiltvalue < -90:
tiltvalue = -90
diffPan = panvalue - pantilthat.get_pan()
if diffPan > 0:
if diffPan > change:
pantilthat.pan(pantilthat.get_pan() + change)
else:
pantilthat.pan(panvalue)
if diffPan < 0:
if diffPan < - change:
pantilthat.pan(pantilthat.get_pan() - change)
else:
pantilthat.pan(panvalue)
diffTilt = tiltvalue - pantilthat.get_tilt()
if diffTilt > 0:
if diffTilt > change:
pantilthat.tilt(pantilthat.get_tilt() + change)
else:
pantilthat.tilt(tiltvalue)
if diffTilt < 0:
if diffTilt < - change:
pantilthat.tilt(pantilthat.get_tilt() - change)
else:
pantilthat.tilt(tiltvalue)
pantilthatのライブラリを使うことで、角度の取得と設定を簡単に行えます。
変更直後、設定した角度へ動きます。この時1ループ0.05秒の周期ですので、0°から90°に設定した場合、急激に首が動くため、逆方向へ反動するような動作があったり、最悪の場合サーボ内部のギアが壊れやすくなります。
ソースコードでは5°以上の移動量がある場合は細かく動作するように制御してあります。
Bluetooth通信
続いてBluetooth通信です。
既に前章でお話しした通りPythonではBluetooth通信が非常に簡単に実装できます。
まずはBluetooth通信のSPPプロファイルで実装したクラスを抜粋します。
class SppConnection(threading.Thread):
def __init__(self):
super(SppConnection, self).__init__()
self.alive = True
self.mode = Mode.AUTO
self.facedetect = True
self.lookface = True
self.eyedetect = True
self.agegenderdetect = True
self.vx = 0
self.vy = 0
def run(self):
last = time.time()
# mode = Mode.AUTO
# vx = 0
# vy = 0
recv = bytes(0)
datasize = 13
while self.alive:
self.mode = Mode.AUTO
self.facedetect = True
self.lookface = True
self.eyedetect = True
self.agegenderdetect = True
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.bind(("", bluetooth.PORT_ANY))
sock.listen(1)
sock.settimeout(3)
# Set Discoverable
subprocess.call(['sudo', 'hciconfig', 'hci0', 'piscan'])
# uuid = 'ef7ce24a-a1eb-45d4-9208-f896b0ae8336'
# uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee"
uuid = '00001101-0000-1000-8000-00805F9B34FB'
bluetooth.advertise_service(
sock, "MyServer", service_id=uuid,
service_classes=[uuid, bluetooth.SERIAL_PORT_CLASS],
profiles=[bluetooth.SERIAL_PORT_PROFILE],
)
print('accept start')
# client_socket, address = sock.accept()
try:
client_socket, address = sock.accept()
except:
print('Bluetooth accept exception')
continue
if client_socket is None:
continue
print('connected')
while self.alive:
try:
result = client_socket.recv(13)
recv = recv + result
if len(recv) >= datasize:
data = recv[0:datasize]
# print(recv.hex())
mode_old = self.mode
self.mode = Mode(int.from_bytes(data[0:1], 'big'))
facedetect_old = self.facedetect
self.facedetect = int.from_bytes(data[1:2], 'big')
lookface_old = self.lookface
self.lookface = int.from_bytes(data[2:3], 'big')
eyedetect_old = self.eyedetect
self.eyedetect = int.from_bytes(data[3:4], 'big')
agegenderdetect_old = self.agegenderdetect
self.agegenderdetect = int.from_bytes(data[4:5], 'big')
vx_old = self.vx
self.vx = int.from_bytes(data[5:9], 'big')
vy_old = self.vy
self.vy = int.from_bytes(data[9:13], 'big')
if self.mode != mode_old or self.facedetect != facedetect_old or self.lookface != lookface_old or self.eyedetect != eyedetect_old or self.agegenderdetect != agegenderdetect_old or self.vx != vx_old or self.vy != vy_old:
print('mode={2}, face detect={5}, look face={6}, eye detect={3}, age/gender detect={4}, x={0}, y={1}'.format(self.vx, self.vy, self.mode, self.eyedetect, self.agegenderdetect, self.facedetect, self.lookface))
if len(recv) > datasize:
recv = recv[datasize:]
else:
recv = bytes(0)
except Exception:
break
通信内容はメンバーのパラメータ値を取得することで、現在の値などを調査する事が出来ます。
import sppconnection
:
spp = sppconnection.SppConnection(
:
def main():
# 通信スレッド開始
spp.start()
あとはインスタンスを生成して軌道開始時にstartのメソッドを呼び出したら以降は通信が可能です。
次回はいよいよカメラ周りの機能の解説です。
ではまた!
関連記事
-
第1回 ラズパイを使用したBLE通信 ~ ディスプレイ、キーボード、マウスを接続しないで設定 前編 ~
こんにちは、GTです。よろしくお願いします。 最近業務でラズパイのBluetooth機能を使...
公開日:2021.12.24 更新日:2021.12.24
tag : Bluetooth Raspberry Pi
-
-
第1回 Visual C++で作成したDLL内のクラスをC#で利用する方法
こんにちは、ILCです。 Visual C++ (以下 VC++)で作成されたDynamic...
公開日:2024.01.19 更新日:2024.01.19
tag : Windows
-
-
こんにちは、TMIHです。 今回から組み込み系のソフトウェア設計標準規格である、MISRA-...
公開日:2022.02.25 更新日:2022.02.25
-
第3回 ラズパイを使用したBLE通信 ~ A/D変換・D/A変換を用いた入出力編 ~
こんにちは、GTです。よろしくお願いします。 第3回の今回は、ラズパイの入出力についてご紹介...
公開日:2023.02.24 更新日:2023.02.24
tag : Bluetooth BLE Raspberry Pi