syuntoku14の進捗

進捗を書きなぐります

冬学期が終わりました

留学の3分の2が終わり、こっちの生活にもだいぶ慣れてきた気がする。基本的に家に引きこもって勉強している。冬学期は雨ばかりで少し憂鬱だったが、最近はとても良い天気なので気持ちが良い。がんばる。

現在春休み期間中であるが、春休みは一周間しか無い。キレそう。この一週間で一ヶ月後のテーマを決め、それの下準備を終わらせないといけない。休みではない。

冬学期にやったこと

授業:

Computer Networking: 

非常に良かった。今まで適当に自分で学んできた知識が体系化された。wireshark弄ったり、プロキシの実装が課題で出てくる。Computer Networking: A Top-Down Approachを読みきった。成績はB+だった。悲しい。

Estimation and Detection of Signals in Noise: 

最高。教科書は難しいが、教授の授業が本当に分かりやすく最高だった。少人数なのでやりたい放題。Principles of Signal Detection and Parameter Estimationを読みきった。難しいが、非常に良い。

また、プロジェクトとして2018年以降のIEEEのSignal Processing Transactionsから一本選んで実装しないといけなかったので、Optimal Bayesian Kalman Filteringを選んで実装した。大して難しくなかった。

Self-Driving Car Project(前期): 

ぶっちゃけるとだいたい既知だったのであまり新鮮ではなかった。が、R-CNNやYOLOあたりの知識が実装を通じて深まったので良かった。本質ではないが、Dockerで動いているGUIのウィンドウをリモートから引っ張ってくるあたりの知識がついた。

知識的にはあまりおもしろくなかったが、異国での初めてのチームワークだったので経験的には非常に良かった。後期は交通制御をやっていくつもり。果たしてプレゼンまでに間に合うのか。

Strategies for Securing and Succeeding in an Engineering job: 

単位の埋め合わせ用。1単位だけ。理系の仕事探しについて色々な人が話す。別に悪くなかったので聞いていた。

授業以外

言い訳になってしまうが、授業で精一杯だったので寄り道勉強をする暇があまりなかった。そもそもの話だが、こちらの授業は本当にしっかりしているので寄り道勉強をする必要が無い気がする。 半年くらい前に始めたはずのCourseraのDigital Signal Processingは未だ終わっていない。春休みに終わるといいなって感じ。楽しそうなので、次は Graph Theory か Information Theory をやろうと思う。

授業以外のプロジェクトはHanabi強化学習やマイクロマウスがあったが、あまりに忙しかったので頓挫した(言い訳)。

英会話力は依然として不十分なのでなんとかしたい。他の留学生、めちゃめちゃ英会話力上がっててビビる。

旅行でフロリダに行きました。ワニがデカかった。

f:id:syuntoku14:20190324043309j:plain

デカイわに

春学期(来学期)について

教授に頼み込んでリサーチをやらせてもらえることになった。テーマは強化学習だったら何でもいいらしい。交通制御かBandit Problemのどっちかになりそう。交通制御はどっちにしろやらないといけないので、まとめて負担を減らしたいところ。

他の授業としては、非線形制御とPCBの設計をとる予定。大丈夫かな…

帰国後について

インターンが通ると帰国が9月になるが、インターンに通る気がしない。通った場合、院試に間に合わないので通常より院生生活が1年遅れてしまう。1年長生きすることで埋め合せが可能。院で何するかまだ決まってない。マルチエージェント制御は結構興味がある。

その他

Twitterで頻繁に帰りたいアピールをしている。生活面では帰りたいが、授業はこっちのほうが圧倒的に良いので難しい。日本の飯に勝てるものは地球上にほぼないと思う。

帰ったら美味しいものが食べたい。誰か焼き肉に連れて行ってくれ。

2018年で学んだことと今後の目標

やったこと

  • NHKロボコン
  • Coursera
  • 英会話
  • 剣道
  • 留学(大変)
  • インターン(ROSなど)
  • Marloコンペ(Dockerで環境構築しただけ)
  • 少しだけサーバー構築
  • AIクラブでごにょごにょ
  • samurAIコンペ
  • 勉強会(確率ロボティクス、確率統計、ベイズ for Programming、Sutton本)
  • たくさんの技術本(たくさん)

学んだこと

  • 強化学習の基礎(Coursera)
  • 信号処理の復習(Coursera)
  • 古典制御の復習(授業)
  • 画像処理(少し)
  • 英語
  • 確率統計, ベイズ関連
  • 確率ロボティクス関連の知識(少し)
  • ディープラーニング(少し)
  • 差動駆動ロボットの制御(少し)

今後の目標

  • 英語の上達
  • アルゴリズムの勉強(Courseraなど)
  • 線形代数の復習(Courseraなど)
  • 現代制御の復習(授業)
  • 自動運転について学ぶ(授業)(この授業、内容がてんこもりすぎてついていけるか怪しい)
  • Roboticsのちゃんとした知識の獲得(Udacityかも)
  • 機械学習をちゃんとやる(ちゃんとやってなかった)
  • マイクロマウス
  • 最適制御、マルチエージェント制御の勉強
  • samurAIコンペ
  • Marloコンペの続き
  • 広く浅い知識を深めていきたいよね
  • 生きて日本に帰る

感想

書き出してみると学んだことが意外と少なかった。来年は更に頑張りをしていきたい。確率関連の知識が身についたのは去年の良い所。

学んだことよりも、勉強の仕方そのものが確立されたのは去年の良かったところの一つだと思う。今年はもっと効率よく勉強していきたい。

去年は人生で一番行動力が高かったと思う。何でもかんでも参加してみることの重要性を感じた。思い切ってロボ飲みやAIなんちゃらに参加したおかげで、多方面に人脈が出来たりサンクスギビングがすごく楽しくなったりした。今年も何でもかんでも参加していく。

英会話や留学を経験したのは大きな前進だったと思う。これも両親の理解や支援なくして出来なかったことなので、本当に感謝している。将来的に恩返しが出来るようになるのも目標の内

剣道で学ぶ根軌跡でのPID設計

 

はじめに

こんにちは。syuntoku14です。現在カリフォルニア大学デイビス校で留学中です。日本に帰ったらよろしくね。

今回は、根軌跡を用いたPID制御器の設計を復習していきます。記事としてまとめる理由は、

  1. PID制御でググると色々な記事が出てくるものの、あまり根軌跡を用いたものが見受けられなかったため
  2. 今までガバガバだった古典制御の知識がようやくまともになったので
  3. 特に書くことがなかったので

などです。

僕は強いエンジニアの皆さんと違い、小さい頃からの回路やプログラミングの経験がありません。小さい頃からやっていることなんて剣道くらいです。なので、今回は剣道を例にしてPID制御器を設計してみましょう。

前提

おつま君とぴたむら君は大学の剣道部に所属しています。来る段審査のため、二人は練習に励んでいます。段審査では、木刀を相手の頭の上ギリギリで止めるような一太刀が求めらており(以降面打ちと呼びます)、二人はこの面打ちを練習しています。

ぴたむら君は面打ちが苦手で、なんどやっても面打ちは頭の上ギリギリで止まらず、おつま君の頭を叩いてしまうか、上すぎる位置で止まってしまいます。

このままでは段審査に落ちてしまうので、二人は制御工学を用いてこの問題を解決することにしました。二人の専攻は幸いにも制御工学だったので、こんなこと朝飯前です。

制御対象について

目標は振りかぶった木刀をおつま君の頭の上ギリギリで止めることです。

簡単のため、以下のように木刀はぴたむら君の肩から直接生えているようなモデルを考えます。さらに、ぴたむら君の肩にはDCモーターが直接埋め込まれていると仮定し、モーターへの入力\(E\) によって90°からの角度\(\theta\)を制御することを考えましょう。

頭の上ギリギリで止めることを、ここでは、90°振りかぶり、おつま君の頭の上から5°の地点まで振り下ろすこととします。

f:id:syuntoku14:20181213140348j:plain



本来ならば入力から出力までの伝達関数を同定しなければならないですが、長くなるので割愛し、剣道部の監督によって教えてもらったこととします。監督曰く、入力\(E\)から出力\(\theta\)までの伝達関数\(G(s)\)

\[G(s)=\frac{\Theta(s)}{E(s)}=\frac{(s+6)}{(s+2)(s+3)(s+5)}\]

で与えられます。これを開ループ伝達関数とし、以下のようなフィードバック制御システムを考えます。

f:id:syuntoku14:20181213140422j:plain

P制御器設計

まず、P制御、つまりゲインKを調整して、いい感じの出力を出すことについて考えてみましょう。おつま君の頭を叩いてはいけないので、目標角度である85°より5°以上振り下ろしてはいけません。そこで、出力のオーバーシュート率(%OS)を5%とすれば、出力は最大で89.3°となります。

少しギリギリなので、ここでは%OS=4.3%となるよう、減衰率\(\zeta=0.707\)として設計していきます。(システムが二次システムの場合、%OSは\(\%OS=\exp(\frac{-\zeta\pi}{\sqrt(1-\zeta^2)}\)で表されるため、減衰率と%OSは一対一に対応します。伝達関数\(G(S)\)は二次システムではありませんが、条件を満たした場合二次システムと近似できます(条件は以下で考えます))

まず、\(\zeta=0.707\)となるようなゲインKを根軌跡から探してみます。

システムが二次システムである場合、減衰率を指定する直線は根軌跡上で\(\cos{\theta}=\zeta\)によって表されます。

おつま君はMatlabを使い、\(G(s)\)の根軌跡と\(\zeta=0.707\)の交点を求めました。

f:id:syuntoku14:20181213140633p:plain

Matlabによって求めた結果によると、\(\zeta=0.707\)のときの閉ループの根は

\[s=-2.32\pm j2.3,\; -5.36\] であり、この時のゲインKは\(K=4.55\)でした。

ここで、極の一つである\(s=-5.36\)を零点の\(s=-6\)と近いとみなし、打ち消し合うと仮定します。そうすると本システムは二次システムと近似することができます。

この時点で、%OSを約5%とするゲインKの設計が終わりました。つまり、ゲインKを4.55とし、開ループの伝達関数

\[G(s)=\frac{4.55(s+6)}{(s+2)(s+3)(s+5)}\] とすれば、出力の%OSが理論上は約5%となります。実際にMatlabでstep応答をシミュレーションしてみると、下図のような出力となり、\(\%OS=3.96\%\)でまあ良いんじゃないでしょうか。

f:id:syuntoku14:20181213141054p:plain



PD制御器設計

さて、P制御器の設計が終わり、あとはぴたむら君の肩にゲインKを実現するオペアンプによる増幅回路を繋げれば、ぴたむら君は無事\(\zeta=0.707\)となる出力を達成出来そうです。

しかし、注意深いおつま君はこのままでは段審査に受からないことに気がついてしまいました。段審査では、振り下ろす速度も見られるのです。

先ほど求めたstep応答を見てみると、Settling Time \(T_s\)、つまり出力が定常状態に落ち着くまでにかかる時間が1.84秒もかかっています。段審査ではこれが1秒以内でないと落ちることに気がついたおつま君は、PD制御器によってこれを解決することにしました。

\(T_s\)が1秒以内であれば良いので、\(T_s=0.86\)となるような制御器を設計してみましょう。

二次システム\(\frac{\omega_n^2}{s^2+2\zeta\omega_n+\omega_n^2}\)に対し、Settling Time\(T_s\)\(T_s=\frac{4}{\omega_n\zeta}\)で表されることから、\(\zeta=0.707\)かつ\(T_s=0.86\)を実現する閉ループの根は、次図における黒垂直線と黒斜線の交点、\(s=-4.65\pm j4.65\)となります。

f:id:syuntoku14:20181213140913p:plain

この時点で、根軌跡が交点を通っていないことから、いくらゲインKを変化させても、\(\zeta=0.707\)かつ\(T_s=0.86\)となるような出力は得られません。この出力を得るためには、根軌跡そのものを変えなければなりません。

根軌跡そのものを変えるため、おつま君はシステムにPD制御器\(K(s+\sigma)\)追加することにしました。軌跡上の点は、開ループ伝達関数\(G(s)\)に対し、以下の式を満たします。

\[\angle G(s)=\angle \frac{K(s+\sigma)(s+6)}{(s+2)(s+3)(s+5)}=(2k+1)180\degree\]

この式を用いると、以下の図における\(s=-4.65+j4.65\)\(s=-\sigma\)の角度を求めることが出来ます。

f:id:syuntoku14:20181213140958j:plain

上図に置いて\(\theta_\sigma=61\degree\)であることから、\(\sigma=7.21\)です。

よって、\(G(s)\)\((s+7.21)\)を追加した新しい開ループ伝達関数\(G'(s)\)の根軌跡は\(s=-4.65+j4.65\)を通ることになります。

\[G'(s)=\frac{K(s+7.21)(s+6)}{(s+2)(s+3)(s+5)}\]

\(s=-4.65+j4.65\)を通る時のゲインKは

\[K=\frac{1}{|G'(-4.65+j4.65)|}=4.79\]

で求まります。

最終的に、PD制御器は\(4.79(s+7.21)\)となりました。試しにこれを追加したシステムのstep応答をmatlabで見てみると、以下のようになります。

f:id:syuntoku14:20181213141117p:plain

PD制御をした場合のSettling timeは0.78秒程度となり、改善されていることがわかります。

しかし、どうやら%OSが増えてしまっているようです。理論的な設計とのずれの原因は諸々の近似などもありますが、s+7.21を追加したことで、補償されたシステムは二次システム近似が妥当とならないことも原因のひとつです。レポートに書いておきましょう。

PID制御器設計

ようやく審査に受かると思ったぴたむら君ですが、残念、まだ受かりません。よく見ると、step応答の定常値と目標値の間に誤差(定常偏差)が存在しています。つまり、ぴたむら君の剣は最終的に85°に至らず、約75°で止まってしまうことになります。

ぴたむら君は賢いので、PI制御器を追加してこれを解決することにしました。

目標は定常偏差を0にすることです。最終値の定理より、step応答に対する定常偏差\(e_{step}(\infty)\)は、以下の式で表されます。

\[e_{step}(\infty)=\lim_{s\to 0}{\frac{1}{1+G(s)}}\]

このことから、\(e_{step}(\infty)\)が0になるには、\(G(s)\)\(s\to0\)で無限大に吹き飛べば良いことがわかります。つまり、\(G(s)\)が分母にsを持てば良いのです。

単純なぴたむら君は積分器sを先ほど求めた\(G'(s)\)の分母に追加しようとしましたが、おつま君に止められてしまいました。なぜなら、極\(s=0\)を開ループ伝達関数に追加してしまうと、根軌跡が変わってしまい、先ほど設計した\(\zeta=0.707\)かつ\(T_s=0.86\)が大きく変わってしまうからです。

これを防ぐためには、極\(s=0\)の近くに零点\(s=0.1\)を配置し、互いに打ち消し合えば、理論的には軌跡はほとんど変わらず、システムの特性もほとんど変わらないはずです。

よって、最終的なPID制御器は、

\[\frac{K(s+7.21)(s+0.1)}{s}\]

となるんですが、これだと上手く行きません。以下の出力を見ればわかるんですが、これでは、実はs=0付近の閉ループ極の影響が大きすぎ、\(T_s\)が非常に遅くなります。何故これが生じるかというと、ゲインKが約4.5と非常に小さく、閉ループ極が十分s=0.1に近くないためです。

 

解決策としては、①ゲインKを大きくする ②零点の位置を変える

の二つがあります。①はシステムの応答特性を大きく変えてしまうので、零点の位置を変えることで解決しましょう。

f:id:syuntoku14:20181213140524p:plain

最終的に、おつま君はMatlabを使って、零点の位置をいい感じに調節し、零点が\(s=-1.22\)の時に応答がいい感じになることを見つけました。

f:id:syuntoku14:20181213140554p:plain



めでたくそれっぽいPID制御器の設計が出来たぴたむら君でしたが段審査には残念ながら落ちてしまったのでした(悲しいね)。

終わりに

この方法はシステムが二次近似できる時のみ使うことができます。学部時代の期末テストなどはこれが分かってればある程度点数取れるんじゃないですかね(適当)

急いで書いたのでなんか間違ってるかもしれません。ごめんなさい。

その他のPIDゲインの調節方法全然知らないので頑張っていこうと思います。アドベントカレンダー遅れてすいませんでした。

デイビスでの生活と感想(一ヶ月経過)

 

デイビスに来てから約一ヶ月が経過しました。最近日報を書いていなかったので、技術的なことはさておき、ここ一ヶ月の出来事について。

 

ロボット部のマイクロマウスのチームに参加した。メンバーの一人が高校でロボット工学を専攻していたとかぬかすので、何言ってんだこいつってなったが、シリコンバレーに高校があったらしい。ほげえ。日本のマイクロマウスをちょっとだけ見たことがあるが、多分日本のほうがレベルが高い。

 

強化学習のコンペチームに参加している。MARLO 2018っていうコンペ。チームの人がほとんど強化学習初心者。私も初心者。何もわからないので、CS専攻の4年の人と週末に強化学習を勉強する会を開催している。

 

ルームメイトからハッカソンに誘われたので、ノリで申し込んだら通ってしまった。SACHACKSっていうサクラメントで開催されるハッカソン。そもそも一度もハッカソンに参加したこともないので、不安でいっぱい。まあなんとかなるじゃろ。

 

AI部に参加しており、いろいろな恩恵を得ている。来学期から始まる自動運転プロジェクト(4年生用の卒業前にとるやつみたい)に申し込んだら通っちゃった。ただ自動運転を学ぶだけでなく、いい感じだと自動運転をやっている企業にインターンに行けるみたい。センサーフュージョンや自動運転に関するアルゴリズムなど色々学べらしく、非常に楽しみ。強めのGPUとJetsonも割り当てられるらしい。アド。

 

本当なら研究室などに参加して教授と仲良くなったほうが良さそうだが、私の計画性のNASAによって今学期は特にそういうことはない。履修も情報理論とか取ればよかった。履修登録は一番後悔している。 教授とのコネだが、自動運転プロジェクトは教授がサポートについていただけるらしいので、そこで1つは必ず生まれそう。ギリギリセーフ。

 

1,2年で怠けていたせいで結構基礎知識が抜けてるところが多かったので、Courseraでなんとか補填している。 今は信号処理をDigital Signal Processingで復習している。かなり分かりやすいし良い。あと強化学習Practical Reinforcement Learningで学んでいる。なかなか難しいが、面白いので大丈夫。最近になって個人的に最強の勉強法を確立させたので、今度記事にでもまとめるかもしれない。

 

剣道部に参加している。運動は無条件で友達ができるので良い。とても忙しいので二週間に一回くらいしか参加できないが、だいたい皆そんな感じだった。しかし部員は多い。アメリカ人皆侍大好きなので。

 

こっちの学生で勉強していない人間を観測したことがない(私が気づいていないだけかも)。皆すごい勉強していて、自習室は夜中まで勉強している学生でいっぱい。 授業も発言が多い。聞いているだけの生徒があんまりいない。私は聞いているだけで終わった後に質問しに行く人。また、授業外にProblem Solving SessionというTAがひたすら問題を解説する会があり、理解がとても深まって良い。

 

私のルームメイトは夏にNASAインターンをしていたらしく、センサーフュージョンの知見をたくさん持っていてすごいなという気持ち。これで二年生なのが本当にアレ。

 

アメリカ、トイレがしょぼいのホントなんとかして欲しい。ウォシュレットつけて。 マンガONEでこれを読んでいて、日本食を非常に食べたい気持ちがする。早く実家の飯が食べたいが帰りたくはない。

進捗: 2018-10-07

 

8:00~ 起床
10:00~ 散髪
12:00~ Japaneseサークルで焼き肉
15:00~ Rustいじいじ&課題

アメリカの床屋、全てをバリカンで解決しようとしてくるので嫌い。ちょっとだけ切ってくれって言ったのにバリカンを出してきて話が通じねえ。

UCDavisのJapaneseサークルで焼き肉をしたりちょっとしたゲームをしたりした。人間の顔と名前を覚えるのが非常に苦手なので、すでに誰も思い出せない。本当にゴミ。

昨日の夜はルームメイトの友人の誕生日会に誘われたので行ったが、正直赤の他人なので非常にアウェイだった。すぐ帰宅して課題やってた。社交性のNASA

今日はRustをいじっていた。vscodeデバッグ環境と競プロ用のスニペットの登録などをした後、二問くらい解いた。別に何か目標があるわけではないが、何もしないと本当に何もしない人間なので、何もしないよりはマシ。

そろそろ強化学習のコンペ用の申し込みプログラムを書かないといけないし、Samurai用のコードもいじっていかないといけない。あとマイクロマウスもやりたいので、割と死期が近い。

あれだけCourseraやっていて、今日Courseraに英会話を学ぼうみたいなコースがあることに気がついた。アメリカに来る前にやれってn回言ってる。

use std::io;

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>()
        .split_whitespace()
        .map(|e| e.parse().ok().unwrap())
        .collect()
}

fn read_vec2<T: std::str::FromStr>(n: u32) -> Vec<Vec<T>> {
    (0..n).map(|_| read_vec()).collect()
}

fn str_cmp(str1: &str, str2: &str) {
    let vec1: Vec<char> = str1.chars().collect();
    let vec2: Vec<char> = str2.chars().collect();

    let l1 = vec1.len() as usize + 1;
    let l2 = vec2.len() as usize + 1;

    let mut length = vec![vec![0; l1]; l2];

    for j in 1..l2 {
        for i in 1..l1 {
            if vec1[i - 1] == vec2[j - 1] {
                length[j][i] = length[j - 1][i - 1] + 1;
            } else {
                length[j][i] = std::cmp::max(length[j][i - 1], length[j - 1][i]);
            }
        }
    }
    println!("{:?}", length[l2 - 1][l1 - 1]);
}

fn main() {
    let n: i32 = read();
    for i in 0..n as usize {
        let str1: String = read();
        let str2: String = read();

        let str1 = str1.as_str();
        let str2 = str2.as_str();
        str_cmp(&str1, &str2);
    }
}
  • Heiankyo Walking:
use std::cmp;
use std::io;

fn read() -> Vec<i32> {
    let mut string = String::new();
    io::stdin().read_line(&mut string).unwrap();
    string
        .trim()
        .split_whitespace()
        .map(|word| word.parse().unwrap())
        .collect()
}

fn change_matatabi(matatabi: Vec<Vec<i32>>) -> Vec<(i32, i32, i32)> {
    let mut vec = Vec::new();
    for m in matatabi {
        if m[0] == m[2] {
            vec.push((m[0], cmp::max(m[1], m[3]), 1));
        } else if m[1] == m[3] {
            vec.push((cmp::max(m[0], m[2]), m[1], -1));
        }
    }
    vec
}

fn calc_map(matatabi: Vec<(i32, i32, i32)>, map: &mut Vec<Vec<i32>>) {
    for y in 0..map.len() as usize {
        for x in 0..map[y].len() as usize {
            //println!("xy: {}, {}", x, y);
            let mut iter = matatabi
                .iter()
                .filter(|m| m.0 == x as i32 && m.1 == y as i32);

            let mut direction = -2;

            if x == 0 {
                direction = -1;
            } else if y == 0 {
                direction = 1;
            }

            loop {
                match iter.next() {
                    Some(m) => {
                        if direction == -2 {
                            direction = m.2;
                        } else {
                            direction = 0;
                        }
                    }
                    None => {
                        break;
                    }
                }
            }
            //println!("direction, {}", direction);

            if x == y && x == 0 {
                map[0][0] = 1;
            } else if direction == -2 {
                map[y][x] = map[y - 1][x] + map[y][x - 1];
            } else if direction == -1 {
                map[y][x] = map[y - 1][x];
            } else if direction == 1 {
                map[y][x] = map[y][x - 1];
            }
        }
    }
}

fn main() {
    let N = read();
    let n = N[0];

    for i in 0..n {
        let XY = read();
        let M = read();
        let gx = XY[0] as usize;
        let gy = XY[1] as usize;
        let m = M[0];
        let mut matatabi = Vec::new();
        for i in 0..m as usize {
            matatabi.push(read());
        }
        let matatabi = change_matatabi(matatabi);
        //println!("matatabi");
        //println!("{:?}", matatabi);
        let mut map = vec![vec![0 as i32; gx + 1]; gy + 1];
        calc_map(matatabi, &mut map);
        if map[gy][gx] == 0 {
            println!("Miserable Hokusai!");
        } else {
            println!("{:?}", map[gy][gx]);
        }
    }
}

日課

  • [ ] Control Systems の予習と復習
  • [ ] Probabilistic Analysis の予習と復習
  • [x] Lang-8に日記を投稿
  • [x] Rustで競プロ
  • [ ] Algorithms at Coursera

進捗: 2018-10-05

 

進捗: 2018-10-05

08:00~ 起床
09:00~12:00 お絵かきの授業
13:00~17:00 睡眠
18:00~24:00 Rustの練習とか

なんで私はアメリカに来てまでお絵かきをしているのか。でもお絵かきの授業全然課題でないから好き。教授が言っていることは英語力が足りないので大体わからない。

今日のお絵かき:

Blinder Contour Drawingを学んだ。その後インクを使ったお絵かきをした。紙は見ないで、描く対象だけ見て輪郭を描くことで目の動きと手の動きの同期を練習するみたいなアレ。よくわからん。弟は現役の美大生だが私は美術全然知らないので何も分からん。

f:id:syuntoku14:20181005164447p:plain

f:id:syuntoku14:20181005164519p:plain

ルームメイトのResumeを見て、賞がたくさんあってすごいなと言う気持ち。自分のResumeを見てみたが何もなかった。つれえ〜。

AIクラブが強化学習のコンペのメンバーを募集してて、強化学習やっときゃよかったと言う気持ちにもなっている。とりあえず顔を出して強化学習を教えてもらおうかな。

暇な時間をRustで競プロにあててる。暇な時間あんまりないけど。Rustがそもそも難しい。私は何故Rustをやっているのか。

Rustが好きすぎて生徒から文句を言われている教授を見つけた。授業取りたかったが、Rustが難しいので学部から文句を言われて今年からPythonになったみたい。悲しい。

* 入力関係:https://qiita.com/penguinshunya/items/cd96803b74635aebefd6

* タプルのソート:

fn main() {
    let mut tuple_list = vec![(1, 5), (9, 2), (4, 8)];
    tuple_list.sort_by_key(|k| k.1);

    print!("{:?}", tuple_list);
}

* VectorのSum:

let sum: i64 = array.iter().fold(0, |sum, a| sum + a.count);

今日解いたやつ:

* Rummy:

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim()
        .split_whitespace()
        .map(|e| e.parse().ok().unwrap())
        .collect()
}

fn is_same_number(a: i32, b: i32, c: i32) -> bool {
    a == b && a == c
}

fn is_sequense_number(a: i32, b: i32, c: i32) -> bool {
    a + 1 == b && b + 1 == c
}

fn is_good_set(a: i32, b: i32, c: i32) -> bool {
    is_same_number(a, b, c) || is_sequense_number(a, b, c)
}

fn remove_good_set(vec: &mut Vec<i32>) {
    let vecsize: usize = vec.len();
    let mut flag: bool = false;
    //println!("{}", vecsize);
    for i in 0..vecsize {
        for j in i + 1..vecsize {
            for k in j + 1..vecsize {
                //println!("{}, {}, {}", i, j, k);
                if is_good_set(vec[i], vec[j], vec[k]) {
                    vec.remove(k);
                    vec.remove(j);
                    vec.remove(i);
                    flag = true;
                }
                if flag {
                    break;
                }
            }
            if flag {
                break;
            }
        }
        if flag {
            break;
        }
    }
}

fn main() {
    let n: i32 = read();
    for i in 0..n {
        let mut vec_num: Vec<i32> = read_vec();
        let vec_mark: Vec<String> = read_vec();

        // modify the vector for simplification
        for i in 0..9 as usize {
            if vec_mark[i] == "G" {
                vec_num[i] = vec_num[i] + 10;
            } else if vec_mark[i] == "B" {
                vec_num[i] = vec_num[i] + 20;
            }
        }

        vec_num.sort();
        let mut ans: i32 = 0;
        for i in 0..3 {
            remove_good_set(&mut vec_num);
        }
        if vec_num.len() == 0 {
            ans = 1;
        }

        println!("{}", ans);
        //println!("----");
    }
}

* Princess's Marriage

use std::io;

fn read() -> Vec<i32> {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim()
        .split_whitespace()
        .map(|e| e.parse().unwrap())
        .collect()
}

fn main() {
    loop {
        let NM = read();
        let N = NM[0] as usize;
        let mut M = NM[1];

        if N == 0 && M == 0 {
            break;
        }

        let mut DP_tuple = Vec::new();
        for i in 0..N {
            let DP = read();
            DP_tuple.push((DP[0], DP[1]));
        }
        DP_tuple.sort_by_key(|k| k.1);
        DP_tuple.reverse();

        for i in 0..DP_tuple.len() as usize {
            let DP = DP_tuple[i];
            let remain = M - DP.0;
            if remain >= 0 {
                DP_tuple[i].0 = 0;
                M = remain;
            } else {
                DP_tuple[i].0 = -remain;
                break;
            }
        }
        //println!("{:?}", DP_tuple);

        let ans = DP_tuple
            .iter()
            .fold(0, |acc, tuple| acc + tuple.0 * tuple.1);
        println!("{}", ans);
    }
}

* Space CoconutCrab

use std::io;

fn main() {
    loop {
        let mut s = String::new();
        std::io::stdin().read_line(&mut s).unwrap();
        let mut iter = s.split_whitespace();
        let e: i64 = iter.next().unwrap().parse().unwrap();

        if e == 0 {
            break;
        }

        let mut min_ = 1e8 as i64;
        let mut flag: bool = false;
        for z in 0..101 as i64 {
            for y in 0..1001 as i64 {
                let x = e - z.pow(3) - y.pow(2);
                //println!{"{}, {}, {}", x, y, z};
                if x < 0 {
                    break;
                }
                if min_ > x + y + z {
                    min_ = x + y + z;
                }
            }
        }
        println!("{}", min_);
    }
}

日課

  • [ ] Control Systems の予習と復習
  • [ ] Probabilistic Analysis の予習と復習
  • [ ] 英会話の練習
  • [ ] 確率ロボティクス
  • [ ] Algorithms at Coursera
  • [ ] 大学生からの確率統計

Fusion360からURDFを一撃で生成するスクリプトを書いた話

作ったもの

URDFを書くのが最高にめんどくさかったので、Fusion360のモデルからURDFを一撃で吐き出すアドインを作りました。

github.com

詳細や使い方、インストール方法などは全部READMEに書きました。そっちを参照してください(英語ですが、自信が無いです... 誰か添削してくれると喜びます)

吐き出すのは

  • URDFファイル
  • gazeboでシミュレーションするためのlaunchファイル

などです。

urdfだけでなく、確認用にlaunchファイルも吐き出します。

roslaunchすると、Fusionで作ったモデルがgazeboでシミュレーションできます。

例えば、

f:id:syuntoku14:20180920160144p:plain こんな感じのFusionのモデルが

f:id:syuntoku14:20180920160213p:plain

gazeboでシミュレーションできます(やったね)。

もちろんrvizでも確認可能です。

f:id:syuntoku14:20180924085234p:plain

欠点

Fusion側でモデルをきちんと定義しないと正しく動作しません。

例えば

  • リンクを全部コンポーネントとして定義する必要がある(Bodiesで全部保持してもダメ)
  • それぞれのコンポーネントをつなげるジョイントを定義する必要がある
  • revジョイント以外には対応していない

など、Fusion側での調整が必要になります。まあそれでもゴリゴリURDFを書くよりはずっと楽な上、デバッグも楽になると思うので...

コンポーネントさえきちんと定義すればちゃんと動作するはずですが、まだバグがあるかもしれません。 何か問題があればissueを投げてくれると泣きながら喜びます。スターをくれると更に喜びます。

Fusion360のアドインの開発について

公式のwebページ以外、書籍やネットにあんまり情報がない気がします。特に日本語の情報は全く無いんじゃないかな。とりあえず公式のリファレンスをひたすら参照しました。

公式のリファレンス:

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-A92A4B10-3781-4925-94C6-47DA85A4F65A

http://help.autodesk.com/cloudhelp/ENU/Fusion-360-API/images/Fusion.pdf

そもそも私がFusion360を触ったことがなかったので、結構苦労しました。 はまりかけたこと:

  • occuranceとcomponentの違いに気をつけよう。occuranceがオブジェクトでcomponentがクラスに当たるのかな?
  • モデルの原点に気をつけよう。モデルの原点がいちいち異なってるとURDF的に正しく計算されないので、スクリプト上では一回全てのoccuranceのコピーを作成し、モデルの原点をworld座標系の原点と合致させている。
  • コンポーネントの作り方:
occs = allOccs.addNewComponent(transform)

ただし、この書き方で返ってくるのはComponentではないので注意。

  • 対応していないライブラリを入れるやり方が分からん。numpyなど入ってないので、行列演算はnumpyでできない。Matrix3D.transformByなどで出来そう。要調査。
  • ジョイントはrootcomponent.jointsに入っている。回転をrevoluteにしているなら、jointsのjoint.jointMotion.rotationAxisVector.asArrayで回転軸が得られる。
  • URDFにおいて、Inertiaは全部重心を原点に計算している。APIでは重心原点のInertiaを返す関数がないので、(returnValue, xx, yy, zz, xy, yz, xz) = physicalProperties_var.getXYZMomentsOfInertia()で得られた値を平行軸の定理と"Parallel plane theorem"(和名はわかんないです)を使って重心原点のものを計算しよう。 式はここに載っているので: Parallel Axis And Parallel Plane Theorem

  • physicalProperties_var.getXYZMomentsOfInertia()だが、多分公式のリファレンスが間違っている。返り値が(returnValue, xx, yy, zz, xy, yz, xz) と書いてあるが、計算してみたら返ってきた値は(returnValue, xx, yy, zz, xy, xz, yz)だった。むかつく。