2025年5月30日金曜日

2025年5月報

1.5月授業まとめ
2.5月の課題
3.4月の解答
4.6月の授業予告
5.今後の授業スケジュール
6.お知らせ


1. 5月授業まとめ


1.0 <スタートアップ(全コース)>


 割愛します。


1.1 <プレプライマリーコース『ロビット』>


 割愛します。

1.2 <プライマリーコース『ウォーカータクシー』>


 割愛します。

1.3 <ベーシックコース『ロボクリーン』>


 おそうじロボットです。
90°ずつ向きをずらした9本の赤い“ブラシ”を高速回転させる様がにくいですね!
吸引(きゅういん)こそしませんが、あのロボット掃除(そうじ)機『ルンバ』を彷彿(ほうふつ)とさせます。

 掃除機として、大事な工夫点があります。
ブラシは高速回転させたいが、本体の走行スピードは…?
疾走(しっそう)されては困るので、タイヤはゆっくり回さなくてはなりませんね。
この相反(あいはん)する要求をどうやって1つのモーターで実現するかです。

部品配置は、モータータイヤブラシ の順に並んでいます。
回転速度は、速い遅い速い です。
つまり、モーターを一旦(いったん)減速(げんそく)してタイヤを回した後、増速(ぞうそく)してブラシに伝える構成が必要であり、それが筐体(きょうたい)の側面に並んだ大小6枚のギアの役割なのです。

3連のピニオンギア(歯数8)がタイヤと同軸のギアL(歯数40)を回すので、1/5に減速されます。
その後、ギアM(歯数24)を経由してブラシと同軸のピニオンギア(歯数8)を回します。
途中のギアMは気にせず(*1)、ギアLピニオンギア と考えて、5倍の増速になります。
つまり、1/5×5=1 で、モーターと同じ回転数(等速)に戻してブラシを回しているのですね。

《ギアの回る速さについて勉強したい人は、下記を読んでください》
 https://robocobo.sakura.ne.jp/blog/bas/bas-1711.pdf

 2日目最後の競技は、ガチンコおそうじ対決!
狙(ねら)いを定めたロボットの前に、小さくカットした紙やスポンジのゴミを散りばめ、「よーいドン!」でゴミの争奪(そうだつ)戦(!?)です。
「そこまで!」の合図までに内部に取り込めたゴミの数を競います。

皆さん、対戦になると燃えますね~。
まして、改造の良し悪しがゴミの数に表れますから、時間いっぱい改造に勤(いそ)しむ人もいましたが、かえって本番で成績が振(ふ)るわず、悔(くや)しい思いをしていました。

 このロボットで苦心(くしん)するのは、赤いブラシが(当然に)固いプラスチック部品であり、柔軟性(じゅうなんせい)がないので、筐体との隙間(すきま)にゴミが挟(はさ)まってブラシが止まりやすいことです。
高速回転(増速)させる分、回転力(トルク)が弱いので、たやすく停止します。

この問題を解決しようと、過去にすばらしいアイデアが発明されました。
ブラシの毛(クロスジョイント)が固いのは仕方がないので、シャフトに“半固定”し、毛一本一本の回転力を弱めるアイデアです(*2)。


1) クロスジョイントをシャフト(十字形断面)の周りに固定するのではなく、自由にぶら下がるよう、十字穴ではなく丸穴に通す
2) クロスジョイント同士が密着するよう、ブッシュやグロメットで隙間を埋(う)める(*3)

これには、
●詰(つ)まらない限り、全ての毛がシャフトと一緒に回転する
●詰(つ)まった毛だけ止まり、ブラシ全体の回転を止めない
という設計思想がしっかり体現されています。

肝心(かんじん)のゴミ取り性能はともかく、何か問題に直面した時、限られた部材(キット)の中でも「何か解決方法があるという好例です。

 他には、毛として、回転シャフトに輪ゴム結束バンドをくくり付けてはどうでしょう。
紙の短冊(たんざく)を巻き付けた人もいました(*4)。


内部の汚れが問題になるほどゴミが取れてしまうかもしれませんよ。


*1 ギアMを気にして計算しても、40/24×24/8=40/8=5 と同じです。
 間にギアMを挟んでいる理由は、回転方向と位置を調整するためです。

*2 スリップトルク(滑り摩擦力を利用した回転力伝達)や、トルクリミッター(回転力制限装置)呼ばれる機械要素です。

*3 ブッシュで埋めた方がクロスジョイント同士の摩擦(まさつ)が強すぎず、詰まりにくいです。

*4 アドバンスコース掃除ロボット『ロンボ』で同じ工作をします。


1.4 <ミドルコース『ロボゲーター』>


 第8回アイデアコンテスト全国大会(2018年)発表作品(当時小2)がベースの、ワニ型ロボットです。

 1日目のロボットは、後ろ足をシャフトドライブ方式で駆動する特徴はあるものの、4本足を同期動作させる上で特にメリットはありません(*1)。

2日目で下半身を“くねらせる”改造に意味がありました。

 下半身をくねらせることができるのは、上半身の背中で回るロッド3アナに挿(さ)したプレート(ユニバーサルジョイントを覆うカバー)が下半身を押したり引いたりするてこクランク機構」によります。

シャフトドライブは、1本の単なるシャフトから、「ユニバーサルジョイント」を介して伝達するようにしました。
ユニバーサルジョイントの採用は、2024年3月『ステアリングカー』に次ぐ2例目です。

ユニバーサルジョイントにより、下半身をくねらせて、上半身のモーターとは回転軸(シャフト)の方向が(一直線から)ずれても回転を伝えられるようになります。

これが無ければ乗り物が作れない程の大発明です(*2)。

 さらに、尻尾(しっぽ)の部分を“しならせる”工夫も施(ほどこ)しました。

後ろ足のギアボックスの背中でもロッド3アナの回転動力を取り出して、
尻尾を構成する4段(ビームの塊×3段 + Tロッド)のリンク(*3)とも「てこクランク機構」により折り曲げて、
尻尾の先ほど(胴体から見れば)曲がる角度を深くしています。

 このような多数の「てこクランク機構」によって胴体や尻尾をしならせる様子は、ロッド(3アナや7アナ)による伸縮作用を筋肉で、ペグを関節、その他のリンクを骨で考えれば、実物のワニに似て見えます。

口やデスロールの他に、ワニの攻撃技の一つが尻尾によるアタックですから、ワニロボットは、モーター1個の力で全体を動かすくらいが安心ですね。

 音センサーをワニの聴覚にして、そばに部品を散りばめれば、
ミドルコース恒例の「音に反応して動き出す」だけのプログラムで「ワニの守る杯から宝物を奪う」パニックゲームのできあがり!

音センサーの感度で難しさも調整でき、家族ゲームにオススメですよ!


*1 下半身を分離して収納したり、頭部を持ち上げて下半身を“デスロール”っぽく回転させて遊べることくらいでしょうか。

*2 さらに興味が湧いたら、インターネットで調べてみましょう。問題点もあります。
 自在継手 https://ja.wikipedia.org/wiki/%E8%87%AA%E5%9C%A8%E7%B6%99%E6%89%8B

*3 リンク(節)とは、ジョイント(関節)ではない、変形しない棒や板のことです。
 ここでは、ジョイントはペグ類であり、リンクはその他のパーツの塊を指します。


1.5 <アドバンスコース『ブランカー②』>


 ≪執筆中≫


1.6 <プロ1年目コース『オムニホイールロボット②』>


 1ヶ月目に製作したオムニホイール(Omnidirectional Wheel;全方向車輪)ロボットの動きの原理を理解し、思い通りの動きをプログラム上で指示できるようになるまでの2ヶ月目の授業です。

 1日目は、改めてオムニホイール(車輪)の仕組みを考察し、3つの車輪を任意の速さ・向きに回したときの進行方向が“力(ベクトル)の合成”によって求まることを学びました。

ここでのベクトルは、車輪の回転の向き速さを、それぞれ矢印の向き長さで(紙面上に)表したものです。

2つのベクトルの合成は、まず作用点(ベクトルの始点)を重ねて、それが平行四辺形の2辺を形成するようにもう2辺を描き足し、作用点から発した対角線(=合力)の向き長さで表します。

3つのベクトルを合成するには、任意の2つを合成した後、その合力と残りのベクトルをさらに合成します。

様々な練習問題を製図して解きつつ、プログラムの数値に反映して動きを確かめ、どんな3輪の回転の組合せでも、ロボットの進行方向を求めることができるようになりました。

 2日目に、ロボットの回転する動きを考察します。
1日目では、進行方向を割り出すことができましたが、ロボットが向きを変えずに移動(=並進運動)する場合は、これで十分でした。

しかし、実際は互いに離れた3輪による作用点(ベクトルの始点)を一点(例えば、ロボットの中心)に集めて合成するために、本体が回転しようとする力(モーメント)を扱うことができませんでした。

例えば、3輪とも同じ速さで時計回りに回転させる場合、ベクトルの合成結果は長さゼロの“点”となって、ロボットが移動しないことを言い当てますが、実際の動きは、移動こそしないものの、その場でぐるぐる回り続けます。
この回る動き(=回転運動)を予測できるようにします。

 レンチやスパナを想い起こしてください。
支点回転中心)のある物体に対し、支点から離れた場所作用点)に、(支点に向かう向きとは異なる)力を加えると、物体は支点の周りで回転します。
この物体を回そうとする作用力を“モーメント”といいます。

支点と作用点の間の距離を単に“長さ”と呼ぶと、

 [モーメント]=[長さ]×[力の大きさ]

という関係があり、長いほど回す作用が強くなるという、てこの原理を表しています。

オムニホイールは、3輪とも円周上(円形ボードの縁)に取り付けられているため、円形ボードの中心を支点としてロボットの回転を考えたとき、各ホイールまでの長さ]が全て等しいので、オムニホイールロボットの[モーメント]を推し量る上では、[力の大きさ](今回はホイールの回転スピード)だけ考慮すればよいことになります。

 難しく述べましたが、簡単には、時計回り反時計回りの回転スピードを差し引きして、ゼロなら回転せず、ゼロ以外ならその値の分だけ優勢な方向に回転する、と言えるのです。

オムニホイールロボットがカーブの軌跡を描いて移動するとき、並進運動だけで実現すれば、飛来するUFOのように向きを変えず、並進運動+回転運動を組み合わせれば、自動車のように自然に曲がることもできるのです。

3つのオムニホイールが生み出す、どんな複雑なロボットの動きも、
ベクトルの合成”で求まる並進運動と、
モーメントの合成”で求まる回転運動とに分解して説明できるのです。

 最終の3ヶ月目では、このロボットに“触覚”と“頭脳”を植え付け、ロボカップに通ずる自律型ロボットに仕上げます。


1.7 <プロ2年目コース『不思議アイテムII②』>


 外付けの電子回路をプログラミングで操る春タームの2ヶ月目です。

 前月は、タクトスイッチというデジタル(ON/OFF)入力装置をマイコンに繋いで、プログラム中 digitalRead() 関数で読み取りましたが、
我々はアナログ量に満ちた自然界に住んでいます。

光の強さ、色の濃さ、音の大きさ、痛みや匂いなど、およそ0/1だけで語られては困る事象と相互干渉しながら暮らしています。
それだけに、コンピュータやロボットに対しても、アナログ的な入力や出力を期待できないと満足がいきません。

とは言え、せっかく微量の電力だけで高速処理してくれる小さなマイコンチップに、人間の都合で味覚や温度、力加減まで、何でも分かる感覚を付け足していくのは酷で、蛇足にも思えます。
それでも、列挙した感覚をもつマシンはめいめい現存しています。専用マイコンなのでしょうか?

そうではありませんね。汎用(はんよう)の演算処理装置(CPU)に、必要なセンサーを繋いでデータを送受信する方法が一般的な解です。

 流すデータ(数値)を構成する物理量として、“電圧値”が最も多用されます。
中身が電圧型論理回路であるマイコンにとって、扱い易く、省エネにできるからです。

マイコンには、電圧のデジタル値(0/1)だけでなく、アナログ値(0~1)も入出力できるピンやモード設定が用意されています(*1)。
だから、光でも音でも熱でも力でも、その量に応じて電圧値に変えられるセンサーなら、直ぐに繋げます
繋いだら、analogRead() 関数で読み取るだけです。

 1日目に、ボリュームセンサー(可変抵抗器)を繋ぎました。
と言っても、その名の通り、ツマミを回して抵抗値を変えられる部品ですから、電圧値を読み取るマイコンの入力ピンにそのまま繋いでも、何もセンシングできません。
耳の穴に抵抗器を入れて音を聞こうとするようなものです。

抵抗値は、電流を流すことで、簡単に電圧値に変換できます(*2)。
電圧・電流・抵抗の間に、オームの法則と呼ぶ次の関係があります(*3)。
水圧・水流・蛇口の絞りの関係になぞらえると理解し易いです。


・電流(I) = 電圧(V) ÷ 抵抗(R)
・電圧(V) = 抵抗(R) × 電流(I) ←電流を流して抵抗を電圧に変換する式
・抵抗(R) = 電圧(V) ÷ 電流(I)

 ボリュームから可変電圧を作り出す原理は、抵抗分圧といって、詳細は割愛しますが、以下に簡単に説明します。

1) 10kΩタイプの3端子ボリュームの場合、両端子間にGND(0V)と5Vを繋ぐことで、I=5V÷10kΩ=0.5mAの電流がずっと流れます。

2) ツマミは、GND側から5V側まで伸びる10kΩ抵抗の任意の位置に接点をスライドさせ、接点は真ん中の端子に繋がっています。

3) 接点がGND側から20%の位置にある場合、GND⇔接点間の2kΩに0.5mA流れていることにより、真ん中の端子には2kΩ×0.5mA=1V(5Vの20%)の電圧が表れます。

4) 真ん中の端子電圧をマイコンで読み取ることで、ツマミを回した量が判定できます。

5) 両端子間の抵抗値が何でも同じですが、流し続ける電流量(ノイズ耐性)で選びます。


 ボリュームセンサーに代わり、CdS光センサーは、明るさに反応する可変抵抗器です。
例えば、暗闇で10kΩ(暗抵抗)、日光で100Ω(明抵抗)程度まで変化します。

これを固定抵抗器と直列に接続し、両端に電圧を掛ける(電流を流す)ことで、中間の接続点に明るさに応じて抵抗分圧された電圧値が表れます
つまり、マイコンで明るさを検知し、メーター表示したり、照明を自動点灯したり、画面の明るさを制御したりできるようになります(*4)。


または、圧電スピーカを繋ぎ、analogRead() 値を tone() 関数に渡すという、たった2行だけで、かざした手の距離(影の暗さ)に応じて音の高低が変わるテルミンのような楽器になりました(*5)。

 2日目は、4桁の7セグメントLEDによる表示方法を学びました(*6)。

下記の表示関数が登場しました。
・setDigit() … 指定した桁に数字およびドット(小数点)を表示
・setLed()  … 指定した桁の指定した棒またはドットを点灯/消灯
・setDec()  … 指定した数(-999~9999)を一発で表示


特に、setLed() を使えば、数字だけでなく、8(7つの棒+ドット)×4桁=32個のLEDによる任意のパターンやアニメーションを表示できるということです。
そもそも、数字の形を指示せずに表示してくれる setDigit()setDec() も、中身では setLed() 相当のプログラムで光らせる棒を1本ずつ指定しているだけです。

意味のある7セグ数字を見ていると、いかにもマイコンらしい表明手段のように感じますが、こちらの幻想に過ぎません。
マイコンにとって、数字の形や意味など知ったことではないのです。

 さて、32個のLEDを使って、誰かが用意してくれた数字の表示関数を使わずに、自分で任意のパターンやアニメーションを表示するには、それなりのデータ量をプログラムに記さなければなりません。

棒を3本光らせるのに、

int a = 4;
int b = 6;
int c = 1;
setLed(0, 0, a, HIGH);
setLed(0, 0, b, HIGH);
setLed(0, 0, c, HIGH);

よりも、

int a1 = 4;
int a2 = 6;
int a3 = 1;
setLed(0, 0, a1, HIGH);
setLed(0, 0, a2, HIGH);
setLed(0, 0, a3, HIGH);

の方が、変数の命名に悩まずに済みそうですが、配列for文による繰り返しを使って、

int a[] = {4, 6, 1};
    // a[0]=4, a[1]=6, a[2]=1 と代入される

for (int i=0; i<3; i++) {
  setLed(0, 0, a[i], HIGH);
  // a[0], a[1], a[2] に順次アクセス
}

と書く方がスマートで、データが増えても破綻(はたん)し難そうです。
for文による配列変数への順次アクセスは、プログラミングの強力な武器です。
しっかりマスターしましょう。


*1 先月も注釈したように、マイコンには真のアナログ信号は扱えないので、
 PWM制御(1年目1月)により擬似的にアナログ出力電圧(256階調など)を生成したり、
 真のアナログ入力電圧をデジタル値(1024階調など)に変換したりするコンバータ回路が搭載されています。

*2 というより、電流をどれだけ邪魔するかの指標が抵抗なので、電流を流してみることで抵抗値が分かります。

*3 速さ・時間・距離の関係と同様、1つの式から他の2つの式を導出できます。
 語呂合わせ「はじき」と同じく、“覚える”ものではなく、感覚で捉えるべきものですが、一先ず V=RI と発音して記憶します。

*4 暗い部屋で眩(まぶ)しくないよう、LED時計やテレビ画面の明るさを落とすために、CdSセンサーを用いたディマー(減光)回路が搭載されています。

*5 明るさ[手の影] → 抵抗値[CdSセンサー] → 電圧値[抵抗分圧入力をanalogRead()] → 周波数[tone()でスピーカにPWM出力] のように物理量を変換したのです。

*6 通称「7セグ」と呼ぶ、7本の棒で数字を表現するデジタル表示器のこと。
 今やローテクの象徴のような表示手段ですが、映画『バック・トゥ・ザ・フューチャー』世代のオジサンには、未だにタイムマシンのコックピットにずらりと並ぶ先進的でロマンに溢れたデバイスにしか見えません。

1.8 <プロ3年目コース『不思議アイテムIII-1②』>


 もっと赤外線で遊びます。

 前月、赤外線データを送受信できるようになりましたので、これを大砲の弾に見立て(*1)、戦車ロボット(オムニホイール)に搭載してサバイバル戦を繰り広げました。

戦車ゲームプログラム[IRtankFire]では、ライフ数(life)や当たり判定閾値(hit_rate)の変数が用意され、ゲーム性を簡単にカスタマイズできます。

 さて、赤外線を使って、このようなゲームが成立するのは、赤外線が照射方向に真っ直ぐ飛ぶ性質を備えているからです。
このことを「指向性が高い」と言います。

ここで波長がメートル級の電波を使ってしまうと、砲塔がどこを向いていようが、発射したビームが部屋中に拡散し、敵・見方の見境(みさかい)も無く全員を攻撃するテロリストになります。

 それならば、次のアプリケーションとして、赤外線の発信源の方向を特定し、回避したり追従したりできるでしょうか?

指向性があるとは言え、実際には、懐中電灯の光の如く、発信源(赤外線LEDやリモコン)から円錐ビーム状にある程度拡がります(*2)。
実験してみると、赤外線LEDの方向から±45°で90°(距離によっては±90°で180°も!)の範囲で受信してしまいます。
そのような赤外線の飛来方向を高精度に特定できるでしょうか?

「できない」と諦(あきら)めたら終わりです。
「入手できる複数の情報を最大限に活用して、その裏に隠された本質を抽出できるアルゴリズム(処理手順)が無いか?」を考えます。

一発の計測では無理だが、複数回の計測により受信角度の範囲が分かり、発信源はその中心の方角にあると推定できる」というアイデアですね。

オムニホイールの操舵性を活かし、その場でぐるぐる旋回させながら、受信できる角度と強度(*3)を記憶していきます。まるでレーダーのようですね。

「言うは易し」ですが、360°を10°刻みで記憶するにも36個の変数が必要です。
int range[36] のような配列に格納すれば、for文を使ってスキャンや集計が簡単になりますね(*4)。

 授業中はアルゴリズムを深堀りする時間が取れないため、ここで、赤外線ビームが飛来する方角を推定するプログラムについて考えてみます。

 赤外線受光素子を搭載したオムニホイールロボットを360°旋回させ、10°刻みで配列 int A[36] に記憶させたところ、

int A[ ] = {
  0,0,0,0,0,0,0,0,0, 
//  0~ 80°
  1,1,1,1,1,1,1,1,1, 
// 90~170°
  0,0,0,0,0,0,0,0,0, 
//180~260°
  0,0,0,0,0,0,0,0,0 };
//270~350°


で初期化した状態と同じだったとします。
ここで、配列中の各値(1/0)は、赤外線の受光有無を表します(*5)。

for( s=0; s<36; s++ ) { 
//開始位置を探す
  if( A[s]==1 )  break;
}
for( e=s+1; e<36; e++ ) { 
//終了位置を探す
  if( A[e]==0 )  break;
}


で、検出開始インデックス s=9, 検出終了インデックス e=18 が得られますから、飛来角度は (s + e)*10/2 = 135°と正しく得られます。

同じアルゴリズムで、

int B[ ] = {
  1,1,1,1,1,1,1,0,0, 
//  0~ 80°
  0,0,0,0,0,0,0,0,0, 
// 90~170°
  0,0,0,0,0,0,0,0,0, 
//180~260°
  0,0,0,0,0,0,0,1,1 };
//270~350°


の場合はどうでしょうか。
s=0, e=7 となり、飛来角度を35°と推定してしまいます。
実際は 340(-20)°~ 70°の中心値 25°と推定すべきです。

先に終了位置を探しておきましょう。

for( e=0; e<36; e++ ) { 
//終了位置を探す
  if( B[e]==0 )  break;
}
for( s=e+1; s<36; s++ ) { 
//開始位置を探す
  if( B[s]==1 )  break;
}


これで、s=34, e=7 と正しい値が得られますが、最初のデータ A[ ] に対しては s=9, e=0 となって、中心角度を 225°と誤ってしまいます。

やはり、終了位置は開始位置の後に探しましょう。

for( e=0; e<36; e++ ) { 
//先ず終了期間まで進める
  if( A[e]==0 )  break;
}

for( s=e+1; s<36; s++ ) { 
//先に開始位置を探す
  if( A[s]==1 )  break;
}
for( e=s+1; e<36; e++ ) { 
//次に終了位置を探す
  if( A[e]==0 )  break;
}


これで、A[ ] については問題ありませんが、今度は B[ ] に対して、s=34 は良いとして、e=36 で終わってしまいます。

もう一周させましょう。

for( e=0; e<36; e++ ) { 
//先ず終了期間まで進める
  if( B[e]==0 )  break;
}
for( s=e+1; s<36; s++ ) { 
//先に開始位置を探す
  if( B[s]==1 )  break;
}
for( e=s+1; e<72; e++ ) { 
//次に終了位置を探す(最長2周分)
  if( B[e%36]==0 )  break;
}


出ました! お助けヒーロー演算子“%”の登場です!
例えば、2周目で e=40 の時、B[e] は不正アクセスになりますが、B[e%36] と書いて B[4] を正しく参照できます。

これで、B[ ] でも s=34, e=43 となり、((s + e)*10/2) % 360 = 385 % 360 = 25°と正しく求められます。

int C[ ] = {
  1,1,1,1,1,1,1,1,1, 
//  0~ 80°
  0,0,0,0,0,0,0,0,0, 
// 90~170°
  0,0,0,0,0,0,0,0,0, 
//180~260°
  0,0,0,0,0,0,0,0,0 };
//270~350°


に対しても、s=36, e=45 で 45°と一応(*6)正しく機能します。

(;´▽`A`` ふぅ、安心ですかぁ?

 さらに考えるならば、自然界の計測値にはノイズ混入が付き物であり、赤外線も壁などで乱反射して届いたり、部品などに当たって瞬間的に遮蔽(しゃへい)されたりするでしょうから、所々0⇔1が入れ替わることも前提にした方が信頼性が高まります。

サンプルプログラム[IRSeekerSt2], [IRSeekerSt3]では、「同じ値が3連続以上」のような条件を位置検出に加えていますが、この仕様の是非はともかく、いい加減に作ったバグだらけのコードに見えますので(*7)、前例の配列仕様に則った改良例を記しておきます。

int D[ ] = {
  0,1,1,1,1,0,1,1,1, 
//  0~ 80°
  0,1,1,0,0,0,1,0,0, 
// 90~170°
  0,0,0,0,1,0,0,0,0, 
//180~260°
  0,0,0,0,0,0,0,1,0 };
//270~350°


のようなノイズ混入データに対して、

for( e=0; e<36; e++ ) { 
//先ず終了期間(e=12)まで進める
  if( D[e]==0 && D[(e+1)%36]==0 && D[(e+2)%36]==0 )  break;
}
for( s=e+3; s<72; s++ ) { 
//先に開始位置(s=37)を探す
  if( D[s%36]==1 && D[(s+1)%36]==1 && D[(s+2)%36]==1 )  break;
}
for( e=s+3; e<72; e++ ) { 
//次に終了位置(e=48)を探す
  if( D[e%36]==0 && D[(e+1)%36]==0 && D[(e+2)%36]==0 )  break;
}


では s=37, e=48 となり、((s + e)*10/2) % 360 = 425 % 360 = 65°と求まります。感覚的にも合いそうですね。

 どうです? この程度のことでも、突き詰めると高度でしょう?
ゲームのバグに文句を言う前に、抜け目の無いプログラムを書くことの難しさを知ってください。
そして、失敗が成功を生むように、バグをたくさん出して成長してください。

 赤外線の飛来角度が推定できたならば、姿勢センサーを搭載して(*8)、その方角に向き直る可愛らしいペット(怖い軍事ロボット?)の完成です。

プログラム中、MPU6050::getMotion6(&ax, &ay, &az, &gx, &gy, &gz) により、z軸(鉛直方向)周りでの(水平回転)角速度 gz を取り出し、これを微少単位時間毎に積算(高校数学の時間積分)して角度を算出しています(*9)。

 次回に続け、さらに赤外線の追従アルゴリズムを探求します。


*1 本当はレーザー砲と言いたいところですが、発砲のタイミングで後退りする演出(慣性の法則・作用反作用の法則)が入っていますので、重量物(物理的な弾)を発射させていると妄想すべきでしょう。

*2 レーザー光/赤外線なら、殆ど拡散せず、遠くの壁に小さなスポットを映します。
 例えば、CDのピックアップ部(読み取り装置)には、盤上の微小ピット(1μm以下)を読み取るために、赤外線レーザー(波長780nm)が使われています。

*3 実際は強度ではなく、赤外線コマンドの受信データ長(rawlen)を記録します。
 受信確度が高いほど、完全なデータ長100に近い値が得られることを利用します。

*4 例えば、a[100]のうち、最大値をmaxに記憶するには、max=a[0]; for(i=1; i<100; i++){if(max < a[i]) max=a[i];} で、
 平均値をavgに記憶するには、sum=0; for(i=0; i<100; i++) sum+=a[i]; avg=sum/100; です。

*5 実際のサンプルプログラムでは、赤外線受光強度の指標となる受信データ長が90以上か否かで判定しています。

*6 一応と言ったのは、s=36 を直接的に見つけたのではなく、一周して見つからなかったから s=36 で止まった結果であり、それでも、
どこかに必ず‘1’がある前提ならば、アルゴリズム上 s=0 を見過ごしたことになるので、ちょうど問題なく機能する特例のケースになるからです。
 このようなトリッキーなアルゴリズム仕様は、何か改変をする際にバグを生み易いので、スッキリ美しい(無駄のない)コードではあるものの、安全上は好ましくありません。

*7 思考が複雑になるので、テキストでは触れてない所ですが、バグの解析に時間を取られた身として、開発側に苦言を伝えておきます。

*8 元より、この方法論により、360°旋回スキャンや10°刻み計測ができていますので、本当は話の順番が逆なのですが。

*9 姿勢センサーについては、2年目コース2025年1月の記事を参照。
 https://higashi-fukuma-robot.blogspot.com/2025/01/#1.7


2. 5月の課題


 <スタートアップ(全コース)>
  特にありません

 <プレプライマリーコース> (プライマリーではありません)
  - オリジナル図形プリント
  https://robocobo.sakura.ne.jp/blog/HW/RobotPP2505-Q.pdf

 <プライマリーコース> (難しければプレプライマリー↑でもOK)
  - オリジナル図形プリント
  https://robocobo.sakura.ne.jp/blog/HW/RobotP2505-Q.pdf

 <ベーシックコース>
  - 授業まとめを精読する(概ね3年生以上/低学年は補助の下で)
  - オリジナル課題プリント(3面図+設問)
  https://robocobo.sakura.ne.jp/blog/HW/RobotB2505-Q.pdf

 <ミドルコース>
  - 授業まとめを精読する
  - オリジナル課題プリント(3面図+設問)
  https://robocobo.sakura.ne.jp/blog/HW/RobotM2505-Q.pdf

 <アドバンスコース>
  - 授業まとめを精読する
  - オリジナル課題プリント(見取図+設問)
  https://robocobo.sakura.ne.jp/blog/HW/RobotA2504-Q.pdf (4月配信済み)

 <プロ1年目コース>
  - 授業まとめを精読する(該当テキストページを見ながら)

 <プロ2年目コース>
  - 授業まとめを精読する(該当テキストページを見ながら)

  - CdS光センサーを使った図2-6(1日目p.17)の回路で、ボリュームインジケータ[LED5_Vol1_bar_answer]をベースに、
  様々な明るさの下で実験しながら、analogRead値 val による分岐条件を決め、
  暗闇~室内照明~日光下で0~5個のLEDが点灯する照度計(明るさメーター)に仕上げる

  - 7セグLEDを実装した形態(2日目)に、図2-6(1日目p.17)のCdS光センサー+10kΩの分圧回路を足し、
  LED8セグメント以上で高分解能表示する照度計にグレードアップする

  - 7セグ表示する2日目p.16のボリュームセンサ[_7segVol]をベースに、
  A5/GNDピンに電池(≦5V)の+/-を繋いだときの電圧[mV]を表示する電圧計に変える。

 【ヒント-照度計】
  A0入力は下図の位置です。CdS抵抗は凡そ10kΩ(暗闇)~100Ω(日光)で変化します。
  このとき、A0電位は、暗闇で 10k/(10k+10k) = 50%、日光で 100/(100+10k) = 1%
  に分圧されます。それを analogRead(A0) で1023段階に読むのです。

  <GND>---[CdS抵抗]---<A0>---[10kΩ抵抗]---<5V>

 【ヒント-照度計7セグ版】
  2日目p.10の"H"表示[_7segTest2]を参考に、7セグLEDを任意のパターンで表示するsetLed()関数を使います。

 【ヒント-電圧計】
  analogRead(A5) でA5入力電圧0~5Vを0~1023で表示するので、analogRead(A5) * 5000 / 1023 でmV表示に変換できます。
  但し、計算途中でint型整数(~32767)がオーバーフローする為、* 5 で近似するか、分かる人はlong型整数で計算します。
  電池は1~3本(4.5V)まで下図のように繋ぎます。5V以上を入力しないこと。

  <GND>---[- 電池 +][- 電池 +][- 電池 +]---<A5>


 <プロ3年目コース>
  - 授業まとめを精読する(該当テキストページを見ながら)


3. 4月の解答


 <プレプライマリーコース>
  https://robocobo.sakura.ne.jp/blog/HW/RobotPP2504-A.pdf
 <プライマリーコース>
  https://robocobo.sakura.ne.jp/blog/HW/RobotP2504-A.pdf
 <ベーシックコース>
  https://robocobo.sakura.ne.jp/blog/HW/RobotB2504-A.pdf
 <ミドルコース>
  https://robocobo.sakura.ne.jp/blog/HW/RobotM2504-A.pdf
 <アドバンスコース>
  翌テーマ1ヶ月目(来月)に配信します


4. 6月の授業予告

 https://robocobo.sakura.ne.jp/blog/hap/robo-2506.pdf

 <プレプライマリーコース>『スシロボー』
 <プライマリーコース>『チャリダー』
 <ベーシックコース>『ダンプくん』
 <ミドルコース>  『扇風丸』
 <アドバンスコース>『ロボビート①』
 <プロ1年目コース>『オムニホイールロボット③』
 <プロ2年目コース>『不思議アイテムII③』
 <プロ3年目コース>『不思議アイテムIII-1③』


5. 今後の授業スケジュール


6月の日曜日の教室(東福間プロ・小倉北・南)は一週ずつ遅れます。
八幡東7/19は創フェスの為、7/26へシフトします

――――――――――<佐藤教室長>――――――――――

[東福間]第1・3土原則<学習ルームでこぼこ>
   - 13:30~ ベーシック/ミドル
   - 15:30~ アドバンス

 ⇒ 6/7, 21,  7/5, 19,  8/2, 16


[東福間プロ]第2・4日原則<学習ルームでこぼこ>
   - 10:00~ プロ1年目
   - 13:00~ プロ2年目
   - 16:00~ プロ3年目

 ⇒ 6/15※, 29※,  7/13, 27,  8/10, 24

 ※6月も一週ずつ遅れます

[サンリブ古賀]第2・4土原則<文化サークル>
   - 10:30~ ベーシック/ミドル/アドバンス

 ⇒ 6/14, 28,  7/12, 26,  8/9, 23


[小倉北]第1・3日原則<ムーブ>
   - 10:30~ ベーシック/プライマリ
   - 13:00~ ミドル
   - 15:00~ アドバンス

   - 12:30~ プロ1・2年目
   - 15:00~ プロ3年目

 ⇒6/ 8※第1回 5F企画ルーム1
  6/22※第2回 5F企画ルーム1
  7/6, 20,  8/3, 17

 ※6月も一週ずつ遅れます

――――――――――<中野教室長>――――――――――

[八幡東]第1・3土原則<レインボープラザ4F>
   - 13:30~ ベーシック/プライマリ
   - 15:30~ ミドル
   - 17:30~ アドバンス

 ⇒ 6/7, 21,  7/5, 26※,  8/2, 16

 ※7/19創フェスの為、7/26へシフトします

[小倉南]第2・4日原則<総合農事センター2F>
   - 10:30~ ベーシック/プライマリ
   - 13:00~ ミドル
   - 15:00~ アドバンス

 ⇒ 6/15※, 29※,  7/13, 27,  8/10, 24

 ※6月は一週ずつ遅れます


【振替教室ご利用条件(ロボプロを除く)】――――――――
 振替先の1週間前までにメール下さい(許可制)
 振替手数料550円/回をご負担下さい(お引落し)
 (東福間⇔小倉北のみ無料)
 所定コース内容・時間のみお受けします


6. お知らせ


1) ロボプロ卒業制作 発表会6/22(日) 観覧自由

 ロボプロ3年目を3月修了した卒業生のうち、下記2名に恒例の卒業制作を発表🎊して貰います。
 🎓小倉北/桑村 悠太郎 君(高2)
 🎓小倉北/二宮 快樹 君(高1)

 発表作品・タイトルは『???』です。
 【日時】6/22(日) 14:40~15:00
 【会場】小倉北ムーブ5F 企画ルーム

 小倉北/ミドル・アドバンス・プロ生および保護者の他、何方でも是非ご観覧下さい。


2) 第15回ロボット教室 全国大会 エントリー開始

 全国大会2025年への参戦応募を受付開始しました。

 案内冊子(紙面)はありませんので、PDFをお目通し下さい。
 https://robocobo.sakura.ne.jp/blog/convention/RobotConv2025.pdf

【日程】
 8/23(土) 東京大学 安田講堂(本郷キャンパス内)

【エントリー部門】
 ●アイデアコンテスト (全コース) :予選応募 画像・プレゼン動画(2分以内)
 ●テクニカルコンテスト(アドバンス):予選応募 動画(連続5回試技)

【告知・申込】
 ▶ 5/28(水)12:00~6/25(水)17:00 募集要項公開・エントリー・観覧申込受付中

【特設HP】
 https://kids.athuman.com/robo/event/convention/2025/


3) 創フェス(クリエイティブフェス) 受付開始

 全国大会に先立ち、従来の「スペシャル地区イベント」に代わり、
今年は未来創生STREAM教育総合研究所(RISE)との共催にて、
全国7都市で創フェス(クリエイティブフェス)を開催します。

【日程】
 福岡県 北九州市: 7/19(土) 九州工業大学(戸畑キャンパス)

【内容】
 自作ロボット/プログラムの発表・展示、体験、ロボットレースなど

【告知・申込】
 ▶ 5/28(水)12:00~6/25(水)17:00 応募要項公開・エントリー受付中

【特設HP】
 https://www.miraisosei.or.jp/fes/2025/


4) 神山まるごと高専 コラボイベント

 ◆7/21~23 サマーキャンプ2泊3日(中1・2生)

【告知・申込】
 ▶ 6/2(月)12:00~6/30(月)17:00 募集要項公開・受付

【特設HP】
 https://kids.athuman.com/summerschool2025/


5) 4月課題 高得点者  []内は教室と学年

 ◆プレプライマリ
   5点…土屋[小倉北1]

 ◆プライマリ
  高得点なし

 ◆ベーシック【14名平均 図面3.1+設問1.5=4.6】
  10点…なし
   9点…なし
   8点…なし
   7点…福田[八幡東4], 姥[小倉南5]
   6点…なし

 ◆ミドル【11名平均 図面3.4+設問1.7=5.1】
  10点…なし
   9点…なし
   8点…土屋[小倉北5], 井上[小倉南5]
   7点…古川[小倉北6]


東福間・小倉北・サンリブ古賀教室 佐藤 / 八幡東・小倉南教室 中野