お久しぶりです。新人のYoです。
前回は「拡張左手法」の実装についてでした。
今回は前回の探索アルゴリズムを使用した大会の結果について書いていこうと思います。
中部地区大会結果
まずは中部地区大会に出場してきました。探索アルゴリズム実装後最初の大会です。
試走会時点では旋回に時間のかかる超信地旋回を使っていたこともあり、
5分以内の完走は見込めないと予想していました。
本番の迷路はまさかの左手法で完走できる稀有な迷路でした。
迷路は完走したものの探索アルゴリズムは全く活かせなかった上、
最短経路走行はできなかったという結果に終わりました。
振り返り①
大会に出場したことにより予想外のバグが存在していることが判明しました。
バグのありそうな場所は機体の動きから見当がついていたので該当箇所を調べました。
案の定バグの原因は探索アルゴリズムとは別に追加していた「探索アルゴリズムを一時的に
無視して目の前のゴール区画に突入する」という割り込み条件でした。
基本的な動作が左手法遵守である関係上ゴール区画の真横を素通りすることが多々あったために
追加した割り込み条件が裏目に出ました。
マップの書き込みも走行完了時にのみ書き込みをしているため、途中で走行を停止し
リセットしてしまうとマップ情報がなく最短経路が算出されません。
加えて、拡張左手法の探索時間の長さを考慮すると超信地旋回のままでは時間が
圧倒的に足りないということも確信しました。
大会結果を踏まえて、次に出場する東日本地区大会までにやるべき内容は
探索時間を短縮させるためのスラローム走行実装に決めました。
余談
バッテリ監視
ESP32版Pi:Co Classic3のサンプルプログラム内にはバッテリ監視のプログラムはあるものの、
Pi:Co Classic3のようなバッテリ保護のための強制停止が存在しないためバッテリを限界以上に
使い過ぎてしまいました。
同じ過ちをもう何度か繰り返しそうだったので強制停止のプログラムを実装しました。
g_battery_value = getBatteryVolt(); if (((g_battery_value - BATT_MIN) * 10 / (BATT_MAX - BATT_MIN)) > bled_cnt) { setBLED(1); } else { setBLED(2); } if(g_battery_value < BATT_MIN){ //バッテリ監視 追加 disableMotor(); //強制停止 enableBuzzer(DEC_FREQ); //ブザー delay(3000); setLED(15); } break;
やっていることはRX版と変わらないので書き方が少し変わっただけになります。
既知と未知の判定
続いて社内で16×16迷路を使用した時に発覚したバグです。
探索中に現在地の既知未知判定をする際に壁の情報を使用していたため突入したことのない
区画でも周囲にある4枚の壁全てを認識していると既知判定にしてしまっていたので、
想定していた経路とは異なる経路で探索していました。
このバグは以下のように既知と未知の判定対象を壁から区画に直すことでなくなりました。
MapManager::MapManager() { for (int i = 0; i < MAZESIZE_X; i++) { for (int j = 0; j < MAZESIZE_Y; j++) { wall[i][j].north = wall[i][j].east = wall[i][j].south = wall[i][j].west = _UNKNOWN; //迷路の全体がわからない事を設定する map_known[i][j] =0; // すべての区画に行ったことない } } map_known[0][0] =1; //スタートは必ず通る
まずmap_manager.inoファイルでmap_knownの初期化を行い、未知区画を0とします。
// 今の方角と位置を取得 now_dir = g_map_control.getMydir(); now_pos[0] = g_map_control.getMyPosX(); now_pos[1] = g_map_control.getMyPosY(); while (now_pos[0] != 0 || now_pos[1] != 0) { // 現在地が0,0区画でない間ループ if(start_flag == true){ start_flag = false; }else if(start_flag == false && temp_next_dir == rear){ accelerate(HALF_SECTION, SEARCH_SPEED); } // 今の方角と位置を取得 now_dir = g_map_control.getMydir(); now_pos[0] = g_map_control.getMyPosX(); now_pos[1] = g_map_control.getMyPosY(); g_map_control.map_known[now_pos[0]][now_pos[1]]++; // 現在地の通った回数を1回増加
区画を通ったらその区画をカウントアップして既知区画とします。
bool check_one_dir_unknown_section(t_direction check_dir){ // 調べたい方向の区画が未探索かどうかのフラグ bool unknown_section_flag = false; // 未探索を確認する座標 unsigned char check_pos[2] = {0,0}; // 調べたい方角から調べるべき座標を得る get_adjoin_section(check_dir,check_pos); //for(int wall_dir=0; wall_dir<4; wall_dir++){ //if(g_map_control.getWallData(check_pos[0],check_pos[1],(t_direction_glob)wall_dir) == _UNKNOWN){ // _UNKONWNの壁が一つでもあれば次の区画を未探索とする //unknown_section_flag = true; //} } if(g_map_control.map_known[check_pos[0]][check_pos[1]] == 0)unknown_section_flag = true; return unknown_section_flag; }
558行目ではunknown_section_flagというフラグを立てて未知区画の判定をします。
552から556行目のfor文を消して558行目のif文を挿入すると区画の判定対象が
壁から区画へと切り替わります。
void add_virtualwall(unsigned char * now_pos){ if(g_sen_l.is_wall == false){ //左壁ナシ // 左側が未探索かどうか if(check_one_dir_unknown_section(left) == true){ //左未探索 setLED(1); return; } else{ //左探索済 if(g_sen_fr.is_wall == false){ //前壁ナシ if(check_one_dir_unknown_section(front) == true){ //前未探索 g_map_control.setVWallData(now_pos[0],now_pos[1],local_to_glob_dir(left)); //左に仮想壁を建てる setLED(2); return; } // 以下省略
既知と未知の判定を使用した仮想壁を建てる条件の詳細は前回の記事に書いてあります。
東日本地区大会結果
スラローム走行を実装して東日本地区大会に出場です。
迷路の全体をぱっと見した地点で結構嫌な箇所に仮想壁が建つのが予測できました。
結果は大方直前の予測通り迷路内を延々と振り回され続け3分が過ぎたあたりで
壁に衝突して失敗に終わりました。
振り返り②
約3分間に渡って探索を続けられていたのでスラローム走行の実装は概ね成功
と言っても差し支えないと思いますが、細かな誤差が溜まり続けていたことが判明しました。
今回はゴール区画に到達したら一旦マップを書き込むプログラムを実装していましたが、
不発に終わりました。
全日本マイクロマウス大会で完走できるようにもっと走行を安定させるための修正が
必要であることはわかりました。
次回予告
次回は今回実装していたスラローム走行の詳細について書いていこうと思います。