本記事では、mBotとServo Packを組み合わせて作る「首振りキャットロボ」のプログラムをご紹介します。
首振りキャットロボ(以下、首振りロボ)は、mBotの超音波センサー(目となる部分)の取り付け部にサーボモーターを取り付けることで、超音波センサーを左右に動かすことが可能となるロボットです。首を振る動作を追加することで、mBotの障害物回避能力を向上させ、より賢く動くロボットを制御させることができます。
今回は、首振りロボの障害物検知プログラムを作成しました。Arduinoを使った具体的なプログラムとそのポイントをご紹介します!
首振りロボによる障害物回避のプログラム概要
mBotの障害物回避動作の問題点
mBotの超音波センサーは、前方1方向の距離を測定する仕組みです。そのため、以下のような状況では正確な判断が難しくなります。
- 視野が狭い: 正面以外の障害物を検知できない。
- 進路選択の困難: 右左どちらに回避すれば良いかを判断できない。
- 障害物の誤認識: 角度が悪い物体に反応しないことがある。
首振りロボで改善できること
首振りロボでは、超音波センサーをサーボモーターに取り付けることで、センサーの視野を左右に広げられます。これにより、以下の改善が期待できます。
- 広い視野を確保: 超音波センサーを左右に振ることで、正面だけでなく周囲の状況を把握可能。
- 進路選択が可能: 右左どちらが空いているかを判断し、適切な方向に回避できる。
- 回避精度の向上: 広い範囲を確認してから進むため、誤認識や無駄な動きを減らせる。
首振りロボのプログラム概要
上記のことを踏まえ、今回のプログラムでは、以下のような動作を実現します。
- 走行中に、サーボモーターで超音波センサーを左右に振り、周囲の障害物の有無を確認。
- 障害物検出後、左右(20°,160°)及び正面(90°)の障害物までの距離を計測し、最も障害物への距離が遠い方向へ回避動作を実行。
- 障害物を回避後、直進を再開。
首振りロボによる障害物回避動作の設計
H/W設計
Servo Pack mBot Add-on Packマニュアルを参考に、「首振りキャットロボ」を組み立てます。
S/W設計
今回の仕様を以下に挙げます。
<常時>
- 障害物までの距離を測定する。
- 15cmより遠ければ前進、15cm以下なら回避動作をする。
<走行中>
- 首を左右に振り、各方向の障害物との距離を測定する。
- 障害物検出時は、障害物までの距離が遠い方に、方向転換する
<回避動作>
- 停止する
- 右(20°)、正面(90°)、左(160°)に首を振り、それぞれの障害物までの距離を測定
- 正面の障害物が5cm未満なら後退する
- 左右の障害物距離を比較し、遠い方へ方向転換する
上記の仕様を盛り込んだ処理のフローを以下に示します。
障害物回避動作のプログラム例
上で示したフローチャートをコードに書き起こすと次のようになります。
1.ファイル内変数として以下を宣言します。
MePort port_4(4);
int neck_cnt = 0; /* 首の角度切り替えタイミング用カウンタ */
2.初期化処理にて、サーボのアタッチ処理を追加します。
※loop()関数内のmodeEの初期化処理部分
/* 初期化処理にて */
servo.attach(port_4.pin2());
3.loop()関数からコールするモードEのメイン処理です。
void modeE()/* 首振り猫ロボット */
{
uint8_t dis = 15; /* 正面で検出する距離 */
uint8_t dis_rl = 10; /* 左右で検出する距離 */
uint8_t dis_min = 5; /* 後退する距離 */
uint8_t d = ultr.distanceCm(70);
if(d > dis)
{
Forward();
neck_cnt++;
if(neck_cnt == 0x0FFF){
uint8_t dis_r0 = modeE_read_dis(20); /* 右 */
uint8_t dis_l0 = modeE_read_dis(160); /* 左 */
servo.write(90); /* 首を元に戻す */
if((dis_r0 < dis_rl) || (dis_l0 < dis_rl)){
if(dis_r0 > dis_l0)
{ /* 左側の方が近い場合は右折する */
TurnRight();
delay(500);
}
else
{ /* 右側の方が近い場合は左折する */
TurnLeft();
delay(500);
}
}
neck_cnt = 0;
}
}
else/* 障害物検出時 */
{
/* 一旦停止 */
RunStop();
/* 各角度の距離計測 */
uint8_t dis_r = modeE_read_dis(20); /* 右 */
uint8_t dis_l = modeE_read_dis(160); /* 左 */
uint8_t dis_c = modeE_read_dis(90); /* 中央 */
if(dis_c < dis_min)
{/* 後退検出距離より小さい場合 */
Backward(); /* 後退する */
delay(300);
if(dis_r > dis_l)
{ /* 左側の方が近い場合は右折する */
TurnRight();
delay(500);
}
else
{ /* 右側の方が近い場合は左折する */
TurnLeft();
delay(500);
}
}
else
{/* 後退検出距離より大きい場合 */
if(dis_r > dis_l)
{ /* 左側の方が近い場合は右折する */
TurnRight();
delay(500);
}
else
{ /* 右側の方が近い場合は左折する */
TurnLeft();
delay(500);
}
}
}
}
uint8_t modeE_read_dis(int deg)
{
/* 首振り実行 */
servo.write(deg);
delay(200);
/* 距離計測 */
uint8_t d = ultr.distanceCm(70);
return d;
}
void RunStop()
{
MotorL.run(0);
MotorR.run(0);
}
4.モードEから他のモードに切り替えることを想定する場合は、他モードの初期化処理にて、首を正面に向ける処理と、サーボのデタッチ処理があると良いです。
/* 猫首を正面に向けて、サーボOFFする */
servo.write(90);
delay(300);
servo.detach();
障害物回避動作の実行結果
このプログラムを実行した結果です。
首を振ることで、周囲の状況を確認しながら進むロボットの動きがとても賢く感じられました。
標準mBotの障害物回避に比べて、回避動作がスムーズになり、斜めの壁に対しても上手に検出することができました。
まとめ
首振りロボを使うことで、mBotの障害物回避機能が大幅に向上し、より複雑な環境でも楽しく挑戦できます。
この記事を参考に、ぜひ自分だけの応用プログラムを作ってみてください!
コメント