私がロ技研で作ってきたロボットたち

はじめに

久しぶりにサークルの記事を書いています、修士1年のしゅんもです。緊急事態宣言が解除されてもなかなか大学に行くことができず、家での研究を進めています。

そんな中、ロ技研での活動実績についてまとめた記事がほしいなと思ったので、後輩への紹介も兼ねて、サークルの記事にまとめてみます。

F3RC2016(大学1年)

まずは私の初ロボコンである、F3RC2016で作ったロボットです。CAD設計、加工、組み立てなどのハードウェア設計を担当しました。今見返すと恥ずかしいくらい簡素な作りですね…

なんとこのロボット、マイコンやモータードライバが乗っておらず、モーターと電池が直結というありえない作りになっています笑 左下の木片がリモコン、それに貼り付けられている乾電池4本が動力源ですね。

F3RC 2016 堀Cチーム 手動機

かわロボ2017(大学1~2年)

F3RCは新入生向けのロボコンなので、自分で申し込んだロボコンとしてはこれが初めてです。私は、足回りのハードウェア設計を担当しました。

かわロボでは4節ヘッケンリンクと呼ばれるリンク機構を用いてロボットの足回りを設計する必要があります。そのため、CAD設計はなかなか苦戦しましたが、このおかげでSolidWorksを用いたCAD設計をある程度習得できた気がします。

CloudRoachの足回りのCAD
CloudRoachの実機
ゴキブリのような動きをします。倒されても復帰可能です。
静止した敵ロボット(に見立てた過去の先輩のロボット)を投げ飛ばす様子

側面の雲(Cloud)のような形と、ゴキブリのような動き(Cockroach)から、CloudRoachと名付けました。ネーミングセンスは微妙ですね。上の動画のように、平地であれば結構素早く動けます。しかし、実際の試合会場は凸凹しており、足が取られてなかなかスムーズな動きを取るのは難しいです。そのため、足回りを色々と工夫されている方が多くいらっしゃいます。

予選トーナメント 第1回戦
敗者復活戦

NHK学生ロボコン2018(大学2~3年)

次に参加(しようと)したロボコンは、NHK学生ロボコン2018です。サークルとしてNHK学生ロボコン初出場を目指し、発足したNHK学生ロボコンチーム、Iliasのチームメンバーとして、シャトルコック(布の塊に紐がついたもの)を投射する自動ロボットの投射機構のハードウェア設計を担当しました。1次ビデオ審査は通過したものの、2次ビデオ審査で落ちてしまい、本戦出場はかないませんでした。

自動ロボットのCAD
シャトルコックを投射する自動ロボット

かわロボ2018(大学3年)

NHK学生ロボコンの出場が叶わなかったため、次のNHKロボコンの活動が開始するまで、かわロボにを行うことにしました。かわろぼ2018では私が最高学年で、優秀な後輩とともにロボットを制作しました。私が担当したのは、かわロボ2017と同様、足回りのハードウェア設計です。

このロボットで重視したことは強さとデザイン性です。試合に勝つことはもちろんですが、かわロボにはかっこいいロボットが多数参加していたので、私もかっこいいロボットが作りたいと思い、赤を貴重とするデザインにしました。赤いパーツは全て自宅の3Dプリンタ(中華製の安物)で印刷しました。

WeissrotのCAD
Weissrotの実機
かわロボ2018 予選トーナメント 第1回戦
かわロボ2018 敗者復活戦

NHKロボコン2019(大学3~4年)

最後に私が参加したロボコンです。サークルとして初めて出場したNHK学生ロボコンでもあります。4足歩行ロボットという難しいテーマでしたが、私が設計していたサーボモータを使った4足歩行ロボットで1次ビデオおよび2次ビデオをクリアし、念願の本戦出場が叶いました。

手動ロボット(MR1)
4足歩行ロボット(自動ロボット, MR2)

私の担当は、この4足歩行ロボットや手動ロボットの全方位移動足回りのの設計、組み立て等のハードウェア設計、および4足歩行ロボットの制御でした。4足歩行のノウハウもなかったため、ネットの記事を参考に、足の位置を順番に指定して歩行させるプログラムを書きました。

また、メンバーが少なく、4足歩行ロボットの機構部分の設計や組み立ては一人で行っていたため、初めは3Dプリンタを使った試作機を素早く組み立て、歩行プログラムを書き始めるようにして、開発スピードの短縮に努めました。

チーム紹介動画
4足歩行ロボットが立つようになってから歩くようになるまでの変遷

最後に(新入部員のみなさんへ)

新入部員の皆さんは、大学という新しい環境で不安な中、なかなか大学に入れず、サークルでの活動も行えなくて、モチベの維持が難しいかもしれません。でもこの記事で、「ロ技研でのロボット製作はこんなに楽しいんだよ!」というのが少しでも伝わればいいなと思います。先輩から様々なことを学んで、サークルで蓄積してきたノウハウを引き継いでいってもらえると嬉しいです。

Fusionで角柱を自動生成する

こんにちは、理工学部電気情報工学科2年のFastriver(@fastriver_org)です。最近活動ができない(機構なので現地じゃないと動けない)ので、久々に機構制御班の仕事をしようと今回のようなことをやりました。

CADを書きたくない

ずっと思ってます。昔(F^3RCの頃)はCAD大好きだったのですが、NHK班に入ってから書くのが非常に面倒に思えてきました。

その原因の一つは、

同じような見た目の角柱を大量に作るので精神が削れていく

というわけです。あんなの設計から加工まで自動化してくれよ。

加工を自動化するのは無謀なのでせめてCADだけでも自動化(コードベース)しよう、という取り組みです。

使うもの

  • Fusion 360
  • Fusion 360 API
  • Python

Fusionさん、学生は無償で使えるの素晴らしいですよね。

https://www.autodesk.co.jp/products/fusion-360/students-teachers-educators

を見てダウンロードしてください。普段使っているSolidworksを見送った要因は、APIのダウンロード方法がよくわからなかったからです。Fusionの方が単純にできると思います。

プロジェクトを作る

最初に開いたところから、[ツール]→[アドイン]→[作成]で新しいプロジェクトが作れます。

スクリプトを選びます。アドインは起動時に実行ができるらしいですが、要らないのでスルー。

C++とPythonだったらPythonの方が書きやすそうなのでPythonを選択。公式リファレンスではどっちのサンプルもあるのでどっちでも大丈夫だと思います。

下のフォルダの場所に生成されるのでVSCodeとかで開いて適当に編集しましょう。最初はダイアログが開くだけのスクリプトが入っています。デバッグも可能なそうですがやりかたがわかりませんでした。

書く

公式リファレンスはこちら。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-7B5A90C8-E94C-48DA-B16B-430729B734DC

一番見やすかったのでこちらのサイトを参考にコードを書きました。

コードは結構長いためGitHubで見てください。

https://github.com/organic-nailer/PrismGenerator

各ファイルの役割はこんな感じです

  • PrismGenerator.py: 動かすとここのrun関数が呼ばれる
  • DialogHandler.py: パラメータを入力するダイアログのクラス
  • Prism.py: 角柱の生成とかを書いたクラス
  • DataManager.py: 全体で必要なオブジェクトを保持しておくクラス

Python書くのすごくつらいですね、この程度の規模でも泣きそうになってました。

ご乱心

git cloneとかすれば誰でも動かせると思います

動かしてみる

書けたら動かしてみましょう

アドインの画面でスクリプトを選択し、[実行]で動きます。

保存する

やっぱりSolidworksで使いたいので、エクスポートしましょう。

左上の[ファイル]→[エクスポート]からStepファイルで保存します(これが最適解だよね?)。

Solidworksにインポートしたところ普通に動いたので問題ないと思います。

最後に

自動化は正義

入試物理をシミュレーション!!

シミュレーションについて軽くおさらい

微分方程式を立てて解く

世の中の様々な物理現象は、基本的に微分方程式を立てて、それを解くことで明らかにすることができます。微分方程式はニュートンの運動方程式とかナビエ・ストークス方程式などがあり、式さえ立てれば、後はそれを解くだけです\(^o^)/。

 

2つの解き方がある

解くための方法として、解析解数値解の2つあります。解析解は微分方程式を数学的操作のみを使って求めます。ゆえに、厳密な結果(誤差0)を導き出せますな。一方、数値解は、微分方程式を四則演算の関係で表す差分方程式に変換して、数値的に求めます。

 

数値解ってな~に?

数値解(オイラー法)

数値解の求め方ってなんなの?って思われるかもしれないので説明しておきます。例としてニュートンの運動方程式F = ma を挙げます。ここでは、数値解として変位xを求めてみましょう。以下の流れで求まりますね。

a = F/m → v = v + aΔt  → x = x + vΔt    ※ ” = ” は右辺を左辺に代入

これをオイラー法と言います。 v = v + aΔtでは、加速度a をΔtの間だけ一定とみなしているので、速度vは時刻tに関して直線的に増減します。ここで解析解との誤差が生じることがわかりますね。x = x + vΔt についても同様です。

 

計算誤差

複雑な物理現象になると、微分方程式も複雑なものになり解析解を求めることが困難になります。このとき、数値解を使わざるを得ないが、数値解には誤差がつきもの(T_T)。

 

より精度の高い数値解(ルンゲクッタ法)

数値解における計算誤差を減らしてできるだけ精度の高い計算をしようとするのが、ルンゲクッタ法です。以下のアルゴリズムで求めます。

V(t, x, v) = v ,    A(t ,x, v) = F(t, x, v)/m

v(1) = V(t,  x,  v)

a(1) = A(t,  x,  v)

v(2) = V(t+Δt/2,  x+v(1)Δt/2,  v+a(1)Δt/2)

a(2) = A(t+Δt/2,  x+v(1)Δt/2,  v+a(1)Δt/2)

v(3) = V(t+Δt/2,  x+v(2)Δt/2,  v+a(2)Δt/2)

a(3) = A(t+Δt/2,  x+v(2)Δt/2,  v+a(2)Δt/2)

v(4) = V(t,  x+v(3)Δt,  v+a(3)Δt)

a(4) = A(t,  x+v(3)Δt,  v+a(3)Δt)

x = x + Δt/6 (v(1)+2v(2)+2v(3)+v(4))

v = v + Δt/6 (a(1)+2a(2)+2a(3)+a(4))

 

いざ、シミュレーション!!

ルンゲクッタでシミュレーションしてみました。Pygameというモジュールを使いました。

titech

う~ん、うまく行かないなぁ。物体がたくさんあるとそれだけ誤差が蓄積されますね。

物体と物体をつなぐ糸の長さはすべて等しいという体で運動方程式を立てたのですが、なんかの伸び縮みしてますね… 。力技で物体間の糸の長さを一定に保つように、PD制御で張力にフィードバックさせたのですが、うまく行かなかったです(TдT)。まぁ、コレは今後の課題ということで(笑)

おまけ

titech_xmas2

 

ニンテンドークラシックミニ スーパーファミコンの通信解析

こんにちは,tokkyo13です.今更感がありますが,ニンテンドークラシックミニシリーズの話題です.ニンテンドークラシックミニ スーパーファミコン(長いので以降 SNES mini と呼びます)のコントローラーが行っている通信について調べたので,まとめておきます.

解析を行うにあたって,以下のブログを参考にしました.NES mini の解析は日本語の資料がすでにあり,SNES mini の通信も結果的にほとんど同じでした.「両コントローラは互換性がある」なんて英語記事もありました. また,実はすでにコントローラーとArduinoが通信するためのライブラリは用意されていますが,今回の解析結果を使うと,コントローラをエミュレートしてArduino-本体間の通信をすることもできます.

プロトコル

通信プロトコルはI2Cを使っています.本体がマスターでコントローラがスレーブです.コネクタのピンは図の番号に対応して次のようになっています.このコネクタはWiiヌンチャクなどにも使われていたものです.

  • ① 3.3V
  • ② 接続認識
  • ③ SDA
  • ④ SCL
  • ⑤ NC(不使用)
  • ⑥ GND

②のピンは,コントローラの抜き差しを本体に知らせるもので,HIGHにしてやると通信が開始されます.コントローラ内部まで②の線は引かれていません.コントローラ側のコネクタ内部で3.3Vに直結されているようです.

コントローラのI2Cバス上でのアドレスは0x52です.これはWiiヌンチャクなど同じコネクタを持つデバイスすべてで共通らしく,デバイスの識別はその後の通信でデバイスIDを送信することで行われます.

コマンド

コントローラは256byte(アドレス1byte)のレジスタを持っており,そこにコマンドでデータを読み書きすることで通信を行います.コマンドは以下の3種類です.データを読み出すときは,読みたいレジスタのアドレスをWriteで指定してからReadするという方式です.

  • 書き込み
    • 0xA4 [レジスタのアドレス] [書き込むデータ]
    • 0xA4 はコントローラのアドレス0x52の下位7bitの後ろに0を付け足したもので, 0x52のデバイスにWriteするという意味です.Arduinoなどでは後ろの2byteを0x52宛に送ってやれば良いです.
  • 読み出すレジスタの先頭アドレスを指定
    • 0xA4 [レジスタのアドレス]
  • 読み出し
    • 0xA5 [データ] [データ] …
    • 先に指定したアドレスを先頭に複数レジスタを順に読み出せます.
    • 0xA5 はコントローラのアドレス0x52の下位7bitの後ろに1を付け足したもので, 0x52のデバイスからReadするという意味です.Arduinoなどでは0x52宛にリクエストを送ってやれば良いです.

通信の流れ

SNES mini が行う通信は以下の流れで行われています.

  1. 暗号無効化1
    0xA4 0xF0 0x55
  2. 暗号無効化2
    0xA4 0xFB 0x00
  3. デバイスIDを書き換え(レポートモードの変更?)
    0xA4 0xFE 0x03
  4. 読み出し指定
    0xA4 0xFA
  5. 読み出し(デバイスID 6byte が読み出される)
    0xA5 0x01 0x00 0xA4 0x20 0x03 0x01
  6. 読み出し指定
    0xA4 0x00
  7. 読み出し(コントローラーのボタン状態含む 21byte が読み出される)
    0xA5 0x82 0x84 0x84 0x84 0xXX 0xXX 0xXX 0xXX 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  8. 以下6-7を繰り返し.

ヌンチャクなど同種のデバイスには通信を暗号化する機能があるようです.今回の SNES mini は上の通信内容からもわかるように暗号が無効化されていたので僕はあまり詳しく調べませんでしたが,参考にした Wii BREW のページには詳しく書かれていました.どうやら最初に本体から鍵(ランダムな数列)が送られてきて,以降それを使った数式で暗号化を行うようです.暗号の無効化は0xF0に0x55,0xFBに0x00を書き込むとできます.

3番ではデバイスIDを書き換えています.この操作を行わなくても通信はできますが,送られてくるデータの構造が変わります.書き換えなかった場合のデバイスIDは 0x01 0x00 0xA4 0x20 0x01 0x01 であり,これは Wii のクラシックコントローラーPROと同じです.このIDのとき,ボタン状態は6byteのデータとして送られてきます.こちらのデータ構造はWii BREWのページを参考にしてください.書き換えた場合,0x00から21byte読み出すと,ボタン状態8byteを含む21byteのデータで送られてきます.SNES mini の場合,意味があるのは0x04-0x07から読み出されたデータで,このときのデータ構造は以下の表のようになっています.

byte \ bit76543210
0
1
2
3
4LLL
5RRR
6SELECTSTART
7BYAX

0x00-0x03までに書き込まれているデータはクラシックコントローラーにおけるジョイスティックの座標情報を格納すべき空間だと思われますが,SNES mini にジョイスティックはないので代わりに謎の数字が格納されています.これらの値はコントローラの操作に応じて変化することはありませんが,0x00のデータなどは頻繁に±1程度の変化を見せることがあります.理由はわかりません.

ボタン状態を表すのは4byteのみですが,それ以外の変化しない謎のデータも一緒に21byte送ってやらないと本体の操作はうまく行かないようです.

これで通信の内容はだいたい説明できたかと思いますが,実際に通信するプログラムを組むときに気をつける点があります.読み出すレジストのアドレスを指定するコマンドと読み出しコマンドの間には1ms程度の時間を開けましょう.オシロスコープで波形を観察したところ1.75ms待っており,実際にArduinoではdelay(1);を入れないとうまく動きませんでした.コントローラがレジスタを更新するのを待っているのかなと思います.

以上,解析結果でした.

この続きとして,SNES mini とコントローラの通信を邪魔せずI2Cのデータを傍受する I2C Sniffer を作成したので,それも記事にするかもしれません.

ロゴの話

慶應ロ技研 Advent Calendar 2018 その1 4日目

前書き

こんにちは、pirorohsです。STM32のHALで何やかんやする話を書く予定でしたが、折角の機会なので今年2月に決定したロ技研のロゴの話をしたいと思います。STM32の方は慶應ロ技研 Advent Calendar 2018 その2で(出来たら)やります。

ロゴが欲しい!

そもそも、去年の時点でロ技研にはマスコットキャラクターのロギ犬は居たものの、ロゴマークは定まったものがありませんでした。各大学のロボットサークルや、慶應でも様々なサークルがそれぞれのロゴを掲げて活動を行っていますから、ロ技研もこれから活動を拡大していくに当たって組織のシンボルとなるようなロゴが欲しいということで検討を行いました。嘘です。今でっち上げました。組織のシンボルを掲げて活動するみたいなの良くないですかと思ったので勝手に作りました、オタクなので。

続きを読む “ロゴの話”

かわロボ2018が終わりました!

こんにちは。3年のしゅんもです。先日8/25にかわさきロボット競技大会 バトルロボット部門の予選トーナメントがありました。昨年同様、1勝もできずませんでしたが、さまざまなノウハウは蓄積できたかと思います。

機体の赤いところはすべて3Dプリンタで制作しました。材質はPLAです。

脚リンクにも3Dプリンタを使ったのですが、流石に負担がかかるところだったため、相手の鎌に引っ掛けられたり、溝に脚がハマったときに折れてしまいました。やはり3Dプリンタの強度は過信しすぎないほうがいいですね。

僕は3年なので来年は参加しませんが、1年生にかわロボを引き継いでもらい、来年こそ決勝トーナメント進出を果たしてほしいです。頑張ってください。

ModernなOpenGLを書きましょう

どうも、チョコです。
最近いろいろあってOpenGLについて調べた結果、その知見を共有したいと思います。

OpenGLは近年(最近とは言っていない)programmable pipeline(つまりシェーダが書ける)を導入し、v3.0以降にはfixed function(シェーダが書けない)関数をdeprecateしました。しかし、OpenGL context(初期化)を作るときにcoreじゃなくてcompatibleにすると古い関数はまだ使えます。これは落とし穴です。

なぜかというと、プラットフォーム(やハードウェア)によって動かなくなるからです。

Windows(OpenGL 4.5) ではうまく動いたり、
Android(OpenGL ES 3.0) では半分動いたり、
VM上のUbuntu(OpenGL 3.3) では全然動かなかったりします。<いまここ

なんでこんなつらい目に合ってるのでしょう?

答え:公式レファレンス(man pageなど)が古い(場合によっては間違っている)からです!!
(K〇大学のS〇工学科でも一番古いやり方を教えていることを最近分かったんですよね)

そもそも同じことをするにはいろんなやり方があり、どっちも(場合によって)動くのでこれといった答えはないが、やっぱり古いAPIは使いたくないですね。

そこで、新しい書き方についてちょっとだけ紹介したいと思います。

Programmable Pipelineでは、xxBeginやxxEndが使われないのが特徴です。また、データ集(頂点など)はバッファ(Array)で渡し(つまりあらかじめGPUに保存させる)、シェーディングは全部シェーダでやります(glShadeXXはダメ)。

従来のやり方を見てみましょう。

一番古い(のに未だによく見かける)書き方はこれでしょう。

glBegin(GL_LINES);
glVertex3f(...);
glVertex3f(...);
glEnd();

読んでるだけで目が痒くなりますね。

では、ちょっと新しい書き方だとどうなるのでしょう。

vec3 vertices[2];
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_LINES, 0, 2); //あるいはglDrawElements
glDisableClientState(GL_VERTEX_ARRAY);

バッファを使いましたね。ちょっときれいになりましたね。先週まではこれで書いていましたね(反省中)。

しかし、これでも動かないときもあって、なぜかというとこれも古い書き方だからです*!
*ソース

では、Modernな書き方*は何でしょう?

vec3 vertices[2];
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, &vertices[0]);
glDrawArrays(GL_LINES, 0, 2);
glDisableVertexAttribArray(0);

参考

*スペースがないので、実際に使ったコードはここを参照してください。

何が違ったというと、古いOpenGLでは頂点や法線などのバッファー位置が固定されていて、glVertexPointerなどで渡さないといけなく(VERTEX_POINTERやらCOLOR_POINTERやらで固定され)、自由度が減っています*。逆に、glVertexAttribPointerは、シェーダ内のパラメータ(uniform in vec3 position)などにデータを渡すので、関数が一般化されて自由度が上がります。
*ソース

さらに、頂点データは全部あらかじめGPUに保存させることになりますし、シェーダを使わない選択肢はありません(考えてみれば理解できます)。ある意味めんどくさいところもありますが、すごく一般化されたAPIになりますね。そして、初期化(vaoのセットアップ)は1回だけなので、繰り返し使うときのコードを短縮できますね。

OpenGLの仕様が変わるのそろそろ10年経ちますし、みなさんもModernなGPUを最大限に生かして、ModernなOpenGLを書きましょう!

では。

 

後書き
仕様を探るときにはいつも公式レファレンスを読んでいましたが、読みやすくて使用例が載っていてdeprecateされたものもはっきり書いてあるコミュニティ制レファレンスを今度読んでみましょう。

後後書き
古いものを読んでしまう可能性があるので公式しか読んでいないのに、公式自体が古いというのはちょっと刺さりますね。

後後後書き
glutを使っているとcontextがしょうがなく古くて(freeglutならglutInitContextVersionで)、glfwを使っているときにglfwWindowHintでcontextを指定できます。

後後後後書き
ここの教えている書き方はModernなのでいいです。

後後後後後書き
僕もこれが一番新しい書き方かの自信はそこまでないので、OpenGLに詳しい方がいらっしゃったら教えていただけるとありがたいです。