【俺式】ジャイロセンサの調整術

はじめに

タイトルにある【俺式】の通り、経験則に基づいたパラメータの決め方でしかありません。
実装は各自の責任でお願いします。

また、調整するパラメータのおおよその値(桁数的な意味でのスケール)を見つけていくことになります。

これができるとマイクロマウスにおける宴会芸ができるようになります。

ジャイロのゲインの調整をする項目について

ジャイロを調整するにあたって、調整する項目は何なのかですよね?

調整する順を説明します。

  1. センサー値の扱い
  2. 取得したセンサー値を角速度に変換する定数(1種類)
  3. 現在の理想の角速度(=理装置)と、センサーから取得した角速度のずれから、理想値に追従するために使用する定数(PID制御を前提3種類。本記事ではPIまでとする)

0. ジャイロセンサとは

現在の角速度の値を出力するセンサーです。 出力の形式は主に2つあります。

  • アナログ式・・・CPU側でA/D変換で電圧を読み取り、角速度にする
    • マイクロマウスにおいては、ADXRS610、ISZ-650、LY3500がよくつかわれている。
  • デジタル式・・・IC側でセンシングしたデータをSPI/I2C通信などの通信方式で取得する
    • マイクロマウスにおいては、MPU-6000(6500)、ICM-20xxx系などInvenSence社のセンサーがよくつかわれている。SPI/I2Cの両方を扱えるICが多いが、速度の関係上、通信規格上速いとされる、SPIを採用することが多い。

1. センサーの値の扱い

ジャイロセンサはアナログ・デジタル問わず、常に値がぶれています。これは他のセンサー(フォトトランジスタ)と似たものと理解してください。

デジタル式においても、不動の状態でも値は0にはならず、いくつかサンプリングした値を参考に(ゼロ点という)、参考値との差分と定数を掛けたものを角速度の現在値として、利用します。

float k ;    // given
float omega; // [rad/s]
float gyroRef ; // ゼロ点
float gyroData ; // ゼロ点
omega = k *  (gyroRef - gyroData) ;

取得には前述したとおりアナログ・デジタルに違いがあるのですが、予めぶれている値であることから、フィルタ処理をする必要があることがあります。

デジタル式では、IC側でフィルタ処理がすでに施されている場合もあります。

【参考情報】ゼロ点の取得について

値を256回取得し平均したものを、ゼロ点として用いてます。 256回である理由は、2のべき乗による除算は計算誤差を減らすのに有効だと勝手に思ってます。

2. 角速度に変換する定数

まず、前述の定数kを求めます。
しかし、この時点では厳密な値は必要ありません。ゲインのスケールを併せることが目的です。 後述の3が終わらないと、正確なゲインは与えられません。 以下のように、角速度(omega)を積分し角度(angle)します。

#define dt 0.001 // given 
omega = k *  (gyroRef - gyroData) ;
angle += omega * dt;

取得した角度の値をUARTでリアルタイムで監視し、初期値0から90度になるまで手でロボットを回してみて、kの値を調整します。

3. 理想値に追従するために使用する定数

【前提】
マイクロマウスにおいては、ジャイロ以外に、モーター側のフィードバック制御のゲインも調整する必要があります。
即興の調整として、PI制御でPとIが決まってるとよいです。

以下の順で決めます。

  1. Pゲイン
  2. Iゲイン

調整においては1つプログラムの実装が必要です。 具体的には、マウス界隈で言われる宴会芸のプログラムを実装してください。 マウスに速度0、角速度0を維持する制御をすればよいです。

Pゲインの決め方

紙の上にマウスを置き、宴会芸を実行します。マウスがその場で回転するように紙を動かします。
紙を速く動かしている間だけマウスが動いていればよいです。
マウスの動きがバタバタし始めたり、音があり始めると、値が大きする証拠なので、その半分ぐらいにするとよいです(経験則)

Iゲインの決め方

Pゲインの決め方同様、宴会芸で紙を動かしていきます。 Pゲインとは違い、ゆっくり動かして、マウスが応答してくれればよいです。 徐々にIゲインを上げていき、プルプルし始めたら、ゲインが高いといえるので、その半分ぐらいにするとよいでしょう(経験則)

終わりに

Dゲインについてはスラロームなどログを取って調整することになります。P,Iについても同様にログから微調整したほうが良いです。 あまりゲインを上げ過ぎるとロバスト性が悪くなることもありますのでご注意を。

本来なら動画ベースでまとめられるとよいのですが、深夜テンションでやっつけ仕事で書いたので、調整順と調整用のアプリぐらい参考になればと思います。

新作"Alter"完成

新作紹介

機体名は、ExiaAlter。 Exia改として作成したが、見た目とかはQuantaそのもの。

名前の由来は勿論、00から。 2つ前の機体、Exiaの名前が呼びやすいということから、名前を戻した。

Quantaからの変更点

f:id:nao1288stusj:20180325173149j:plain 実物 f:id:nao1288stusj:20180325173152p:plain 設計時

前作との違いは、

  • センサーの数を6つから5つに変更
  • エンコーダ付きモーターに変更
  • ギア比を小さくした(63:17 ⇒67:21)
  • 吸引モーターに使っているモーター(DCX10-S)を4.5V版から3V版に変更
  • 電源から5Vを取得するときに使うICをLDOからDCDCに変更(効率化のため)
  • タイヤを低扁平率に変更(4点接地できてないのか、すり減り方に偏りが・・・)
  • バッテリーを160mah⇒200mahに変更(予定)

参考にしてはいけない設計

いくつか自分でリスクを承知に、仕様書無視をやった。やらかしているのは2か所。

  • エンコーダが5V系だが、その出力を直接マイコンに入れている

    これにより3.3V系で統一されていた回路がなぜか3.7Vとなってしまい、AD変換の基準電圧がぶれセンサーが使えなくなってしまった(指で触れると3.3~3.7Vのうちランダムに値が変わる)マイコン内部で流入したと考えている。ADの基準電圧取得のピンに別のLDOから取得した3.3Vを入れて統一した

  • センサー、LEDをすべて5V系で統一

    ADの入力値が3.3V以上になる(=飽和する)。LEDの出力を低くする(抵抗値を20Ω⇒50Ωに変更したら、十分使えるようになった)

新機能-1

画像を見ても分かるが、DCマウス特有の前向きのセンサー2つ、横向き2or4つの構成を止めた。 斜めの制御に前向きのセンサーを傾けて使用する必要はないと考えたためである。

まっすぐ向いた前センサーと2つの真横のセンサーを使うことで、前壁制御屋、探索中の壁切れの再現性・精度が向上した。

新機能-2

前述の前センサーを1つにした代わりに、斜めの制御を45度センサーに任せるようにした。 この構成は、「シン・ウムオ」を参考にしたもので、全日本で見た大階段での制御を見て、いけそうかなと思い、その後、K島氏とも話してみて、確信し、仕様変更をした。

実際に制御を変えてみたが、傾けた前壁センサーをつかった制御よりも再現性があること、ログの値から評価がしやすいことから、現状、改善点のみが見受けられる。

(新機能)工夫-3

エンコーダ付きモーターにしたことで、DCX-10から出ているコードが邪魔である。 処理を考えたところ、モーターの導線を2.54ピッチのコネクタ(流せる電流量が3A以上のため)、エンコーダ周りをハーフピッチとして、合計6つあるコネクタを接着剤で固め独自のコネクタを作っている。 実装ミスは取り返しのつかないことになるため、実装には最新の注意を払いアルコール駆動開発を)実施。見てみたい人は大会会場で見せます。

新機能-4

2017年度大会もやりたかったことだが、吸引探索をしなくては今後の海外大会で戦績が残せない時代が来ると考えている。

5分とはいえ、吸引を続けると後半ばてることが予想されるため、燃費について工夫を行った。 前述したが、今回、12.6Vから5Vにする際にLDOではなく、DCDCを使用し、取得した5Vを、センサーLEDに使用している。

この工夫により、回路のアートワークがしやすいことに加え、今までLDOを使用した際に生じていた、ドロップ損失が抑えられた(と思う)。あくまで感覚的ではあるが、電池の交換スパンが長くなった気がする。(自己満足かなと)

これにより、前述した仕様書無視をやらかしている。悪いとは思っているが、反省はしていない。

新しい充電器

中部勢がよく使う充電器を手に入れた

実は現役中部支部会員であるにも関わらず、いまだに手を出してなかったISDT Q6 Plus 300Wをアマゾンで購入。 もっと安いところがあるかもしれないが、それはおいといて、いろいろ紹介する。f:id:nao1288stusj:20171216003419j:plain

機能

  • 対応バッテリーの種類
    • LiHv
    • LiPo
    • LiIon
    • LiFe
    • Pb
    • NiMH/Cd
  • 最大セル数6セル
  • バランサー端子あり
    • 未接続時、充電開始時に確認あり
  • 日本語対応
  • UI
    • センタープッシュ+コロコロ
      • 短く押すと決定。
      • 長押しで設定に入る。

必要なもの

  • コネクタ(XT60コネクタのメス)×2
    • 2個同梱しているがなんかきつすぎたので、買ってきた。
  • ACアダプター(秋月の12V1Aのこれ
    • 端子を切断して、プラス、マイナスどちらかを確認したうえでXT60コネクタにはんだ付け+熱収縮チューブで保護
  • バッテリーにつなぐケーブル
    • ケーブルはXT60コネクタにはんだ付け
    • 逆側はバッテリー用のコネクタを付ける。

感想

  • 音が大きかったので、設定で「低」に変更
  • 画面がキレイ
  • ファンの音が気になる
  • 充電が何%ぐらいかがわかるのがいい。
  • シリコングラフェンが使えるのは驚いた。
  • スペースを取らない。
  • XT60コネクタの抜き差しが固い

調整の手間を省こう

この記事はMice Advent Calendar 7日目の記事です。

全日本について

更新してなかったので、この場で。
まずは、2017年度お疲れ様でした。全日本ではクラシック競技で我々Bustersの圧倒的勝利でしたね。

自分は4位とMice系トップは守りました。U宮さんとは今年こそ、日本人が優勝取り返しましょう話していましたが、ふがいない結果・・・。これで晴れて地球人に戻させていただけたと思います。いやしかし、10/19に基板が届いてから丁度1か月で4位なので、実は相当良いのでは??

あとakoちゃんは個人的にバスター対象にしたので、来年はシーズン通して完封します。


さて、今日の記事ですが、 web系わからない人には初見バイバイです。さようなら。

パラメータ調整の手間を軽減しよう。

もう、ただただ調整がツライ。 いうことで。ツライ要因を上げる。

  • 理論がいまいち
  • 調整パラメータはすべてハードコーディングされているため都度、ビルド⇒書き込みが発生していた。

前者は皆努力しても無駄なので、後者を専用システム構築をして改善する。

構成

f:id:nao1288stusj:20171205004258p:plain

図にはデータフラッシュとか書いてあるので、RX系マイコンを前提に話をしますが、ロボット側に不揮発性メモリがあればよいです。

socket.ioは双方向通信のために使用しているのは高速な更新と、サーバーからのレスポンスを反映を容易にするためです。

Node.jsを利用した理由は環境構築がものすごく楽だから。OS依存ないし。

コンソールGUI

コンソールGUIはWebアプリで作成。どの道どんなGUIでも内部でXML使っているし、HTML5でそこらへんに転がってるサンプルを使えば、おしゃれに作れる。

ここら辺使うのはどうだろうか

フレームワークにAngular.JSなど、双方向データバインディングがあると楽です。

  • Angular.JS/Angular (2+)
  • React
  • Vue
  • Riot

jQueryは不要。以下を参照。
You might not need jQuery.

socket.ioから送信するデータの体裁

{
 "type":"update",
 "id":123456,
 "name":"TIRE",
 "value":24.5,
 "descript":"タイヤ直径[mm]"
}

ロボットにはid(マイコン上の書き込み先アドレス)とvalueだけ渡せばよいですが、コンソール画面上には、変数名とそれが何なのかを示すため、nameとdescriptも送信。

サーバー

今回Node.jsにはwebサーバーはいらないです。expressを使って、socket.ioサーバーと同時に建てられますが、サーバーからhtml貰う必要ないので、webリソースは全てローカルファイル参照でいいでしょう。HTTPリクエストは飛ばさないので、CORSの考慮も不要。 コンソールとの通信はsocket.ioにすべて任せます。

以下、必要なnode_modulesのインストール。

npm install --save-dev socket.io serialport

上の2ついればOKです。serialportはpython前提になります。こちらを参照。

サーバーにはコンソールGUIからリクエストしたファイルをローカルファイルに書き込む。わざわざDB立てるのももったいない上、別環境での再構築や復元が面倒です。

var fs = require('fs');
function readFile(path, success) {
    fs.readFile(path, 'utf8', function (err, data) {
        if (err) {
            throw err;
        }
        success(data);
    });
}
function writeFile(path, data) {
    fs.writeFile(path, data, function (err) {
        if (err) {
            throw err;
        }
    });
}

コンソールGUIから来たデータをファイルに書き込んだら、いよいよシリアル通信です。 自分は以下のようにデータを送信してます。コンソールからはnameなど色々送信してますが、ロボット上での解析が面倒なので、必要なデータだけ送ります。(json-cがあればもっと複雑な形式で送信できますね。必要なのかは別として)

// {key:value}
 {123456:24.5}

ロボット側の処理

サーバーからの送信データは、接頭に{ 、接尾に}、Key-Valueのセパレータにコロンを使用しています。
ロボット側には接頭文字を検知したら、受信バッファを初期化、以降のコロンと接尾文字を除き、バッファに貯める。接尾を検知したら、バッファから送信データを構築します。

key(id)には4バイト以内の自然数valueには浮動小数として認識させます。

#define MAX_BUFFER 20
typedef struct {
    int index;
    int length;
    char buffer[MAX_BUFFER];
} s_key;
volatile s_key keys, values;

void detectChar() {
    SCI1.SSR.BIT.RDRF = 0; // 受信フラグを解除
    char recieveData = SCI1.RDR;
    switch (recieveData) {
    case '{':
        recieveMode = KEY;
        flushData();
        break;
    case '}':
        recieveMode = END;
        applyRecieveData(recieveMode, recieveData);
        break;
    case ':':
        recieveMode = VALUE;
        break;
    default:
        applyRecieveData(recieveMode, recieveData);
        break;
    }
}
void applyRecieveData(char type, char data) {
    if (type == KEY) {
        pushKey(data);
    } else if (type == VALUE) {
        pushValue(data);
    } else if (type == END) {
        mapping();
    } else if (type == STAY) {
    }
}
void mapping() {
    const char* keyBuffer = &(keys.buffer);
    const char* valueBuffer = &(values.buffer);
    long key = atoi(keyBuffer);
    double value = atof(valueBuffer);
    save(key, value);
}
void pushKey(char key) {
    if (keys.length < MAX_BUFFER) {
        keys.buffer[keys.length] = key;
        keys.length++;
    }
}
void pushValue(char val) {
    if (values.length < MAX_BUFFER) {
        values.buffer[values.length] = val;
        values.length++;
    }
}
void flushData() {
    for (char i = 0; i < MAX_BUFFER; i++) {
        keys.buffer[i] = values.buffer[i] = 0;
    }
    keys.index = keys.length = values.index = values.length = 0;
}

かつて、CとJSのソースを1つの記事に書いたヤツがいるだろうか・・・。

画面イメージ

途中まで作ってみました。左の写真には各センサー値を表示、値を見ながら、閾値を調整するという形式にしてみました。 f:id:nao1288stusj:20171207000129p:plain

今後の課題。

  • パラメータの調整だけではなく、テストシナリオも同様にバイナリ書き込みではなく、フラッシュからの読み出しから実行したい。
  • json-cを使う。
  • 大量のデータを一度に更新できるようにする。

新作構想

北信越大会終わりましたが、モチベーションが上がりません。

そこで、何を思ったか、クラシックに現実逃避すれば、少しはマシになるかもしれん。ということで、

今回はそのブループリントを書いていく。

構成

CPUや足回りについては大体決まっている。

名称 備考
CPU RX71M 100pin RAMの暴力
モーター DCX10L 4.5V版フランジ付き、取り付けネジ90度傾け
吸引モーター DCX10S 4.5V版フランジ付き
センサー ST-1KL3A 6個 取り付けは再現性向上のため、ネジ止め
センサーLED SFH4550 6個 取り付けは再現性向上のため、ネジ止め
モータードライバ TB6614FNG 新規設計非推奨に指定
通信 TWELITE ワイアレスしたい。
ジャイロ ICM-20602 or MPU ICM系は手はんだ付けしにくいので敬遠
エンコーダー AS5147P or IEH2-4096 後者は使わなくなった1717から奪う?・・・?
バッテリー Hyprion G3 180mAh(25C) or Hyprion G5 160mAh(50C)×3 吸引するので電源は大事。
吸引ファン 外形Φ32 内径Φ16 10枚歯 高さ13.25mm 3Dプリンタ前提
モーターマウント アクリルでつくる  3Dプリンタ前提
外部メモリ MRAM  SPI前提で何か
ギア比 63:19 (M0.3)  3Dプリンタ(アクリル) + kkpmo(真鍮)
電源 DCDC⇒5V(2A)⇒リニアレギュレータ⇒3.3V)  ドロップ時の熱対策の結果

不安要素いますね。嘘情報も紛れていますが・・・。

狙い

  • センサーは真横を見るようにして、探索時に壁切れをしやすくし、安定性向上
  • TWELITE⇔シリアル通信(UART)で、無線&ログをリアルタイム出力
  • RX71Mのデータフラッシュが使いにくいので、外部メモリに、調整したパラメータを入れる。(RX71M向けのライブラリが出たらしいので、それを見てから。決める)
  • パラメータ調整はTWELITEを使って確認する
  • DCX10はいずれも4.5V仕様にすることで、3セル対応する。
  • ギア比は効率重視の結果。(滑らかさ次第で、POMで作り直しも視野)

懸念事項

  • やる気
  • 時間

個人的に時間を懸念することが珍しいと思っている。
あまり理由にならないと思っていたが、最近ガチで忙しい。

【備忘録】Edisonの環境構築

Intel Edisonの環境構築手順

  • システム側の最新化は済ませておく

ネットワークの構築

開発PCに挿したモバイルスポットを使用する
Windowsのモバイルホットスポットから部屋のWifiを指定しブリッジさせ、グローバル接続も可能にさせる

固定IPの振り方

/etc/wpa_supplicant/wpa_cli-actions.sh を以下のように修正

if [ "$CMD" = "CONNECTED" ]; then
    kill_daemon udhcpc /var/run/udhcpc-$IFNAME.pid
#     udhcpc -i $IFNAME -p /var/run/udhcpc-$IFNAME.pid -S
     ifconfig $IFNAME 192.168.10.2 netmask 255.255.255.0
     route add default gw 192.168.10.1
fi

上記の場合、モバイルホットスポットゲートウェイのIPが
192.168.10.1
Edisonの固定IPが192.168.10.2となる

一々IPアドレスを見るのは面倒なので、名前解決 <Edison(マシーン名)の名前>.localになる ping EdisonName.local が届けばいい

今回、EdisonName=myedisonとする

Wifiの手動設定

以下のコマンドから、対話形式での設定が可能

# configure_edison --wifi

今回、モバイルホットスポットSSIDを指定し、接続させた
登録した内容はファイルに格納される。何度もやるとスタックされていく。優先順位は先勝ち。
以下のファイルから手動で変更可能
/etc/wpa_supplicant/wpa_supplicant.conf

Sambaファイルサーバーの設定

opkg install samba

ここ を参考にし、/etc/samba/smb.conf で以下を設定

[global]
   security = share
   hosts allow = 192.168.10.
# 同じネットワーク内の端末へのアクセスを許可
[hoge]
   comment = Comment for Windows
   path = /home/hoge
   guest ok = yes
   writable = yes
   share modes = yes
   force create mode = 0777
   force directory mode = 0777

ユーザーも忘れず作る

useradd hoge
passwd ******

Sambaはデーモン化されているため、特に再起動の必要はない(ps | grep smbで確認)
これで、Windowsから[ネットワークドライブの割り当て]で
\myedison.local\hoge を入力すると、ユーザーが問われるので、
hogeと******を入れてあげればよい

これで、WindowsエクスプローラーからEdisonのファイルが編集できるので、VSCodeなどのエディタを組み合わせることができる

APEC大会

負けました。

探索は走行中の最速経路導出+既知区間斜めも成功(?)。
最短は連続斜めは良かったものの、そのあとの45度ターンが入れずに全部リタイア。

原因は環境に対応できなかったこと、対応しようと調整をし過ぎて、タイヤのグリップ力が一定以下になってしまったことです。
Exiaはタイヤのグリップがなくなると、本来5m/sぐらい出せるのが、4m/sが限界ぐらいにまで落ち込みます。
速度に関してはタイヤ径が小さくなったときに、どこか余計なところが擦れてしまっているのが、直線速度については要因なのかなと。

今回、短期間でメンテも全くできない状況だったので、以下のハードトラブルが発生 * 真横センサーのエポキシ固定がは流れる(比較的マシ) * 左前センサーのエポキシ固定がはがれて、曲がってしまう(斜めの制御に問題。左側の制御を切った。) * 吸引のモーターマウントが破損、振動により色々制御できない状態になる

散々ですね。

Exiaは2017年度大会には出さないと思います。
基板の出来が悪いため、ものすごくハードウェアトラブルを抱えました。
何をしよう。。。

ともかく、やっと2016年度のシーズンが終われました。

大会会場では自分が作ったMMCルーラーやマウス用のブラウザアプリに興味を持ってもらった人がいるので、気が向いたら公開なりなんなりしたいです。

thank you for inviting me.