東日本大会
大会結果
- クラシック2位
- サーキット1位
分析(クラシック)
過去の中部地区大会の迷路をリバイバル
- 金沢草の根に比べると、比較的難易度は抑えめ
探索に失敗。スラローム時の直進区画の補正プログラムに特定の条件で打ち切るバグがあった。
当日中に部屋で同じような迷路を再現、特定に至る。
前日の試走会などがあれば十分吸収できた。
東日本大会は前日の試走会がある年とない年がある。 現地確認は大事だが、家でどれだけ担保できるかも重要である。最短は全ターンのパラメータの挙動を確認。
- フェイルセーフがかかりやすい。
機体の損傷を回避するため、必要な措置と判断
分析(サーキット)
- 過去のU宮さんの記録を上回ったことから、日本記録更新した?
- 速さのために、中心を走ることをあきらめたことで、ターン時の曲率半径を拡大。
- ターン速度の向上、減速区画の減に成功
- 壁ギリギリまで近づくことが重要
次回までにやること
- 探索の安定性向上。(対処済み)
- 制御系の変更
- やや追従が遅い場面が見受けられたた。
- ターンの再確認・再調整
- バッテリーを新しいのを作っておく(完了済み)
制御について
以下の変更を加えてみた。
Before
- 並進方向の制御量=速度に対するPID+距離に対するI
- 回転方向の制御量=角速度に対するPID+角度に対するI
After
- 並進方向の制御量(今のところ変更なし)角度と同じような構成にすべきか確認中
- 回転方向の制御量=角速度に対するPD+角度に対するPI
目標角度の入力の対応ができるように修正
金沢草の根大会
まえがき
4年ぶりに参加した金沢草の根大会
4年前と違い、場所が鶴来と金沢駅から遠くなってしまったが そこには、新たなる精神と時の部屋が出来ていた。
広大な自然の中にあるきれいな校舎
廃人たちが集うこの北陸の大地で起きた事件とは・・・
環境
迷路は昨年のU氏のブログにも合った通り、他の大会と路面とだいぶ違う印象。ざらつきが目立ち、タイヤかなりが削れた印象。 埃が少ない気がしました。
照明がLEDなのか、一部の区画で挙動がおかしくなる現象に苛まれる機体が少しいました。
準備
新作ExiaAlterの初陣に、初の大会最終出走と判明。
だが、探索すらできないことが発覚
2か月間ほとんど調整してないせいか、拗ねてしまった模様。
探索時に利用する補正が諸々狂っていたらしい。
結局、夜中3時までかかってしまったが、例の調整システムのおかげで、何とか調整をやり切れた。
人事は尽くした
大会結果
優勝しました。本来Busterすべき後輩たちがいなかったけれど、昨年度個別に指定していた黒椿をバスターしたのと、Dの陰謀が見え隠れしていたが、それを阻止。
詳細
- 1走目:1m/s探索は難なく成功(振り回されていたため、改良の必要あり)
- 2走目:オートスタート⇒失敗
- 3走目:最低パラメータ⇒成功(暫定3位)
- 4走目:たぶん2走目と同じパラメータ⇒失敗
- 5走目:複数ある上位のパラメータの中で最低のパラメータ⇒優勝
やはり、最短の安定性が課題として残る。
壁切れの読み逃し時の安全装置が働いてくれてたのは非常に助かったが、全日本に向けて不安が残ってしまった。
オートスタートの失敗は海外大会にも影響が出る。早急に対処せねば。
分析
迷路は10年前の中部地区大会のリバイバルで難易度が高め(シーズン初期でこの難易度を要求とは・・・。これは後半戦はもっとヤバい迷路が来そう)
完走率は16/28、最短成功率が9?/16。
比較のしようがないが、全日本の地区予選という位置づけなので、これぐらいの難易度ってことなのだろう 探索のゴールは難しくないが、欲張ろうとすると大変になる。お手本のような迷路ですね。
実際、最短に成功しているのが、昨年の全日本決勝に出てる人ばかり。やはり経験値の差が出てる気がします。
しかし、新作を出してきてる人が、ハーフ合わせても少ない。
9月に合わせているのだろうけど、それだと学生大会に間に合わないだろう。
終わりに
Twitterでも何度も言ってますが、今回I,Sysの組織力を感じました。 てきぱきとした運営、見ていて感心しました。
ペヤングGigamaxは好評でしたが、もう1つか2つ買ってくるべきでしたね。
副賞のヒートガンですが、おかげで旧作のマウスからマイコンを引っぺがすことに成功しました。 これで、BGAの実装もできたらな・・・
調整項目たち
調整がつらい
アドベントカレンダーでも書いた気がするのだが、何がどの規模で辛いかを伝えてなかったので、紹介したい。
調整項目の種類
項目は大きく4種類
- フィードフォワード制御で使用する物理的で自明、または計算により導出可能な数値
- スラロームなどのターンパラメータの計算可能なモノについてもここ
- フィードフォワード制御で使用する、実測により導く数値
- 計測が難しい摩擦、慣性モーメントはここ
- フィードバック制御で使用する、シミュレータなどを利用して当てはめ可能な数値
- matlab大好き勢や最適制御大好き勢に任せます。知らない。
- フィードバック制御で使用する、実測により調整する数値
おおよそ、新しい機体が完成した後に、数字の順番でパラメータが決定されると思います。
1. FFが自明
例えばモーターなど、カタログスペックを参考に、
- トルク定数
- 回転定数
- 逆機電量定数
- 電機的時定数/機械的時定数
- 抵抗値
- 質量
- ギア比
などの数値を使用し、出力トルクを導出、その後、電圧へと変換しそれをDutyとして扱うと思います。
2. FFが自明でない
1とは異なり、
- 慣性モーメント
- タイヤのグリップ力
などは、個人での計測は難しいため、繰り返しによる調整が必要になる。 例えば、FBの項をなしにした状態での動きを確認した場合の追従具合を見る。 FFが適切ならば、原理上応答が遅くなるFBの項はゲインを低く設定できる。これはロバスト性の向上にもつながる。
類似として、光センサーから距離を導出際に用いる、パラメータの決定もこれにあたる。
3.FBが自明
知らん。次。
4. FBが自明でない
一番つらい。
具体例として、
- モーターのゲイン(PID)
- ジャイロの値から角速度へのゲイン(左右それぞれ)
- 角速度追従のゲイン(PID)
- 光センサーを使った制御のゲイン(PID)
- 光センサーを使った制御のゲイン(斜め走行時)(PID)
- 区画の真ん中を走るために、
- 左右の中央時(通常時+斜め時)
- 壁切れ制御の閾値(有無の判定含め)
- 探索時の壁の有無の判別する閾値
- 壁切れ補正を時の直進距離
- 前壁制御の要求値・閾値
- スラローム時の前距離・後ろ距離
- 左右・斜め進入時で1つのターンで最大8項目×ターンの数だけ
調整に必要なもの
- 時間的余裕
- パラメータの管理能力
- 壊れない精神力
最後に
文章にしてしまうとなんて事の無い、と思うじゃないですか。。。調整パラメータが100を超えるのは意外と簡単です。 パラメータ管理を正しく行うことで、次回作以降の調整スピードにも大きく影響を及ぼしますので、管理方法も考えましょう。
また、調整に際してはバイナリへの書き込み回数を減らせるようにシステムを構築するとよいです。 例えば、「シリアル通信から、フラッシュ領域への書き込み」など。コレだけで、ビルド+書き込みの時間を大幅に削減可能です。
【俺式】ジャイロセンサの調整術
はじめに
タイトルにある【俺式】の通り、経験則に基づいたパラメータの決め方でしかありません。
実装は各自の責任でお願いします。
また、調整するパラメータのおおよその値(桁数的な意味でのスケール)を見つけていくことになります。
これができるとマイクロマウスにおける宴会芸
ができるようになります。
ジャイロのゲインの調整をする項目について
ジャイロを調整するにあたって、調整する項目は何なのかですよね?
調整する順を説明します。
- センサー値の扱い
- 取得したセンサー値を角速度に変換する定数(1種類)
- 現在の理想の角速度(=理装置)と、センサーから取得した角速度のずれから、理想値に追従するために使用する定数(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が決まってるとよいです。
以下の順で決めます。
- Pゲイン
- Iゲイン
調整においては1つプログラムの実装が必要です。
具体的には、マウス界隈で言われる宴会芸
のプログラムを実装してください。
マウスに速度0、角速度0を維持する制御をすればよいです。
Pゲインの決め方
紙の上にマウスを置き、宴会芸を実行します。マウスがその場で回転するように紙を動かします。
紙を速く動かしている間だけマウスが動いていればよいです。
マウスの動きがバタバタし始めたり、音があり始めると、値が大きする証拠なので、その半分ぐらいにするとよいです(経験則)
Iゲインの決め方
Pゲインの決め方同様、宴会芸で紙を動かしていきます。 Pゲインとは違い、ゆっくり動かして、マウスが応答してくれればよいです。 徐々にIゲインを上げていき、プルプルし始めたら、ゲインが高いといえるので、その半分ぐらいにするとよいでしょう(経験則)
終わりに
Dゲインについてはスラロームなどログを取って調整することになります。P,Iについても同様にログから微調整したほうが良いです。 あまりゲインを上げ過ぎるとロバスト性が悪くなることもありますのでご注意を。
本来なら動画ベースでまとめられるとよいのですが、深夜テンションでやっつけ仕事で書いたので、調整順と調整用のアプリぐらい参考になればと思います。
新作"Alter"完成
新作紹介
機体名は、ExiaAlter。 Exia改として作成したが、見た目とかはQuantaそのもの。
名前の由来は勿論、00から。 2つ前の機体、Exiaの名前が呼びやすいということから、名前を戻した。
Quantaからの変更点
実物 設計時
前作との違いは、
- センサーの数を6つから5つに変更
- エンコーダ付きモーターに変更
- ギア比を小さくした(63:17 ⇒67:21)
- 吸引モーターに使っているモーター(DCX10-S)を4.5V版から3V版に変更
- 電源から5Vを取得するときに使うICをLDOからDCDCに変更(効率化のため)
- タイヤを低扁平率に変更(4点接地できてないのか、すり減り方に偏りが・・・)
- バッテリーを160mah⇒200mahに変更(予定)
参考にしてはいけない設計
いくつか自分でリスクを承知に、仕様書無視をやった。やらかしているのは2か所。
-
これにより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をアマゾンで購入。 もっと安いところがあるかもしれないが、それはおいといて、いろいろ紹介する。
機能
- 対応バッテリーの種類
- 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系わからない人には初見バイバイです。さようなら。
パラメータ調整の手間を軽減しよう。
もう、ただただ調整がツライ。 いうことで。ツライ要因を上げる。
- 理論がいまいち
- 調整パラメータはすべてハードコーディングされているため都度、ビルド⇒書き込みが発生していた。
前者は皆努力しても無駄なので、後者を専用システム構築をして改善する。
構成
図にはデータフラッシュとか書いてあるので、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つの記事に書いたヤツがいるだろうか・・・。
画面イメージ
途中まで作ってみました。左の写真には各センサー値を表示、値を見ながら、閾値を調整するという形式にしてみました。
今後の課題。
- パラメータの調整だけではなく、テストシナリオも同様にバイナリ書き込みではなく、フラッシュからの読み出しから実行したい。
- json-cを使う。
- 大量のデータを一度に更新できるようにする。