アイコン

佐藤さとる

梶研 [リアルタイムでスマホの姿勢を表示するアプリ]

2024年07月16日

thumbnail

リアルタイムでスマホの姿勢を表示するアプリ

出席率

  • 3年セミナー:??%

スケジュール

短期的な予定

  • mocopi と お料理センシング
    • シーンとランドマークを決める(~2月上旬)
    • SVM で動作判別する
    • 機械学習を深める
    • お料理センシング
      • お料理でどんな動作があるかを知る
      • レシピを決める
      • 関節を3次元座標に変換する
      • 関節を2次元座標に変換する
      • ST-GCN を大体理解
      • ST-GCN で動作推定
      • 伊達巻きで動作推定する
      • レシピ(手順書)を元に動作推定を補う
    • 論文書く
    • 発表
  • BookWorm
    • Pasori と デスクトップアプリを接続する(技術検証)
    • nfc読み込み機能 & 画面を作る
    • API と連携させる
    • 管理者画面を作る

長期的な予定

  • 6月 精度は置いておき、動作認識を完成させる
  • 7月 リファクターと精度を向上させる
  • 8月 仕上げ
  • 9月 論文書き始め
  • 10月末 論文提出
  • 12月 WiNF

進捗報告

ST-GCN

進捗ありません


リアルタイムでスマホの姿勢を表示するアプリ

オーキャンでリアルタイムで表示できるアプリあれば嬉しいかも

技術構成

スマホ側 (モバイルアプリ)

React Native (expo): 使ってみたかった

PC側 (デスクトップアプリ)

Tauri (Rust, React - Vite)

通信

候補

  • Web Bluetooth API
  • Bluetooth
  • Socket(TCP)
  • WebSocket

Web Bluetooth API 使ってみる

ブラウザでBLE通信ができる WebAPI(ブラウザーAPI)

1 // デバイスをフィルター 2 const options: RequestDeviceOptions = { 3 filters: [ 4 { 5 namePrefix: "デバイス名のPrefix", 6 }, 7 ], 8 }; 9 10 // デバイスを探す & 接続 11 const device = await navigator.bluetooth 12 .requestDevice(options) 13 .then((device) => { 14 return device; 15 }) 16 .catch((error) => { 17 console.error(error); 18 return null; 19 }); 20 21 // 通信のためのあれこれ 22 const server = await device?.gatt?.connect(); 23 const service = await server?.getPrimaryService('battery_service'); // PrimaryServiceの指定 24 const characteristic = await service?.getCharacteristic(service.uuid); 25 26 console.log(characteristic); 27 28 // 受信時のイベント 29 characteristic?.addEventListener('characteristicvaluechanged', (e) => { 30 console.log(e); 31 })

相手もブラウザの場合、PrimaryService ってなに...?

1const service = await server?.getPrimaryService('battery_service');

Web Bluetooth API [MDN]

どうやら、BLE通信でセンサー等としか接続できないらしい?
少なくともブラウザ同士でやるものじゃない

Bluetooth やめた

Rust側 が面倒そう.

Socket通信(TCP) 使ってみる

接続すれば双方向から通信できるいいやつ
(HTTP はサーバーから送信はできない)

Rust(Tauri)で実装(サーバ側)

1// サーバー起動 2pub fn start_server(app: &mut App) { 3 let app_handle = app.app_handle(); 4 5 thread::spawn(move || { 6 let port_number = 12345; 7 let listener = TcpListener::bind(SocketAddr::new( 8 IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9 port_number, 10 )) 11 .expect("Failed to bind to address"); 12 13 print!("Server started"); 14 emit_socket_payload( 15 &app_handle, 16 SocketState::Started, 17 "Server started".to_string(), 18 ); 19 20 recv(listener, app_handle); 21 }); 22 23 print!("Server Stop"); 24}
1// 接続待ち 2fn recv(listener: TcpListener, app_handle: AppHandle) { 3 loop { 4 match listener.accept() { 5 Ok((mut socket, addr)) => { 6 emit_socket_payload( 7 &app_handle, 8 SocketState::Connected, 9 format!("Accepted connection from {:?}", addr), 10 ); 11 read(&mut socket, &app_handle); 12 } 13 Err(e) => { 14 eprintln!("Accept error: {}", e); 15 std::process::exit(1); 16 } 17 } 18 } 19}
1// 受信しまくる 2fn read(socket: &mut std::net::TcpStream, app_handle: &AppHandle) { 3 loop { 4 let mut buffer = [0; 1024]; 5 match socket.read(&mut buffer) { 6 Ok(0) => { 7 emit_socket_payload( 8 app_handle, 9 SocketState::Disconnected, 10 "Client disconnected".to_string(), 11 ); 12 13 break; 14 } 15 Ok(bytes_read) => { 16 let message = String::from_utf8_lossy(&buffer[..bytes_read]); 17 emit_socket_payload(app_handle, SocketState::Received, message.to_string()); 18 } 19 Err(ref e) if e.kind() == ErrorKind::WouldBlock => { 20 continue; 21 } 22 Err(e) => { 23 emit_socket_payload( 24 app_handle, 25 SocketState::Error, 26 format!("Error reading from client: {}", e).to_string(), 27 ); 28 break; 29 } 30 } 31 } 32 33 // Close the connection if it's still open. 34 if let Err(e) = socket.shutdown(Shutdown::Both) { 35 emit_socket_payload( 36 app_handle, 37 SocketState::Error, 38 format!("Shutdown failed: {}", e).to_string(), 39 ); 40 } 41}
1// React(フロント)に送信 2fn emit_socket_payload(app_handle: &AppHandle, state: SocketState, data: String) { 3 let payload = SocketPayload { state, data }; 4 app_handle.emit_all("socket", payload.clone()).unwrap(); 5}

簡単にできた.

React

いい感じにした

1export const useSocket = (handler: recvHandler, event: EventName = 'socket') => { 2 const isListening = useRef(false); 3 4 useEffect(() => { 5 if (isListening.current) return; 6 isListening.current = true; 7 8 (async () => { 9 console.log('Listening to socket events'); 10 const unlisten = await listen(event, (event: SocketEvent) => { 11 handler(event.payload.state, event.payload.data); 12 }); 13 14 return () => { 15 unlisten(); 16 isListening.current = false; 17 }; 18 })(); 19 }, []); 20};

React Native

react-native-tcp-socket というライブラリがあった

1// 接続 2const client = TcpSocket.createConnection({ port: portNum, host: ip }, () => {}); 3// 送信 4client?.write(sendData); 5// 切断 6client?.destroy();

楽にできた

センシングする

expo には DeviceMotion っていう便利なやつがいた.
加速度, 線形加速度, 角速度, 回転量, デバイスの向き を扱える

1export const useSensor = (handler?: Handler, sampleingRate = 1 / 60) => { 2 const [data, setData] = useState<DeviceMotionMeasurement>(); 3 const [subscription, setSubscription] = useState<Subscription>(); 4 5 /** 6 * センサーの読み込みを開始する 7 */ 8 const subscribe = () => { 9 const deviceMotion = DeviceMotion.addListener((data) => setData(data)); 10 DeviceMotion.setUpdateInterval(sampleingRate); 11 setSubscription(deviceMotion); 12 }; 13 14 /** 15 * センサーの読み込みを停止する 16 */ 17 const unsubscribe = () => { 18 subscription?.remove(); 19 setSubscription(undefined); 20 }; 21 22 if (handler && data) handler(data); 23 24 return [data, { subscribe, unsubscribe }] as const; 25};

簡単にできた

スマホを表示する

Three.js でできた

センサーの値にフィルターを掛ける

データ

  • 22Hz前後
  • 1秒前まで持っている(変更可)
  • 加速度(重力含む) と 角速度
    • 各x, y, z

フィルター

相補フィルターでやるつもり

  1. 加速度のノルムの分散を出す
  2. 分散が 0.05 以上 であれば動いているとみなす
    • 動いている場合
      • 現在の姿勢(回転量) に足していく(積分)
    • 動いていない場合
      • 重力加速度から下方向を出す

現状

お家では動いたけど...
ReactNative から socket が繋がらない...
(ping は飛ばせる)

動画撮ってなかった...

  • 動いていない状態は問題なく表示できた
  • 動いている状態では暴れ出した
    • 多分周波数あたりのなんやかんやをどうにかすればいい感じになるはず

TODO

  • センサデータを0.5秒に1回まとめて送信するようにしたい
    • 0.5秒くらいの遅延は許して欲しい
    • 現状はセンサの周波数だけ送信している (Reactが可哀想)
  • フィルタを修正したい

進路関係

pluszero (長期インターン) が幸福です

余談

余談はありません.
梶研入ってから1週目(2023/4/11 ~ 4/18)以外は欠かさずに余談を生成していたので悲しいです.

余談は任意(強制)です
by makino

スクリーンショット 2024-07-02 12.00.13.png (41.3 kB)