3軸加速度センサーのキャリブレーションを試みた(arduino)
加速度センサーとは
- 加速度を検知するセンサー.
- 加速度とは,速度の微分(変化量).
- 高校物理で習う方程式`F=ma`を思い出せば,物体に掛かっている力を検知するとも言えそう.
- 一番検知しやすいのは重力.センサーが重力の方向に対してどの程度傾いているのか検知する.
こちらの加速度センサーを使用.
3軸加速度センサーを手に入れたので軽く触る(arduino) - shonen.hateblo.jp
正確な値の測定
静止状態(加速していない状態)のセンサーの3軸の値の大きさを求めてみる.
この状態でセンサーが影響を受ける要素は重力のみである.
#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #if (SSD1306_LCDHEIGHT != 64) #error("Height incorrect, please fix Adafruit_SSD1306.h!"); #endif void setup() { pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64) display.display(); delay(2000); display.setTextSize(1); display.setTextColor(WHITE); } void loop() { int32_t acc_x = analogRead(A0)-512; int32_t acc_y = analogRead(A1)-512; int32_t acc_z = analogRead(A2)-512; double magnitude = sqrt(acc_x*acc_x + acc_y*acc_y + acc_z*acc_z); display.clearDisplay(); display.setCursor(0,0); display.println((magnitude)); display.display(); }
- 向きを変えて静止させると,元の数字より大きくなったり,小さくなったりする.
- なぜか?
- 上のプログラムでは,`512`をニュートラル値(力が全くかかっていない時の値)として扱っている.
- しかし,実際のところは違う可能性がある.
- http://shonen.hateblo.jp/entry/2018/03/20/135715 の結果から,ずれがあることが実験的に分かっている.
- まず,力が全くかかっていない時の値の推定をする必要がある.
- ニュートラル値を数値解析的に推定したい
パラメータの洗い出し
未知のパラメータは次の4つ
- x軸方向に全く力がかかっていない時のセンサーの値
- y軸方向に全く力がかかっていない時のセンサーの値
- z軸方向に全く力がかかっていない時のセンサーの値
- 重力の大きさ
測定出来るデータは次の3種類
- 静止した状態の時に測定したセンサーの値
測定データをいくつかかき集めて,未知パラメータを推定しよう,という事がしたいです.
方程式を解く
非線形の方程式なので,計算機に任せる方法を考えてみます.
二階偏微分を求めます.
ニュートン法の漸化式.
t回目の反復は次の式で表現できるので,これを実装してループを回せばよいです.
python3(numpy)を書きました.
from collections import namedtuple import numpy # 構造体っぽいの定義 Parameter = namedtuple("Parameter", "X Y Z G") Record = namedtuple("Record", "x y z") # 入力読み込み N = int(input()) records = [Record(*map(float, input().split())) for i in range(N)] param = Parameter(0, 0, 0, 0) for loopCount in range(50): dx = dy = dz = dg = 0.0 dxdx = dydx = dzdx = dgdx = 0.0 dxdy = dydy = dzdy = dgdy = 0.0 dxdz = dydz = dzdz = dgdz = 0.0 dxdg = dydg = dzdg = dgdg = 0.0 ja = 0.0 for rec in records: ja += ((rec.x - param.X) ** 2 + (rec.y - param.Y) ** 2 + (rec.z - param.Z) ** 2 - param.G)**2 dx += 4.0*((param.X - rec.x)**3 + (param.X - rec.x)*((rec.y - param.Y)**2 + (rec.z - param.Z)**2 - param.G)) dy += 4.0*((param.Y - rec.y)**3 + (param.Y - rec.y)*((rec.x - param.X)**2 + (rec.z - param.Z)**2 - param.G)) dz += 4.0*((param.Z - rec.z)**3 + (param.Z - rec.z)*((rec.x - param.X)**2 + (rec.y - param.Y)**2 - param.G)) dg += 2.0*(param.G - ((rec.x - param.X) ** 2 + (rec.y - param.Y) ** 2 + (rec.z - param.Z) ** 2)) dxdx += 4.0*(3.0*(param.X - rec.x)**2 + (param.Y - rec.y)**2 + (param.Z - rec.z)**2 - param.G) dydx += 8.0*(param.X - rec.x)*(param.Y - rec.y) dzdx += 8.0*(param.X - rec.x)*(param.Z - rec.z) dgdx += 4.0*(rec.x - param.X) dxdy += 8.0*(param.Y - rec.y)*(param.X - rec.x) dydy += 4.0*((param.X - rec.x)**2 + 3.0*(param.Y - rec.y)**2 + (param.Z - rec.z)**2 - param.G) dzdy += 8.0*(param.Y - rec.y)*(param.Z - rec.z) dgdy += 4.0*(rec.y - param.Y) dxdz += 8.0*(param.Z - rec.z)*(param.X - rec.x) dydz += 8.0*(param.Z - rec.z)*(param.Y - rec.y) dzdz += 4.0*((param.X - rec.x)**2 + (param.Y - rec.y)**2 + 3.0*(param.Z - rec.z)**2 - param.G) dgdz += 4.0*(rec.z - param.Z) dxdg += 4.0*(rec.x - param.X) dydg += 4.0*(rec.y - param.Y) dzdg += 4.0*(rec.z - param.Z) dgdg += 2.0 # print(ja) # print(param) jaco = numpy.matrix( ( [dxdx, dydx, dzdx, dgdx], [dxdy, dydy, dzdy, dgdy], [dxdz, dydz, dzdz, dgdz], [dxdg, dydg, dzdg, dgdg] )) ocaj = numpy.linalg.inv(jaco) pv = numpy.mat([param.X, param.Y, param.Z, param.G]).T - ocaj*numpy.mat([dx, dy, dz, dg]).T param = Parameter(pv[0, 0], pv[1, 0], pv[2, 0], pv[3, 0]) print(param)
入力データはこちら.arduinoから取得したデータで,静止時(姿勢は異なる)のセンサーの各軸の値です.
26 517 489 702 482 742 497 727 524 517 257 522 507 475 510 313 292 632 497 701 419 564 480 371 665 664 627 602 325 387 600 313 527 381 479 676 372 600 376 369 614 649 390 467 480 702 720 519 554 500 504 703 403 519 684 475 731 447 698 584 436 257 520 505 349 359 404 492 579 694 524 439 693 469 616 681 471 474 701
得たもの
Parameter(X=493.25812379306166, Y=511.6144020468419, Z=494.82229994669643, G=48703.524830696435)
結果をarduinoに組み込みます
void loop() { int32_t acc_x = analogRead(A0) - 493; int32_t acc_y = analogRead(A1) - 511; int32_t acc_z = analogRead(A2) - 495; double magnitude = sqrt(acc_x*acc_x + acc_y*acc_y + acc_z*acc_z); display.clearDisplay(); display.setCursor(0,0); display.println(magnitude); display.display(); }
結果
X,Y軸方向に傾けた時に30程度重力が大きく表示される….
おまけ
- もう少し高精度な関数当てはめを考える.
- 各軸ごとの傾向きの感度(倍率)を導入する.新たに導入する変数をとすると,i番目の測定データは次の方程式を満たすような挙動をする,と仮定する.
変数が7つに増えました.微分回数60回近く.また暇な時にやろうと思います.