加速度センサで角度の計算|ArduinoとMMA8452Q

重力加速度から姿勢角度を計算する

これから行うことは次のとおり。
- 重力加速度から姿勢角度を計算する
- Arduinoで加速度センサMMA8452Qを使ってみる
- Arduinoと加速度センサで姿勢角度を計算する
加速度センサを使って姿勢角度を計算する方法を解説します。ここで説明するのは、重力加速度から姿勢角度を計算する方法になります。また話を簡単にするため「重力方向のz軸」と、「前後方向のx軸」の2軸に絞って考えます。
加速度センサから角度を計算する方法
センサにおけるx軸方向の加速度を(a_x)、z軸方向の加速度を(a_z)とします。
ここでは以前にRaspberry Piで作った 倒立振子
のようなモデルで姿勢角度を考えてみましょう。
倒立振子がθだけ傾いた時を考えてみます。ただし、この状態で静止しているものとします。
物体は静止しているので、重力(a)のみの加速度がかかってます。 よって、センサで読み取った加速度(a_x)と(a_z)のベクトルの合成値が、そのまま重力加速度(a)になるはずです。

これを分かりやすく書き直すと、次の図のようになります。
つまり、θは加速度(a_x)と(a_z)を使って次の式で表すことができます。
\[tanθ=\frac{a_x}{a_z}\]
姿勢角度θを計算するために、逆三角関数のアークタンジェントを使います。姿勢角度θを、次式であらわすことができました。
\[θ=tan^{-1}\frac{a_x}{a_z}\]
Arduino言語(C++)でアークタンジェントを使う場合は、atan2
関数使います。
(ax, az) atan2
また、Raspberry
PiなどのPythonでアークタンジェントを使うには、numpy
のarctan2
関数を使います。
np.arctan2(ax, az)
Arduinoで加速度センサMMA8452Qの使い方
それでは実際にArduinoで加速度センサをつかってみましょう。
開発環境
項目 | バージョン |
---|---|
Arduino IDE | 1.8.13 |
パソコン | macOS Big Sur 11.0.1 |
ArduinoとMMA8452Qの配線
こちらが、ArduinoとMMA8452Qの配線図になります。
ArduinoとMMA8452Qの配線で注意があります。それは、330Ωの抵抗を通してSCLとSDAをつなぐことです。ArduinoのGPIOが5Vであるのに対し、MMA8452Qが3.3V基準なので破損を防ぐため、抵抗でレベルシフトさせる必要があります。お使いのArduinoのデジタルピンが3.3Vの場合は、レベルシフトの必要はありません。

MMA8452Qライブラリのインストール
加速度センサからデータを読み取るには、SparkFunが公開しているMMA8452Q
のArduinoライブラリを使うと簡単です。
下記のGitHubページからzipファイルをダウンロードしてください。
https://github.com/sparkfun/SparkFun_MMA8452Q_Arduino_Library
zipファイルを解凍し、フォルダを~/Document/Arduino/libraries/
へ移動します。そして、Arduino
IDEを再起動します。
ライブラリのインストールができていれば、File
→
Examples
→
SparkFun MMA8452Q Accelerometer
以下にMMA8452Q
のサンプルプログラムが表示されるはずです。
もしくはライブラリマネージャから、「SparkFun
MMA8452Q」でライブラリ検索してインストールしてください。
ソースコード
こんな感じのプログラムを作っていきます。
サンプルプログラムをそのまま使わせて頂きました。加速度センサで取得した値をシリアルモニタや、シリアルプロッターで表示させることができます。
#include <Wire.h>
#include "SparkFun_MMA8452Q.h"
;
MMA8452Q accel
void setup() {
.begin(9600);
Serial.begin();
Wire
if (accel.begin() == false) {
.println("Not Connected. Please check connections and read the hookup guide.");
Serialwhile (1);
}
}
void loop() {
if (accel.available()) {
.print(accel.getCalculatedX(), 3);
Serial.print("\t");
Serial.print(accel.getCalculatedY(), 3);
Serial.print("\t");
Serial.print(accel.getCalculatedZ(), 3);
Serial.println();
Serial}
}
ソースコードの解説
accel.getCalculatedX
などで取得される値は、重力加速度1gを単位としてます。また、Spark
Funの MMA8452Qチュートリアル
によれば、データの転送速度は800Hzで、±2gまで測ることができるそうです。
Arduinoと加速度センサで姿勢角度を計算
加速度センサの使い方が分かったところで、今度は実際に、Arduinoを使って加速度センサから角度を計算させてみます。
ソースコード
Arduinoと加速度センサで姿勢角度を計算させたプログラムがこちらです。冒頭で説明したように、アークタンジェントを使って姿勢角度を制御してます。
#include <Arduino.h>
#include <Wire.h>
#include "SparkFun_MMA8452Q.h"
;
MMA8452Q accel
void setup() {
.begin(9600);
Serial.begin();
Wire
if (accel.begin() == false) {
.println("Not Connected. Please check connections and read the hookup guide.");
Serialwhile (1);
}
}
void loop() {
if (accel.available()) {
float ax = accel.getCalculatedX();
float az = accel.getCalculatedZ();
float angle = atan2(ax, az) * 180.0 / PI;
.print(angle, 3);
Serial.println();
Serial}
}
Processingで視覚化
これから行うことは次のとおり。 - Processingでシリアル通信、Arduinoからデータの受信 - 加速度センサの姿勢に合わせ、Processingで図形を動かす - ローパスフィルタでセンサノイズの除去、動きの違いを視覚化
ArduinoのデータをProcessingで受け取る
まずは、Processingでシリアル通信からデータ受信を行います。
こちらの映像のように、ArduinoのSerial.print
で出力したデータを、Processingで受け取ってテキストで表示させました。
Processingのソースコード
Processingのプログラム内容は次のとおりです。
import processing.serial.*;
;
Serial serial= "";
String v int lf = 10;
void setup()
{
(Serial.list()[3]);
println
(200, 150);
size(30);
frameRate
= new Serial(this, Serial.list()[3], 9600);
serial .bufferUntil(lf);
serial}
void draw()
{
(255, 255, 255);
background
if(v != null) {
(0, 0, 255);
fill(28);
textSize(CENTER);
textAlign(v, 100, 75);
text}
}
void serialEvent(Serial p) {
if (serial.available() >= 2)
{
= p.readString();
v }
}
ソースコードの解説
シリアルポート名取得のために、Serial.list()
Arduinoのポートを確認し、指定します。また、シリアル通信の速度をArduino側と同じにします。
println
で送られた改行コード(Line
Feed)が受信されると、serialEvent
のイベントが呼び出されますので、そこで描画を更新します。
Arduinoのソースコードは前半で紹介したものを流用しました。
加速度センサの姿勢をProcessingでシンクロさせる
Processingで長方形のオブジェクトを作り、加速度センサの姿勢に合わせてオブジェクトをシンクロさせてみました。
Processingのソースコード
Processingのプログラムがこちらになります。四角形の中心を軸に回転させるため、rect(-w/2, -h/2, w, h)
のようにして起点をずらしました。また、角度はラジアンで受信してます。
import processing.serial.*;
;
Serial serialint lf = 10;
float ra = 0;
int w = 100;
int h = 40;
void setup()
{
(Serial.list()[3]);
println
(300, 300);
size(30);
frameRate
= new Serial(this, Serial.list()[3], 9600);
serial .bufferUntil(lf);
serial}
void draw()
{
(255, 255, 255);
background
(77, 77, 77);
fill(width/2, height/2);
translate(ra + PI);
rotate
(-w/2, -h/2, w, h);
rect}
void serialEvent(Serial p) {
if (serial.available() >= 2)
{
= float(p.readString());
ra }
}
ローパスフィルタでセンサノイズの除去、動きの違いを視覚化
Arduinoからシリアル通信で受信した加速度センサの姿勢角度に、Processingでさまざまなローパスフィルタをかけてセンサノイズを除去し、動きの違いを図形で視覚化してみます。

動画の続きはYouTubeでご覧ください。
ローパスフィルタが強いほど手ブレが目立たなくなりますが、反応が鈍くなって速い動きに追従できなくなります。
Processingのソースコード
6つのオブジェクトを作り、加速度センサの姿勢角度に係数を変えたローパスフィルタかけたProcessingのプログラムです。
import processing.serial.*;
;
Serial serialint lf = 10; // line feed
float ra = 0; // radian
int w = 100; // objects width
int h = 40; // objects height
int n = 6; // objects count
float[] y = {0, 0, 0, 0, 0, 0};
float[] a = {0, 0.2, 0.3, 0.5, 0.7, 0.9};
void setup()
{
(Serial.list()[3]);
println
(900, 600);
size(30);
frameRate
= new Serial(this, Serial.list()[3], 9600);
serial .bufferUntil(lf);
serial}
// y[n] = a*y[n-1] + (1-a)*x[n]
float rc_lpf(float y, float x, float a) {
return a * y + (1-a) * x;
}
void drawObject(int i, float ra) {
float dx = float(w) * float(i);
float dy = 0;
if(i >= 3) {
= float(w) * float(i-3);
dx = w * 1.5;
dy }
(width/(n+1) + dx, height/3 + dy);
translateif(n == 0) {
} else {
(dx, 0);
translate}
(16);
textSize("a="+a[i], -w/2, h*2);
textfloat r = rc_lpf(y[i], ra, a[i]);
[i] = r;
y(r + PI);
rotate(-w/2, -h/2, w, h);
rect();
resetMatrix}
void draw()
{
(255, 255, 255);
background(77, 77, 77);
fill(24);
textSize("Low-Pass Filter", 50, 70);
text("y[n] = a*y[n-1] + (1-a)*x[n]", 50, 120);
text
for(int i = 0; i < n; i++) {
(i, ra);
drawObject}
}
void serialEvent(Serial p) {
if (serial.available() >= 2)
{
= float(p.readString());
ra }
}
ローパスフィルタの解説
ここでは、プログラム内のローパスフィルタについて解説します。 ### ローパスフィルタとは ローパスフィルタとは、周波数の高い成分をカットするフィルタです。ArduinoやRaspberry Piなどのマイコンでは、センサノイズの除去に使ったりします。 たとえば、加速度センサで姿勢角度を測定する場合、微振動などのセンサノイズが混ざってしまいます。そこで、測定データにローパスフィルタをかけ、本来の動きよりも周波数の高いセンサノイズを除去します。
RCローパスフィルタのアルゴリズム
次の式は、デジタル信号におけるRCローパスフィルタです。非常に単純なアルゴリズムですが、手軽さゆえによく使われます。
\[y[n]=ay[n-1]+(1-a)x[n]\]
aは係数で、ローパスフィルタの強さと思ってください。yが出力で、xが入力です。また、nはある時点でのサンプルを意味します。 よって、この式で行なっていることは、1つ前の出力結果に係数をかけてフィードバックし、新たな入力と足し合わせたものを出力してます。
RCとは
RCローパスフィルタのRCとは、Resistor(抵抗)とCapacitor(コンデンサ)のことです。電子回路のアナログで使われるRC回路のローパスフィルタを、デジタル処理に置き換えたものが先ほどの式になります。
関連記事
- 10日で作る!ラズパイ倒立振子ロボット
- ArduinoとCdS光センサ
- M5StickC PLUSの加速度センサで振動測定と周波数特性
- 【Raspberry Pi】BMX055でジャイロ・加速度の測定