マイクロマウスで使いやすいモード機能を考える

はじめに

マイクロマウスを作る上で,誰もが”モード機能”を実装すると思います.探索モード,最短モード,調整モード,センサ値表示モードなどなど,いくつもの機能を一つのプログラムで切り替えて動作させることは必ず必要になるでしょう.
しかし,僕の所属するサークルでは全員が思い思いの実装をしており,コードを第三者に見せることがありません.どういうコードを書けばわかりやすく,人為的ミスが発生しにくいのか議論されることがありませんでした.

そこで,この記事では僕のマウス”鵤”で実装しているモード機能を説明しようと思います.この記事をきっかけに,良いアイデアや改善点などが集まることを期待しています.

必要な機能

“鵤”についてはこちらを参照してください.ハーフサイズマウスですが,サイズ的にはクォーターマウスです.なんとこのマウス,スイッチを一つも搭載していません.前作”タニタンv2.0H”にはEVP-BB2A1B000(生産中止品)という極小のタクトスイッチを搭載していましたが,押しにくい上に壊れやすい,しかもクォーターマウスには大きすぎるということで搭載を見送りました.
また,タイヤをコロコロ回してエンコーダで回転を検出し,モードを切り替える人が多くいますが,力の加減を間違えて壊しかねないので使わないことにします.
6軸IMU(ジャイロ+加速度)センサと壁センサのみでモード機能を作っていきます.

モード選択のイメージは,SONY製品のメニューと似たような感じです.
つまり,第一段階でカテゴリを選択し,その中でサブの項目を選べるようにします.図にするとこのような感じです.

モード遷移のイメージ図

しかし,ここで決定したモードの中で更にオプションが必要な場合が存在するので,第三段階としてパラメータを与えられるようにしています.パラメータはジャイロセンサのZ軸を使い,予め設定した定数以下の整数を設定可能にしています.

加速度センサで選択・決定する案もあったのですが,操作の難易度が上がるような気がしているので見送っています.そのうち実装するかも.

選んだモードは,一定時間静止状態にすることで決定しています.具体的には,加速度センサ3軸とジャイロセンサ3軸の値が閾値を超えない状態が一定時間続いた段階で,モード選択を終了します.
つまり,マウスを手に持って振ることでモードを選択し,迷路に置くだけでモードが確定されるという使い方になります.

実装

上記で考えたモード選択機能を実装していきます.新しいモードを追加する時,モードを削除する時にできるだけミスが発生しづらく,お手軽に変更できるようにします.

まずは,モード機能で選択したモードを格納する構造体を定義しています.

1
2
3
4
5
struct StructMode {
uint8_t prime;
uint8_t sub;
uint8_t number;
};

次に,第一段階のモードを一覧にした列挙型を定義します.

1
2
3
4
5
6
7
8
enum class MODE_PRIME : uint8_t {
TURNADJUST = 0x0,
SENSORLOG,
RUNLOG,
EXPR,
SHRT,
LAST // コレより下に定義しない
};

列挙型の一番最後に MODE_PRIME::LAST を定義することで,要素数を取得できるようにしています.

で,第二段階のモードはコレと同じように作っていきます.

1
2
3
4
5
6
7
8
9
10
11
enum class MODE_EXPR : uint8_t {
NEW,
LOAD,
LAST // コレより下に定義しない
};
enum class MODE_SHRT : uint8_t {
SMALL,
BIG,
DIAGO,
LAST // コレより下に定義しない
};

各モードには,初期状態で選択されていて欲しい項目があると思うので,設定しておきます.

1
2
3
const static MODE_PRIME first_mode_prime = MODE_PRIME::EXPR;
const static MODE_EXPR first_mode_expr = MODE_EXPR::NEW;
const static MODE_SHRT first_mode_shrt = MODE_SHRT::SMALL;

さて,この方法の一番クソなところですが,各モードの要素数(最大値)を返す関数を作っておきます.

1
2
3
4
5
6
7
8
9
10
11
12
uint8_t ModeSelect::getModeSubLast(uint8_t prime){
switch(static_cast<MODE_PRIME>(prime)){
case MODE_PRIME::TURNADJUST:
return static_cast<uint8_t>(MODE_TURNADJUST::LAST);
break;
case MODE_PRIME::EXPR:
return static_cast<uint8_t>(MODE_EXPR::LAST);
break;
case MODE_PRIME::SHRT:
return static_cast<uint8_t>(MODE_SHRT::LAST);
break;
}

……という感じで,第二段階のモードを増やす度に関数を修正する必要が出てきます.絶対忘れるのでなんとかしたい

後は簡単,モード選択関数 StructMode ModeSelect::select(); が戻り値を返せばいいだけです.main関数の中では二重の switch 文を使ってモードの識別を行っています.

使ってみた感想

それぞれのモードにそれっぽい名前を付けているので,何をするモードなのか一瞬で識別できるのは嬉しいです.第二段階のモードを増減させるだけならお手軽にできるので,いいアイデアだと思いました.
ただ,第一段階のモードの増減,今は無いですが第n段階を増やす際(n>3)が少し大変そうなので,改善点は多いと思います.

ジャイロセンサと加速度センサを組み合わせてモード選択をする操作性については,慣れれば簡単ということに尽きると思います.ジャイロの2軸までなら操作ミスはほぼ無く,3軸なら閾値と待ち時間の調整を適切にすれば問題なく使えるレベルだと思います.

まとめ

物理スイッチはなくてもなんとかなるという知見を得られた.
他の人のモード機能についても知りたいので教えてください.