はじめに
Xsens Dot を制御するAndroidアプリを制作できる「Xsens Dot Sdk」の、サンプルアプリに実装がない「Recording」の基本的な実装方法をまとめました。
Xsens DOT SDK本体や公式のドキュメントは、下記のURLのフォームに回答することで送られてくるメールからダウンロードすることができます。
https://content.xsens.com/xsens-dot-software-development-kit?hsCtaTracking=2af14a41-b15f-4733-b2ca-5498b2888842%7C21941862-cb62-421e-9e0e-2dac04d1ca9f
サンプルアプリもgithubに公開されています。サンプルにRecording機能はなく、Streamingのみの実装がされています。
https://github.com/xsens/xsens_dot_example_android
本記事は、サンプルアプリ内に実装されていることに関しては解説しておらず、サンプルアプリ内の実装に一通り目を通したことを前提で書いています。「サンプルアプリに変更を加えてRecordingもしてみたい!」「Xsens DOTでRecordingできるアプリ作りたいけど日本語情報が無い!サンプルアプリにRecordingの実装がない!」みたいに思っている人がもしもいたら読んでみてください。(ほぼ備忘録です)
また、ほとんど公式ドキュメントに書かれていることのはずなので、英文に抵抗がない方は公式ドキュメントを読むことをお勧めします。
筆者は普段はWeb開発をしており、Android開発はほぼ初学者レベルです。Qiita記事を書くのも初なので、あまりよろしくない記述等見つけましたらご指摘いただけると幸いです。
StreamingとRecordingの違い
計測方法には2種類あります。
Streaming -> 計測結果をリアルタイムに取得する。
Recording -> 計測終了後にデータを取得する。
今回解説するのは、後者のRecordingのみです。
Recordingの基本的な流れ
1.Bluetoothスキャンを実行し、Xsensを見つける。
2.Xsensと接続する。
3.Xsensと同期する。
4.Recordingを開始する。
5.Recordingを終了する。
6.データのエクスポートを開始する。
7.(データを出力する)
1, 2, 3まではサンプルコードに実装されているため、本記事では4以降を行うための実装方法を解説します。
XsensDotLoggerクラスを使用することで簡単にCSVで計測データを出力することができますが、この実装についてはサンプルコードに組み込まれていたと思うので割愛します。
Recordingを開始から停止まで
Recordingを行うためには、XsensDotRecordingCallbackを継承したクラスを用意する必要があります。
ここからは、すべて上記のクラス内で記述してください。
Xsensを操作するためにXsensそれぞれにたいしてXsensDotRecordingManagerをインスタンス化したものを用意し、通知を有効にします。
val manager = XsensDotRecordingManager(requireContext(), device, this)
manager.enableDataRecordingNotification()
//deviceは、同期が完了したXsensと接続するXsensDotDeviceをインスタンス化したものです。
enableDataRecordingNotificationのコールバックは下で受け取ります。
isEnableがtrueであることを確認したら、manager.requestFlashInfoを実行し、xsens内のデータ情報を取得します。
override fun onXsensDotRecordingNotification(address: String?, isEnable: Boolean) {
if(isEnable) {
manager.requestFlashInfo()
}
}
requestFrashInfo()のコールバックは、下で受け取ります。
Recordingを開始するためには、空き容量が総容量の10%以上なければなりません。
override fun onXsensDotRequestFlashInfoDone(address: String?, usedFlashSpace: Int, totalFlashSpace: Int) {
if(usedFlashSpace / totalFlashSpace < 0.1) {
// 空き容量が10%を下回った場合の処理。
// Erais(Xsens内のデータ削除)を行うことになると思います。Eraisについては今回は解説しません。
}
}
空き容量が10%以上であることを確認したら、manager.startRecording() / manager.startTimedRecording()によって、設定してRecordingを開始することもできます。2つの違いは、前者は時間指定がない、後者は時間指定があるということです。
Recordingを停止するには、manager.stopRecording()を実行します。
また、下記の場合もRecordingが停止します。
・Xsensのパワーボタンが一秒以上押された
・空き容量が10%を下回った
・(startTimedRecordingの場合)設定時間を経過した
Recordingの開始/停止ともに、実行後に下のコールバックを受け取ります。
override fun onXsensDotRecordingAck( address: String?, recordingId: Int, isSuccess: Boolean, recordingState: XsensDotRecordingState? ) {
if (isSuccsess){
// このアクションが成功した場合
if (recordingId == XsensDotRecordingManager.RECORDING_ID_START_RECORDING) {
// このアクションがRecording開始だった場合
}else if (recordingId == XsensDotRecordingManager.RECORDING_ID_STOP_RECORDING){
// このアクションがRecording停止だった場合
}
}
}
Recording中の時間経過をmanager.requestRecordingTime()で受け取ることができます。onXsensDotGetRecordingTime()がコールバック関数です。
計測データのエクスポート
データをエクスポートするためには、まずmanager.requestFlashInfo()を再度実行しておく必要があります。
※これをしないと最新の計測情報がエクスポートできません。
requestFlashInfo()の実行が完了したら、次にmanager.requestFileInfoを実行します。
実行後に下のコールバック関数が呼ばれます。
override fun onXsensDotRequestFileInfoDone( address: String?, list: ArrayList<XsensDotRecordingFileInfo>?, isSuccess: Boolean ) {
}
manager.selectExportedData( ByteArray型 )でエクスポートするデータを選択し、manager.startExporting( Array型 )でエクスポートを開始します。
onXsensDotRequestFileInfoDone内で最後の計測データのみをエクスポートする処理を実装した場合下記のようになります。
override fun onXsensDotRequestFileInfoDone(
address: String?,
list: ArrayList<XsensDotRecordingFileInfo>?,
isSuccess: Boolean
) {
if(isSuccess && address != null){
val selectExportedDataId = byteArrayOf(
XsensDotRecordingManager.RECORDING_DATA_ID_CALIBRATED_ACC,
XsensDotRecordingManager.RECORDING_DATA_ID_CALIBRATED_GYR
)
manager.selectExportedData(selectExportedDataId)
val selectExportFile = arrayListOf(list.last())
manager.startExporting(selectExportFile)
}
}
manager.selectExportedData( ByteArray型 )に渡す配列の中身は、下記の中から選択します。
RECORDING_DATA_ID_CALIBRATED_ACC | Acc id |
RECORDING_DATA_ID_CALIBRATED_GYR | Gyr id |
RECORDING_DATA_ID_CALIBRATED_MAG | Mag id |
RECORDING_DATA_ID_CLIP_ACC | Clip count acc id |
RECORDING_DATA_ID_CLIP_GYR | Clip gyr acc id |
RECORDING_DATA_ID_DQ | dq id |
RECORDING_DATA_ID_DV | dv id |
RECORDING_DATA_ID_EULER_ANGLES | Euler angles id |
RECORDING_DATA_ID_IQ | iq id |
RECORDING_DATA_ID_IV | iv id |
RECORDING_DATA_ID_ORIENTATION | Orientation id |
RECORDING_DATA_ID_STATUS | Status id |
RECORDING_DATA_ID_TIMESTAMP | Timestamp id |
onXsensDotRequestFileInfoDoneで返ってくるArrayList<XsensDotRecordingFileInfo>には、各計測ごとの計測データ情報が格納されています。
先ほどのコードでは、最後の計測のデータのみをエクスポートするために、新しい配列(selectExportedFile)を定義しmanager.startExporting()の引数としています。
val selectExportedFile = arrayListOf(list.last()) // 最後の計測のデータのみ
manager.startExporting(selectExportFile)
エクスポートが開始されると、下の3つのコールバック関数が呼ばれます。
・各計測タイミングのエクスポート後(120Hzで計測した場合と、120x計測時間(s)回呼ばれる)
override fun onXsensDotDataExported(
address: String?,
fileInfo: XsensDotRecordingFileInfo?,
exportedData: XsensDotData?
) {
exportedData.acc
exportedData.gyr //こんな感じで計測データを取得できます。(=> 1.98729398)
}
・各ファイルのエクスポート後
override fun onXsensDotDataExported(address: String?, p1: XsensDotRecordingFileInfo?) {
}
・全ファイルのエクスポート後
override fun onXsensDotAllDataExported(address: String?) {
}
onXsensDotAllDataExportedが呼ばれたらエクスポート完了です。
といってもこれらを実行しただけでは保存されたりはしないので、XsensDotLoggerクラスを利用するなどして保存する処理を書く必要があります。(サンプルアプリはXsensDotLoggerクラスでcsvに保存する実装がされていたと思います)
エクスポートを中断したい場合は、manager.stopExporting()を実行します。コールバック関数は↓
override fun onXsensDotStopExportingData( address: String?){
}
そういえば、公式が公開しているサンプルアプリにRecording機能つけたい人向けみたいなこと書きながらkotlinで解説してしまいました。(サンプルアプリはJava)