加速度センサーの3軸をタイムチャートでグラフにして見ました。
low-pass filterの効果がわかり易くなりましたね、数値ではやはりわかりにくい。
 
![[Android] Accelerometer 加速度センサーをグラフにしてみた 1x1.trans - [Android] Accelerometer 加速度センサーをグラフにしてみた](https://akira-watson.com/wp-content/themes/simplicity2/images/1x1.trans.gif)
2021.2.1
Accelerometer
チャートはMPAndroidChartを使うことにしました。いろいろありますが、Androidの仕様変更に対応が比較的早いので(今の所ですが)。
チャートはLinerChartを使いました。こちら MPAndroidChart ライブラリーでグラフを描画 でも試しています。
 
また、SensorEvent | Android Developersにあるlow-pass filterも試してみます。
 
low-pass filter
GoogleのSensorEventには次のような式が載せてあります。
加速度センサーの生データは敏感に反応するため、急激な変動をある程度除去して、ゆっくりした動きを取り出す、つまりlow-passのフィルタリングをした方が人間にはわかりやすい場合があります。またある程度というのが味噌で、この場合alphaという定数によって変化が変わって来ます。
 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void onSensorChanged(SensorEvent event)      {           // alpha is calculated as t / (t + dT)           // with t, the low-pass filter's time-constant           // and dT, the event delivery rate           final float alpha = 0.8;           gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];           gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];           gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];           linear_acceleration[0] = event.values[0] - gravity[0];           linear_acceleration[1] = event.values[1] - gravity[1];           linear_acceleration[2] = event.values[2] - gravity[2];      } | 
サンプルコード
早速実装してみます。加速度センサーの元になるのは、加速度センサー Accelerometer を使ってみるです。ここでは生データを扱っているので、それに上のlow-passを入れた場合をボタンで切り分けるようにして見ます。
グラフは、MPAndroidChart ライブラリーでグラフを描画で使ったLineChartです。
 
最初にライブラリーのgradle設定からです。
settings.gradle
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | pluginManagement {     ... } dependencyResolutionManagement {     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)     repositories {         google()         mavenCentral()         maven {             url "https://jitpack.io"         }     } } ... | 
 
build.gradle (Module…)
| 1 2 3 4 5 6 | ... dependencies {     ...     implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' } | 
 
MainActivity.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | //package your.package.name import android.annotation.SuppressLint; import android.os.Bundle; import android.app.Activity; import android.content.pm.ActivityInfo; import android.graphics.Color; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import java.util.Locale; public class MainActivity extends Activity         implements SensorEventListener, View.OnClickListener {     private SensorManager sensorManager;     private Sensor accel;     private TextView textView;     private LineChart mChart;     private final String[] labels = new String[]{             "linear_accelerationX",             "linear_accelerationY",             "linear_accelerationZ"};     private final int[] colors = new int[]{             Color.BLUE,             Color.GRAY,             Color.MAGENTA};     private boolean lineardata = true;     @SuppressLint("SourceLockedOrientationActivity")     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // 縦画面         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);         // Get an instance of the SensorManager         sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);         // Get an instance of the TextView         textView = findViewById(R.id.text_view);         mChart = findViewById(R.id.chart);         // インスタンス生成         mChart.setData(new LineData());         // no description text         mChart.getDescription().setEnabled(false);         // Grid背景色         mChart.setDrawGridBackground(true);         // 右側の目盛り         mChart.getAxisRight().setEnabled(false);         Button buttonStart = findViewById(R.id.button_start);         buttonStart.setOnClickListener(this);         Button buttonStop = findViewById(R.id.button_stop);         buttonStop.setOnClickListener(this);         Button buttonChange = findViewById(R.id.button_change);         buttonChange.setOnClickListener(this);     }     @Override     public void onClick(View view) {         if(view.getId() == R.id.button_start){             sensorManager.registerListener(this, accel,                     SensorManager.SENSOR_DELAY_NORMAL);         }         else if(view.getId() == R.id.button_stop){             sensorManager.unregisterListener(this);         }         else if(view.getId() == R.id.button_change){             lineardata = !lineardata;         }     }     @Override     protected void onResume() {         super.onResume();         // Listenerの登録         accel = sensorManager.getDefaultSensor(                 Sensor.TYPE_ACCELEROMETER);         sensorManager.registerListener(this, accel,                 SensorManager.SENSOR_DELAY_NORMAL);     }     // 解除するコードも入れる!     @Override     protected void onPause() {         super.onPause();         // Listenerを解除         sensorManager.unregisterListener(this);     }     @Override     public void onSensorChanged(SensorEvent event) {         float[] gravity = new float[3];         float[] linear_acceleration = new float[3];         final float alpha = 0.6f;         if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER ) {             // alpha is calculated as t / (t + dT)             // with t, the low-pass filter's time-constant             // and dT, the event delivery rate             gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];             gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];             gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];             linear_acceleration[0] = event.values[0] - gravity[0];             linear_acceleration[1] = event.values[1] - gravity[1];             linear_acceleration[2] = event.values[2] - gravity[2];             String accelero;             if(!lineardata){                 accelero = String.format(Locale.US,                         "X: %.3f\nY: %.3f\nZ: %.3f",                         event.values[0],event.values[1],event.values[2]);             }             else {                 accelero = String.format(Locale.US,                         "X: %.3f\nY: %.3f\nZ: %.3f",                         gravity[0],gravity[1],gravity[2]);             }             textView.setText(accelero);             LineData data = mChart.getLineData();             if(data != null){                 for(int i = 0; i < 3; i++){                     ILineDataSet set3 = data.getDataSetByIndex(i);                     if(set3 == null){                         LineDataSet set = new LineDataSet(null, labels[i]);                         set.setLineWidth(2.0f);                         set.setColor(colors[i]);                         // liner line                         set.setDrawCircles(false);                         // no values on the chart                         set.setDrawValues(false);                         set3 = set;                         data.addDataSet(set3);                     }                     // data update                     if(!lineardata){                         data.addEntry(new Entry(set3.getEntryCount(), event.values[i]), i);                     }                     else{                         data.addEntry(new Entry(set3.getEntryCount(), linear_acceleration[i]), i);                     }                     data.notifyDataChanged();                 }                 mChart.notifyDataSetChanged(); // 表示の更新のために変更を通知する                 mChart.setVisibleXRangeMaximum(50); // 表示の幅を決定する                 mChart.moveViewToX(data.getEntryCount()); // 最新のデータまで表示を移動させる             }         }     }     @Override     public void onAccuracyChanged(Sensor sensor, int accuracy) {     } } | 
 
activity_main.xml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"     android:id="@+id/activity_main"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:padding="16dp"     android:orientation="vertical"     android:background="#dfe"     tools:context=".MainActivity">     <LinearLayout         android:orientation="horizontal"         android:gravity="center"         android:layout_margin="10dp"         android:layout_width="match_parent"         android:layout_height="wrap_content">         <TextView             android:id="@+id/text_view"             android:textColor="#00f"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />         <Button             android:id="@+id/button_start"             android:text="@string/start"             android:layout_margin="5dp"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />         <Button             android:id="@+id/button_stop"             android:text="@string/stop"             android:layout_margin="5dp"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />         <Button             android:id="@+id/button_change"             android:text="@string/change"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />     </LinearLayout>     <com.github.mikephil.charting.charts.LineChart         android:layout_width="match_parent"         android:layout_height="match_parent"         android:id="@+id/chart"/> </LinearLayout> | 
 
strings.xml
| 1 2 3 4 5 6 | <resources>     <string name="app_name">YourAppName</string>     <string name="start">Start</string>     <string name="stop">Stop</string>     <string name="change">Change</string> </resources> | 
 
![[Android] Accelerometer 加速度センサーをグラフにしてみた 1x1.trans - [Android] Accelerometer 加速度センサーをグラフにしてみた](https://akira-watson.com/wp-content/themes/simplicity2/images/1x1.trans.gif)
ボタンでlow-pass filterを切り替えてその効果を確認してください。また定数alphaによる違いはコードで変更するとわかります。
