手検出やジェスチャー認識で PC を操作することは多くの方が想像するところではないでしょうか?この場合、HID (Human Interface Device)として BLE Peripheral の実装を行いたくなります。しかしながら、私自身のように普段は別の分野の業務に従事する方や、こうした話が持ち込まれる機械学習エンジニアにとって突然 HID Over GATT を実装を行うのは骨が折れることだと思います。(マイコン向けの micro python などで記述された実装は簡単にヒットするが、デスクトップ環境の実装の記事が少なく、dbus-api として提供されるようになった bluez の背景を追うことや SerialPortProfile (SPP) の存在が私を混乱させた。)
私自身は当時 tech demo のために設けられた一週間のうちに実現できず、その後1年ほど少しずつ情報を集めて BLE マウスのエミュレートの実現に至っています。この話は別案件においても応用が効くように思うので、私の実装の公開を行い、この記事では実装を修正するための要所を記載しようと思います。(去年1年は余裕がなくまとめられなかった)
利用されるソフトウェア
おそらく当時下記の内容で動作させていたと思います。
手検出(ローカライゼーション)には mediapipe の hand pose estimator を利用しています。手姿勢の推定の前段に detection (localization) を行う箇所があるので、この部分を利用しています。
- ubuntu (20.04)
- bluez 5.4
- python 3.8 系
- mediapipe 確認中
- opencv 確認中
実装修正の勘所
ここでは今後アプリケーションに応じた実装を記述する方法を記載します。
実際は GATT における Service
, Characteristics
の記述と Application
部分の簡単な編集で済む話になっています。
Application 部分
リポジトリには3つのアプリケーションの実装が入っています。(ようだ)
まずは example_gatt_server のデモを起動できると理解が進むと思います。
実際に GATT アプリケーションを利用(デバッグ)する手段としてお勧めなのは
GattBrowser(android/ios)になります。ただし、HID サービス (hand_gesture_mouse_server, multitap_gatt_server) は macos/ios のセキュリティの理由で表示されず利用できません。
-
example_gatt_server.py
- GATT のサンプル実装デモ
- 4つのサービスが動作する (
BatteryService
,DeviceInfoService
,HeartRateService
,TestService
)
-
hand_gesture_mouse_server.py
- カメラから手の位置を検出してマウスポインタが追従するデモ
- windows 推奨(macos, ios では動かない)
-
multitap_gatt_server.py
- digitizer (タッチパネル)のデモ
- windows 推奨で5秒に一回タップ操作が走る
上記のファイル内で編集が必要な点は1つで以下の __init__
部分になります。
example_gatt_server
では HeartRateService
, BatteryService
, DeviceInfoSerivce
, TestService
の4つのサービスが登録されていることが分かりますが、このうち HeartRateService
, TestService
にあたる部分を自作のアプリケーション用に用意する流れになります。(BatteryService
, DeviceInfoSerivice
もダミー実装になっているが、動作する点だけ注意が必要になっています)
class Application(dbus.service.Object):
"""
org.bluez.GattApplication1 interface implementation
"""
def __init__(self, bus):
self.path = '/'
self.services = []
dbus.service.Object.__init__(self, bus, self.path)
self.add_service(HeartRateService(bus, 0))
self.add_service(BatteryService(bus, 1))
self.add_service(DeviceInfoService(bus, 2))
self.add_service(TestService(bus, 3))
L94 付近でアドバタイズを行う Agent を登録しているので、GATT Browser や windows のデバイスの登録での検索にデバイスが AIBot
として表示されて接続ができる状態になります。
Service
/Characteristics
部分
Service
/Characteristics
を記述するためには既存の hand_gesture_mouse_service.py のファイルをコピーして編集していくと早いように思います。
注目が必要な箇所はいくつかあるのですが、Report Descriptionは absolute mouse と同じものを使っています。こうしたバイト列は何らかの手段で入手する必要があるのですが、参考内のリンク先から引っ張ってくる以外に現実的な方法が見えてきません。また、self.PropertiesChangedでマウスのボタン押下状態、x、y を送信するのですが、どうやら x, y のそれぞれの範囲は 0 ~ 127 の整数値で指定していることに気づくかと思います。つまり、 [dbus.Byte(1), dbus.Byte(0x00), dbus.Byte(64), dbus.Byte(0x00), dbus.Byte(64)]
とした場合は左ボタンを押下した状態で画面中央付近にポイントが移動します。そして、直後に [dbus.Byte(0), dbus.Byte(0x00), dbus.Byte(64), dbus.Byte(0x00), dbus.Byte(64)]
とすることでクリックの挙動が再現されています。
まとめ
一番手間取ったのはrelative_mouse_service、absolute_mouse_service それぞれの notify_report
内の value
の範囲の理解でした。absolute_mouse_service は実際には x, y 位置が 0 ~ 127 の範囲になっていて、2 byte で表現する理解でいたので気づくのに時間がかかりました。この話を塩漬けにしたのは、いつか調査、整理してからと思ったからなのですが、プライベートの時間も限られているので、現状の材料で一度まとめました。 HID はダメですが、BLE Peripheral はブラウザ(WASM) から今後使われていくと思うので期待しています。