クォータニオンとかorientation filterとか

最近fenrirさんのオープンソースハードウェアプロジェクトNinja-Scan-Lite(ブログページ)を広めようとNinja-Scan-Lite(ちょっとすごいロガー)(Google Code内プロジェクトページ)を中国深センに部品実装まで発注しました。写真はちょっとすごいロガーの1号機です。最近無事に秋田大学に納めまさせて頂きました。
その記事は後日書くとして、ジャイロで値を計測しても、姿勢角が知りたいのに、けっこう悩ましいですよね。
クォータニオンってやつを使うと計算が便利ってところまでは調べるとわかるのですが、
日本語の教科書はあっても、Web上の日本語記事はロクなのがありません。
公開されている論文だと良いのがありますが、日本の某企業の公開論文が悪すぎて時間を無駄にしたりしました。

クォータニオンの説明と計算は実際に商品にしているx-io社のものが良いです。どこかで前提条件となるクォータニオンの説明を見た後で、実装を確認する感じのレベル対象です。
特にMatlabの実装まで示しているので、参考になります。
http://www.x-io.co.uk/quaternions/

x-io社の慣性計測ユニットは加速度とジャイロと地磁気の値から現在の姿勢をフィルタリングして姿勢を求めるアルゴリズムが走っています。
フィルタの中身は公開されていて、orientation filterで調べると出てきます。

orientation filterには2パターンのフィルタがあります。
・加速度とジャイロから姿勢を求めるIMU filter
・加速度、ジャイロ、地磁気から姿勢を求めるMARG filter

どちらも、フィルタの機能として動きが少ないこと前提です。
フィルタのパラメータである、βとγの値を変えると動いている対象にもある程度対応できるようですが、
加速度は重力加速度のみを検知していて、磁気センサーは地磁気のみを計測していることが、フィルタの前提条件になっています。
センサー統合する際に多く用いられるカルマンフィルタに比べて計算負荷が少ないことが論文中のメリットに書いてあります。

その分使用条件があるのですが。。。

MEMSセンサーのジャイロのバイアスは無視出来ないほど大きいので、このようなセンサー統合が必要になってきます。多くのスマホでももう少し簡単なフィルタが入ってセンサー統合しているはずです。complementary filterとかが多いのでしょうか。。。

x-io社は色々とオープンソースにしていて期待しているところです。

というか個人プロジェクト的には明らかにライバルです。
http://www.x-io.co.uk/category/open-source/

OpenRocketを日本語翻訳 続き

先の記事の続きです。

一時的なものですが、OpenRocketの日本語翻訳されたものができました。本バージョンにもそのうち対応される予定です。最新バージョンは必ず本家のここで確認してください。

OpenRocket2012-12-29.jar

元はダウンロードに置いています。直リンクです。

翻訳作業している間にブログのロゴやら何やらくれと言われたので、作ってみました。すごくやっつけ作業ですが、これから使っていきます。

ロゴ

Favicon
 

 

 

OpenRocketを日本語翻訳してみた

モデルロケットの設計、シミュレーションソフトであるOpenRocketの紹介の記事を前に書きました。

そのOpenRocketを日本語翻訳しました。各種言語に翻訳するためのプロジェクトページなどもあって、各種言語に翻訳されています。そこに翻訳作業にコミットしたことによって日本語も増えた形になります。

2012年12月23日現在、翻訳ファイルを送ったところなので、向こうの更新タイミング次第で日本語化できるようになるはずです。

日本語化方法

  1. Javaのランタイム入れてない人はここからDLしてインストールしておく
  2. OpenRocketを起動する
  3. メインメニューの[Edit]→[Preferences]の中のOptionsタブの中のInterface languageを日本語に変える

(ここではまだ日本語翻訳ファイルが入ってないので見えてない。)

あとは前の紹介記事から随分進化しているので、そのまとめ

改良されているポイント

 3Dビュー

かっこ良く表示されるようになりました

印刷機能

印刷機能が付いてPDFにも概略が保存できるようになりました。

Component analysis(成分分析)

風の向きや迎え角ごとの、部品ごとの重心や風圧中心・抗力はどこで発生しているか・ロール方向の運動方程式のパラメータはどうなるのかの解析が出来るようになりました。

Rocket optimization(最適化)

部品のパラメータを色々変えた時に距離や滞空時間が一番伸ばせる条件はどれなのかが自動的に探索される機能が付きました。うまい使い方はいまいちわからないですが、色々弄ってみると楽しいです。

Cusom Expressions(カスタム式)

打ち上げシミュレーションにおいて、元々入っているパラメータ以外の計算したいパラメータに関して自分で計算したい式を入れて出力できるようになりました。比推力Ispやら固有周波数、動圧なんかは入力すると使える人には使えるかもしれません。

このページ(List of useful sustom expressions)の式を参考に入力すると良いです。

Simulator listeners

自分でJavaのプログラムを書くことで、シミュレーションに関して拡張できるようになりました。高度な内容ですが、オープンソースらしい拡張性高いことを示すところです。

Andoroidアプリ

Androidのアプリがでました。作ったOpenRocketのファイル(orkファイル)を読み込むと設計したものが表示されます。編集とかは出来ずに、ただ単に見るだけ。うーん、シミュレーションの結果も一部は表示されるので外で確認したいときに便利なのかも。

Andoroid版のインストールはダウンロードのページにある[OpenRocket-Andoroid-xx.xx.apk]をダウンロードして、自分のAndroid端末のフォルダに入れて、フォルダから~.apkファイルをタップしてインストール。orkファイルもAndroid端末に入れておく必要が有るので入れてAndroid版OpenRocketから読み込み。

インストールの前にAndroidの[設定]→[アプリケーション]→[提供元不明のアプリ]にチェックを入れておかないとインストールできません。

 

雑記

ネットでモデルロケット調べてみても、古いサイトしか出てこなくて新しく始めたい人は既にやってる人とコンタクト取るしか無いような趣味のロケット界隈ですが、導入が簡単になって趣味人の人口が増えればいいなと思っています。

モデルロケットっていうのは何も知らない人から見ると物騒に見えますが、飛ぶものは見ていて成果がわかりやすく楽しいし、科学教育・ものつくり教育としてとても良い素材だと思っています。ちょっと進んでくると流体力学やら材料力学やら燃焼やら数値計算法やら電子工作やらと、勉強しなきゃいけない(勉強できる)範囲広くて幅広い力が付くと思います。

OpenRocketは小さなモデルロケットだけではなく、もう少し大きめのハイブリッドロケットのようなものにも使えるので、独自に解析ソフト作るより信頼性あると思います。超音速のときは幾つかの点で考慮しきれてない部分があって、精度が落ちるようですが、それ以外の点ではtechnical documentをみる限りかなり正確なようです。

気圧高度計(MS5611)をArduinoで使ってみた その1

前回までに以下の様な基板を作っていました。

MPU-6050基板作ってみた(前半)基板設計

MPU-6050基板作ってみた(後半)リフローしてみた

これは加速度ジャイロ地磁気、更に気圧計を載せて10軸の情報を得られるものです。ロケット姿勢・位置の計測用に使う予定です。当初の予定では秋月電子にも売っているMPL115A2というセンサを使う予定でしたが、さらに高精度な気圧計があるということでピンコンパチなこともあって、そちらのセンサを試してみました。

measurement社のMS5611-01BA03というものです。系列としては秋月電子などにモジュールとして売られているパララックス社高度計測モジュールに載っている気圧高度計のセンサ(MS5607)の次世代バージョンのものになります。

高分解で10cmまで測れると宣言していて本当かよ、とにわかに信じがたいので実際の動作を見てみました。

マイコンはArduinoでI2Cで接続しています。SPIでも繋げられるのでSPIの方が良さそうですが、今回の基板がそもそもMPU-6050というI2Cで繋がる基板への追加部品ということでI2Cでつなげています。

写真のように、5VのArduinoと繋がるようにセンサボートの前にI2Cのレベル変換モジュールを入れています、そしてSDカードに記録するために抵抗分圧で3.3Vで駆動するSDカードとつなげています。SDカードはArduinoにライブラリがあるのでそのまま使いました。すごくお手軽にうごくので嬉しいです。

MS5611でも秋月に売っているパララックス社の高度計測モジュールでも動くライブラリは存在しますが、センサがどのように動いているか理解のために自分でコード書いてみました。

Arduinoの既存のライブラリとしては

パララックス社が公開しているものFreeIMUを作っている方のブログに公開されているもの

があります。他にもmbedのライブラリとしてセニオネットワークさんが作っておられるライブラリとサンプルがあります。このmbedのライブラリはプロの仕事なので抽象度も高くキレイにまとまっていて勉強になります。

サンプルライブラリ

上のライブラリの方がオススメですが、自分で作ったものも公開しておきます。今回はシリアル通信でPCに表示させるところまでです。このあと、ログをSDカードに保存したり、生データにフィルタをかませたり、加速度ジャイロと合わせたりします。

/*
MS561101BA用スケッチ
MS5607でも使えるはず(たぶん)
I2Cでセンサデータを取得し、シリアル通信でPCに表示
by ina111
2012/07/16
*/

#include <Wire.h>

//気圧計のアドレス
#define MS5611_ADDR 0x76 //CBR =HIGHの時は0x76,LOWの時は0x77
//#define MS5607_ADDR 0x76

//気圧計で使う変数
uint16_t C_[6] ={40127, 36924, 23317, 23282, 33464, 28312}; //初期値
uint32_t D1=0,D2=0;
int32_t dT,TEMP=0;
int64_t OFF,SENS;
int64_t T2,OFF2,SENS2;
int32_t P=0;
int32_t Height;

unsigned long now;                   //現在時間を入れて変換時間を計算
unsigned long lastD1Conv,lastD2Conv; //最後に変換した時間micros()
unsigned long ConvTime = 10000;      //変換時間[マイクロ秒]
boolean SWD1Conv = true, SWD2Conv = true; //D1,D2どっちを処理しているか
boolean SWD1D2 = true;               //trueでD1の処理,falseでD2の処理

void setup(){
  Wire.begin();
  Serial.begin(9600);
  //Serial.begin(115200);
  delay(100);
  readPROM();
}

void loop(){
  getD1();
  getD2();
  getPressTemp(D1,D2);
  getHeight(P);

  Serial.print(millis());Serial.print("\t");
  Serial.print(TEMP);Serial.print("\t");
  Serial.print(P);Serial.print("\t");
  Serial.println(Height);
}

/*
 * func name  : getD1,getD2
 * processing : MS5611の生データD1,D2を取得
 * param      : 
 * summary    : 後述のstartConvとreadADC使用
 *              変換の時間前だと変換、変換後なら読み出しを行う
 *              D1とD2は交互に読み出されるようにスイッチしている
 * return     : 
 */
void getD1(){
  now = micros();
  //SWD1D2==trueだとD1,falseだとD2
  //SWD1Conv==trueだと変換、falseかつ時間経過後は読み出し
  if(SWD1Conv == true && SWD1D2 == true){
    startConv(0x48); //D1のとき0x48,D2のとき0x58,OSRによって変化
    lastD1Conv = micros();
    SWD1Conv = false;
  }else if(now - lastD1Conv >= ConvTime && SWD1D2 == true){
    D1 = readADC();
    SWD1Conv = true;
    SWD1D2 = false;
  }
}

void getD2(){
  now = micros();
  if(SWD2Conv == true && SWD1D2 == false){
    startConv(0x58); //D1のとき0x48,D2のとき0x58,OSRによって変化
    lastD2Conv = micros();
    SWD2Conv = false;
  }else if(now - lastD2Conv >= ConvTime && SWD1D2 == false){
    D2 = readADC();
    SWD2Conv = true;
    SWD1D2 = true;
  }
}

/*
 * func name  : getPressTemp,getHeight
 * processing : MS5611の生データから温度TEMP、気圧P、高度Heightを計算
 * param      : ADC1,ADC2    / getD1,getD2で得られたD1,D2
 *              hPa          / getPressTempで得られたPをhPaにしたもの
 * summary    : MS5611のデータシートによる計算,と高度気圧式から線形化
 * return     : 
 */
void getPressTemp(uint32_t ADC1, uint32_t ADC2){
  dT   = (int32_t)(ADC2 - ((int32_t)C_[4] << 8));
  TEMP = 2000 + ((dT * (int64_t)C_[5]) >> 23);
  OFF  = (((int64_t)C_[1]) << 16) + (((int64_t)C_[3] * dT) >> 7);
  SENS = (((int64_t)C_[0]) << 15) + (((int64_t)C_[2] * dT) >> 8);
  P    = ((ADC1 * SENS) >> 21) - OFF >> 15;

  if (TEMP < 2000) {
    T2    = (dT * dT) >> 31;
    OFF2  = 5 * (TEMP - 2000) * (TEMP - 2000) >> 1;
    SENS2 = 5 * (TEMP - 2000) * (TEMP - 2000) >> 2;
    TEMP = TEMP - T2;
    OFF  = OFF - OFF2;
    SENS = SENS - SENS2;
  }  
}

void getHeight(int32_t hPa){
  //Height = -938.502 * hPa/100.0 + 948697; //t0=30[deg]で1000mまでの線形近似
  float t0 =30.0;float P0 = 1013.25;
  Height = 153.8*(t0+273.2)*(1-pow((hPa/100.0/P0),0.1902));
}

/*
 * func name  : startConv,readADC
 * processing : MS5611の内部ADCの変換開始関数
 *              変換後OSRの時間はアクセス不可
 *              ADCの結果を読み込む関数。returnでuint32_tが出てくる
 * param      : command    / D1,D2,OSRによってアドレスが異なる
 *                         / OSR(Over Sampling Ratio)             
 * summary    : startConv(command) -> delay(10) -> readADC()
 *              変換コマンドを送信
 *              値呼び出しコマンドを送信、受信
 * return     : 
 *              conversion    / ADCした値.D1,D2のこと
 */
void startConv(uint8_t command){
  Wire.beginTransmission(MS5611_ADDR);
  Wire.write(command);
  Wire.endTransmission();
}

uint32_t readADC(){
  uint32_t conversion = 0;
  // start read sequence
  Wire.beginTransmission(MS5611_ADDR);
  Wire.write(0x00);
  Wire.endTransmission();

  Wire.beginTransmission(MS5611_ADDR);
  Wire.requestFrom(MS5611_ADDR, 3); //3byteリクエスト
  if(Wire.available()){
    conversion = Wire.read() * 65536 + Wire.read() * 256 + Wire.read();
  }
  return conversion;
}

/*
 * func name  : readPROM
 * processing : MS5611の工場校正値を読み込み
 * param      : 
 * summary    : C_[i]に0~5読み込む(C1~C6とは一つズレている)
 * return     : 
 */
void readPROM(){ 
  for(int i=0; i<6; i++){
    Wire.beginTransmission(MS5611_ADDR);
    Wire.write(0xA2 + i*2); //PROMの最初 データシートでは0xA0に見えるが0xA2から   
    Wire.endTransmission();

    Wire.beginTransmission(MS5611_ADDR);
    Wire.requestFrom(MS5611_ADDR,2);
    if(Wire.available() >= 2){
      C_[i] = Wire.read() * 256 + Wire.read();
    }
  }
}

 

MPU-6050基板作ってみた(後半)リフローしてみた

前半からの続きです。

作った基板のセンサのハンダをつける足は外にでてない恥ずかしがり屋さんばかりです。特に地磁気センサや気圧計は基板につけると足が全く見えない真性の引きこもりです。このハンダ付けについてのことです。

QFNパッケージなどのハンダ付けはホットエアーを使えばできたのと、Fenrirさんがやってる方法もあるみたいです。しかし、手先の器用さが関係なく誰でも作れるようになるといいなと思ってリフローを試してみました。

参考にしたのはスイッチサイエンスのご自宅リフローキットのページです。

準備

大学にいると環境が良く、以下のものが使えました。スイッチサイエンスの例ではクラフトロボというカッティングマシーンではんだのマスク(ステンシル)を作っています。自分はEagleで基板設計したものを直接読み込めるメリットを考えて基板切削機を使いました。

  • ステンシル作り:基板切削機(LPKF社製)
  • ヒーター:ホットプレート(加熱実験用)
  • ステンシル1:OHPシート(厚さ100μm)
  • ステンシル2:レーザープリンター専用紙(サンワのつやなしマット紙厚手0.174mm)
  • クリームはんだ:鉛ありのもの(鉛フリーの方が健康的に使えそうだったと反省)
  • 道具類:ピンセット、スキージ(使用済みクオカード)、基板固定用基板、両面テープ、薄刃カッター

最初に試したOHPシートはちょうどいい厚み(100μm)で硬さも良さそうで、紙みたいに濡れないので使ってみました。しかしこれが上手くいきませんでした。

OHPシートの失敗

OHPシートを切削機で削るとバリが出るのではんだを塗るときに浮いてしまい、境界がきちっとでませんでした。目で見えるバリは取ったつもりでも、端面が曲がっているようなバリは取りきれませんでした。

写真は最初にOHPシートでカットしてプリヒートまで行った一番ヒドイときのものです。基板とステンシルが離れているためにクリームはんだが広がって細かい部分が全部潰れてしまっています。あと、プリヒートすると少し粘度が下がってはんだが流れていくので写真のような状態になりました。

このまま部品を付けずにリフローすると多くがブリッジしてしまいました。

レーザープリンター専用紙

そこでスイッチサイエンスのページの通りにポリプロピレン合成紙というのを使ってみようと思いました。同じ物かわからなかったのですが、似ていると思いレーザープリンター専用の紙(生協で売っていたサンワのつやなしマット紙厚手0.174mm)を使いました。

基板切削機のユニバーサルカッターは刃が円錐状になっているので表面と裏面でステンシルの大きさが違うので広い方が基板につくようにした方が良さそうでした。

以前作った基板用のステンシルはこんなです。薄刃カッターで切り残りなどアヤシイ部分を手直ししています。

やってみて

スイッチサイエンスのページにほとんど書いてるようなことですが、ポイントだなと思った点は以下です。

  • Eagle上のCAM ProcessorでtCreamを表面反対にして(Mirrorをチェック)ガーバーデータ(GERBER_RS274X)を出力する
  • そのままだとtCREAMを残して切削してしまうので、カットオフを利用して読み込んだガーバーデータを切り抜く形にする
  • 基板をしっかり固定する
  • ステンシルを浮かせない(浮かないように周りの押さえの基板よりハンダ塗布の基板の下に紙を挟んで高くした)
  • スキージはカードが薄くて良かった
  • スキージで細かい部分にしっかりクリームはんだを入れ込む
  • スキージを立ててしっかりこそぎ落とす
  • リフローの温度管理を時間を守ってしっかり
  • リフローしたらルーペで検査

全部の端子がうまくいくわけではないので手ハンダである程度修正を行なっています。ルーペは大事です。

ステンシルを基板に貼ったところと、出来た基板はこんなです。

参考

スイッチサイエンスさんが作ったサイトと動画が参考になります。

http://trac.switch-science.com/wiki/HomeReflowKit

 

リフローをせずにホットエアーでハンダ付けようと思うと以下の動画が参考になるみたいです。