home1.gif

back1.gif

Acceleration Sensor

概要

 ここではアナログデバイス社で製造されている2軸加速度センサ「ADXL202」について、そのデータの取り扱い方について説明する。
 右の写真はADXL202のサンプル基板である。この写真からも分かるとおり、このセンサは非常に小型軽量に設計されている。実物はピン間が1.27mmピッチであるため、ハンダ付けのまったくの素人には少々扱いにくいサイズといえるかも知れない。
 このセンサは秋月電子通商などで購入できる。写真のタイプの物は1個2800円(2001年9月25日現在)、これよりもさらに1サイズ小さいタイプの物が2000円で販売されている。ただし、販売はチップ単体での販売であるため、基板を自作する技術がなければ何の役にも立たないことを念頭に入れて置いて欲しい。

 

 

傾斜計としての使い方 

 このセンサの用途はその名が示すとおり加速度を測定することであるが、案外素人レベルの日用電子工作で一番使われるのは「傾斜計」としての用途ではないかと思われる。
 なぜ加速度センサで傾斜が測れるのかだが、少し考えれば分かることで、重力加速度も加速度であることに違いはない。我々は四六時中その加速度にさらされているので、改めて車で実感するようなGを体感することは無いが、この9.8m/s2という値はちゃんとセンサの出力として現れる。

 そこで、見て貰いたいのが右の図である。
 重力加速度gは図に示すとおり物体の鉛直下向きに働く。もし、物体が鉛直方向に対して90度であった場合にはθはゼロとなり、

 g・sinθ = 0
 g・cosθ = g

となる。
 ここで、このセンサはチップの横向きに対して掛かる加速度を検出するように設計されているので、出力値としては、加速度=0というふうに扱われる。

 

 

 では、物体が図のようにθだけ傾いている場合、センサ取り付け方向には重力加速度gの分解成分g・sinθが掛かることになる。したがって、センサの出力としてもg・sinθに相当する値が出力される。

 これにより、この加速度センサは水平位置から±90度の傾斜計として使用できることが分かる。

※±90度を超える場合の測定

 sin89°= sin91°

であることからも分かるとおり、加速度センサがある位置から回転をはじめ、一旦真横を向き、さらに回転が進んだ場合、出力値はgをピークに対称となる。したがって、このセンサ単体での90°を超える測定はできない。しかしながら、同一のセンサを2つ、互いに垂直になるように配置した場合、片方のセンサが最大値を示しているとき、もう片方のセンサは最小値を示すというように、2つの値を合わせて考えることで90°を超えるような測定にも応用することが可能である。 

 

ピン配置と回路例

回路例

 ピンの割付と基本的な回路例を右に示す。
 ピンで気をつけなければならない点は特には無いが、GND接続が2カ所あること、Vdd(2.5〜5.25V)が2カ所あることだけは注意したい。

 ノイズフィルタのC1とC2は電解コンデンサではなく、セラミックコンデンサを使用する。カットオフ周波数によって変わってくるのでメーカーの仕様書を調べていただきたい。ちなみに、暴言に近いが、ここで設定した周波数でざっくり落ちるわけではないので、極端な話、大きいコンデンサを付けておいた方が扱いやすいかもしれない(私は1.5μFを付けているが、十分使えている)。

 また、周波数設定のR1だが、これは

周波数(Hz) = 125MΩ / R1

の式で求まる。つまり、125kΩの抵抗をつければ、出力は1kHz、1.25MΩの抵抗をつければ100Hzとなる。

ピン

記号

機能

NC

接続しない

Vtp

テストポイント

ST

セルフテスト

COM

GNDに接続

T2

周波数設定

NC

接続しない

COM

GNDに接続

NC

接続しない

Yout

Y軸出力

10

Xout

X軸出力

11

Yfilt

Y軸フィルタ

12

Xfilt

X軸フィルタ

13

Vdd

電源

14

Vdd

電源

 

データの出力形式

 この加速度センサの信号(Xout、Yout)の波形をオシロスコープで観測すると右の図のような波形が見られる。この図は周波数を1kHz(つまりR1=125kΩ)、電源電圧を5Vで動かしたときの場合を例にしている。

 センサ自体を軸に沿って傾けると、Highの時間+Lowの時間は変化せず、このHighの長さとLowの長さの比(デューティー比と言う)が変化する。
 つまり、水平時が50%:50%であるならば、傾けることによって(あるいは加速度を加えることによって)40%:60%になったりするわけである。なお、仕様書では水平時にはデューティー比50%ということになっているが、これは必ずしも正しくはない。実際にはセンサ個々によって個体差がかなりある。

 ここまでの説明から想像が付くように、マイコンでこのセンサ情報を読み取るためには、センサ出力のHighの部分かLowの部分、どちらかの時間を測定すれば良いことがわかる。

 

マイコンでの読み取り方法

 センサ出力がこのような信号(PWM(Pulse Width Modulation)信号という)である場合、マイコンで読み取る方法は大まかに言って2通りしかない。すなわち、

方法1. 信号の立ち上がりから立ち下がりまでを監視し続け、その間、ある値をインクリメントし続ける
方法2. インプットキャプチャを使う

この2つである。方法1は非常に簡単である。たとえば、センサの信号線がPort-1のビット0番に繋がっているならば、C言語では次のようなサブルーチンを呼べばよい。

int  get_adxl(void)
{
  int cnt = 0;

  while(!P1.DR.BIT.B0);   // Lowレベルを確認
  while( P1.DR.BIT.B0);   // 立ち上がりを待つ

  while(!P1.DR.BIT.B0){   // 立ち下がりまでカウントをインクリメント
    cnt++;
  }

  return(cnt);
}

 この方法のメリットは、なによりもプログラミングが簡単明瞭であること、そしてハードウェアリソースを消費しなくて済むことがメリットである。しかし、その反面、信号が立ち下がるまでの間はサブルーチンから決して返ってこないこと(もちろん普通はタイムアウトを設けるので不慮の事故で信号が来なくなっても返るようにする)、信号を計測している間は他の処理ができないことが問題である。

 これに対して、方法2のインプットキャプチャを使えばハードウェアリソースとしてタイマーを1つ消費するが、信号計測自体にはほとんどCPUパワーを使わずに、一見「自動的に値が入ってくる」ように見える。インプットキャプチャについての詳しい使い方はこちらのページで解説しているので参照してほしい。

 

 

ばらつき補正

  ここまでの解説でセンサの出力を読み取り、それを変数に代入するところまではできる。しかし、最後に現実的な問題がひとつ残っている。それはセンサ出力のドリフトという問題である。

 実際に計測してみれば一目瞭然であるが、これだけに限らずセンサという物は大なり小なりドリフトという現象が伴う。つまり、センサ自体はまったく動かしていないにも関わらず値が変化するのである。物によってはあらかじめそれを抑制する回路の内蔵された物も存在するが、このADXL202に関してはそんな物は無い。したがって、ソフト側で何らかの処理を施してやることでドリフト分を吸収しなければいけない。

 もっとも簡単な方法は平滑化という手法である。これは過去何回か分のデータを蓄えておき、それの平均を取るという方法である。そして次のデータが入ったときには最も古いデータを破棄し、再び平均を取るのである。これにより、「使えるデータ」が得られるまでには一定の遅れが生じるが、ほとんどの場合は信用のできるデータになる。

 上記の方法1を使った場合を例に、簡単なソースを示す。


#include "iodefine.h"

#define AVE_MAX   16

int  get_adxl(void);
int  averaging(void);


int wk[AVE_MAX];        // 作業用の配列をグローバル宣言しておく

void  main(void)
{
  int ret;

  while(1){
    wk[AVE_MAX - 1] = get_adxl();  // 配列の最後尾に最新情報を入れる
    ret = averaging();        // 平滑化処理の結果を変数retに代入する
  }
}

int  get_adxl(void)
{
  int cnt = 0;

  while(!P1.DR.BIT.B0);   // Lowレベルを確認
  while( P1.DR.BIT.B0);   // 立ち上がりを待つ

  while(!P1.DR.BIT.B0){   // 立ち下がりまでカウントをインクリメント
    cnt++;
  }

  return(cnt);
}

int  averaging(void)
{
  int  i;
  int  ret;
  long tmp = 0;

  for(i=0; i<AVE_MAX; i++){     // 過去のデータを全て加算する
    tmp += (long)wk[i];      // 値がint型に収まらない場合はlong型を使う
  }

  ret = (int)(tmp/AVE_MAX);    // その平均を取る

  for(i=0; i<AVE_MAX -1; i++){   // 1つ分配列をずらす
    wk[i] = wk[i+1];
  }

  return(ret);
}