こんにちは、Reveです。技術のほうです。
最近は仕事の方で立て込んでいましたが、久々に投稿したいと思います。
今回も電子部品の紹介で、加速度センサーを紹介します。
以前の記事でも加速度センサーを取り扱ったことはありましたが、改めて詳細について書いていこうかと。
(加速度とは何か)
加速度センサーとは名前の通り加速度を計測するものですが、では加速度とは何でしょうか?
要は「単位時間当たりの速度の変化率(Wikipedia)」ですが、言い換えると「1秒間でどれだけ速度が増すか」を意味します(単位はm/s2)。)。
速度に時間をかけて物体の移動量(距離)が求まるのと同様に、加速度に時間をかけて速度の増加量が求められます。
加速度は物体に外力(外からの力)が加わることで生じますが、これを理論的に表したものが、学校でも習うニュートン力学の「運動の第2法則」および「運動方程式」です。授業で習った「F = m × a」という数式は、加わった力と加速度の関係を表し、力と物体の質量から加速度を求められることを意味しているのです(正確な理論については面倒なので割愛)。
(改めて、加速度センサーとは)
当然、加速度を計測するものですが、どういった場面で利用されるかといえば、物体にかかる力の測定、歩行・走行など運動の解析、さらには姿勢計測などにも使われます。
加速度でなぜ姿勢が取れるかというと、実は静止していても重力が常に働いているため、そこから物体にかかる加速度(重力加速度)を求められ、重力がかかる方向から結果として姿勢が分かるのです(ちなみに、重力加速度は約9.8m/s2)。
重力といえばやはりニュートンが発見したことでおなじみですが、初めてこのように使われていたと知った時は驚きました。
説明だけでは想像しづらいかと思いますので、図にしてみました。
今回は簡単にするために2次元のみで考えてみます。
センサーは重力(加速度)を、互いに垂直な3方向(今回は2方向のみ)に分解して測定します。
センサーに対して水平方向に働く力と垂直方向に働く力の割合か逆三角関数によって傾いている角度がわかるのです(詳しい解説やより正確な手法などについては割愛。ググれ)。
そのため、逆に動いている状態では物体の姿勢を計測することはできません。
運動を見ながら物体の姿勢も測りたいときは、他のセンサーと組み合わせるなどの工夫が必要です。
近年ではセンサーも小型・低価格化が進み、利用しやすいモジュールの形で販売されているものも増えました。
主に電圧の高低で出力(アナログ出力)するものと、SPIやI2C通信などで測定データを送信するものに分かれますが、使いやすさ重視であれば前者、正確なデータ重視であれば後者を選ぶとよいでしょう。
(サンプル)
せっかくですので、この姿勢計測を実際に試して見ましょう。
今回使うセンサーは、秋月電子通商のKXR-2050です。なお、当方は前のバージョンのものを使用していますが、ピンの配置や基本的な性能は一緒です。このセンサーとArduino Micro(なければArduino Unoでも可)でサンプルを作ります。
それでは、回路とプログラムを紹介していきます。
回路は、以前の記事で制作したものとまったく同じです。
なお、Arduinoとセンサーのピン対応は以下の通りです。
Arduino | 加速度センサー |
5V | 1, 2 |
GND | 3, 5 |
A0 | 6 |
A1 | 7 |
A2 | 8 |
回路を一通り見たところで、いよいよプログラムについて見てみます。
とりあえずソースコードすべてを見てみましょう。
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 |
; title="Arduino_i2c_Raspi1.ino"]//加速度センサーのパラメータ #define MaxX 739 //1G加わった時の値 #define MaxY 737 #define MaxZ 762 #define MinX 298 //-1G加わった時の値 #define MinY 296 #define MinZ 365 //加速度センサ int workX[8]; //X軸方向の出力 int workY[8]; //Y軸方向の出力 int workZ[8]; //Z軸方向の出力 int offsetX = 0, offsetY = 0, offsetZ = 0; //中央値 void setup() { Serial.begin(9600); //シリアル通信の転送速度:57600bps //中央値および相殺値 //0gの時, 出力平均が中央値より大きいなら、相殺値を足す //0gの時, 出力平均が中央値より小さいなら、相殺値を引く offsetX = (MaxX + MinX) / 2 + 1; offsetY = (MaxY + MinY) / 2 + 2; offsetZ = (MaxZ + MinZ) / 2; //加速度センサの初期値 for(int i = 0; i < 8; i++)workX[i] = analogRead(0); for(int i = 0; i < 8; i++)workY[i] = analogRead(1); for(int i = 0; i < 8; i++)workZ[i] = analogRead(2); } void loop() { double theta; //出力平均値(加速度センサx, y, z) int adcavX = 0; int adcavY = 0; int adcavZ = 0; //出力の平均値を軸(x, y, z)別に求めていく //単純移動平均をとることで突発的な誤差の影響を少なくする for(int i = 0; i < 8; i++) { if(i == 7) { workX[i] = analogRead(0); workY[i] = analogRead(1); workZ[i] = analogRead(2); } else { workX[i] = workX[i + 1]; workY[i] = workY[i + 1]; workZ[i] = workZ[i + 1]; } adcavX += workX[i] >> 3; //3bitの右シフトは 8<2の3乗>の割り算 adcavY += workY[i] >> 3; adcavZ += workZ[i] >> 3; } //加速度センサの出力を正規化 int normaX = ((long)adcavX * 1023 - (long)offsetX * 1023) / (MaxX - MinX); int normaY = ((long)adcavY * 1023 - (long)offsetY * 1023) / (MaxY - MinY); int normaZ = ((long)adcavZ * 1023 - (long)offsetZ * 1023) / (MaxZ - MinZ); //逆三角関数(AVRマイコン用)も利用できる, atan2も使用可 if(normaZ > normaX || -normaZ > normaX) { theta = asin(normaZ / sqrt((double)normaX * normaX + (double)normaZ * normaZ)); } else { if(normaZ < 0) theta = -acos(normaX / sqrt((double)normaX * normaX + (double)normaZ * normaZ)); else theta = acos(normaX / sqrt((double)normaX * normaX + (double)normaZ * normaZ)); } Serial.print(theta, 4); //小数点以下4桁を表示 } |
大まかな流れとしては、以下の通りです。
1. 加速度センサーのパラメータ(中央値、相殺値)調整
2. 加速度センサーの値を取得
3. 傾斜角の計算と標準入力(シリアル通信)
加速度センサーの処理については、からくり小箱様のこちらの記事を参照しました。
今回のような加速度を電圧で出力するタイプのセンサーは、それぞれの出力の最大値と最小値が本来の仕様と若干ずれてしまうため、正規化をかけて、中央値と実際のゼロ点のずれを相殺値によって補正しています。ちなみに、理想的には中央値が電源電圧の半分(2.5V)、出力感度が1/5(1V/g)になります。
また、単独のアナログ入力による取得値では、ノイズや軽い振動による出力のずれが影響してしまうため、移動平均という手法で過去の値と平均をとって細かい値の変化を抑えています。
プログラムを書き込んで、角度が表示されていれば成功です。
表示は、垂直に立てた場合を0°として、±180°で傾斜角が表示されます。
ちなみに、加速度とジャイロが一体になったセンサーがAmazonで売られていました(しかも450円と安い!!)。I2C通信で動かすようですが、こちらも試してみるといかがでしょうか。
コメント