机上の空論主義者

-♰- 有言不実行の自身をブログ名で戒めろ -♰-

NAISTを修了しての振り返りと、NAISTでの活動の特徴考察

こんにちは。
恐ろしいことに、明日から社会人です。なんか頑張ります。

f:id:ume-boshi:20220331195609j:plain:w450
霧立ちこめしNAIST刑務所

そう、私の大学院生活ももう終了(修了)しました。2年間、奈良先端科学技術大学院大学で生活をしていたわけですが、ただ目の前のタスクを片づけていたら終わってしまったという印象です。博士前期課程の学生なんて皆同様に、ただ「忙しかった」と結論付けられるでしょうけど。ただ感想を述べるだけではつまらないので、自身のNAISTでの生活を振り返りつつ、活動の印象についてまとめていけたらと思います。




そもそもどんな大学院生活を送ったか

まず、修士2年間でどのような生活を送ったかをまとめていきます。大したことを書いていないので、時間が無い人は本章を読み飛ばして下さい ;)

修士1年 前半

M1前半は、授業・修士研究テーマ決め・学部時代の論文執筆・就職活動をする必要がありました。活動量が一番多く、肉体的に疲れたのはこの時期だと思います。

NAISTに入って最初に待ち受けているのは、当然のごとく授業です。NAISTでは英語で授業が進められ、その内容は学部時代に情報系学科の成績上位だった私にとっても難しいものでした。さらに、M1前半にすべての授業単位を取得することができ、それに挑戦した私は課題の多さに睡眠を削ることになります。特に、コロナ禍の始まりに入学したため、授業の出席点や筆記テストは1つもなく、課題の提出により成績評価されたため、その量は例年とは比べ物になりません。

学部4年の半年近くを利用して大学院受験し、研究室を変えているため研究活動を疎かにすると「苦労してNAISTまで入学した意味があったのか?」という疑問に苛まれることになります。これは大学院2年間を通して常に付きまとう問題でした。ともかく、1年目は研究テーマを確立・安定化して、同期よりも少しでも進めたいという思いがありました。そのため、研究テーマ決めや論文調査にも力を入れて、常に気を張っていなければなりません。アイデア出しが好きな私には、この時間は苦しくもなんともなかったのですが、それらに慣れていない同期は大変苦労していたことを覚えています。

上記2つの研究活動に加え、同時並行して学部時代の研究論文を3本も執筆していました。これは大学院受験の影響で卒業研究が後ろ倒しになったこと、単純に自分自身で論文を書き上げたかったこと、また、技術雑誌からの寄稿依頼という名誉なことだったために取り組みました。卒論の短縮 + 作業分担などはしていたため、死ぬことは無かったのですが、NAISTでの活動に追加して好きでやっていたことなので、忙しくても文句は言えず歯を食いしばりながら堪え抜いたことが記憶に残っています。

最後の就職活動ですが、上記のことがある中で時間を費やせなかったことは想像に難しくないでしょう。私は就活として逆求人イベントへの参加と、5社程度のインターンシップへ応募したことくらいで終了しました。幸いながら、学部時代からの成果が認められて書類選考には優位に働き、人より苦労は少なかったです。無事にソフトバンクの4週間の現地インターンに通過し、コロナで中止になり、どこのインターンシップにも参加しない人になりました。


修士1年 後半

M1後半は、修士研究と研究テーマ変更・就職活動・個人開発をしていました。個人開発が出てきたということで、少し余裕が出てきたわけですね。しかし、修士2年間を通して、最も精神的に疲弊した時期でした。

修士研究は、遠回り遠回りしながら実装を進めていました。1つひとつ自力での実装することをこだわっていたため、研究には本質的に重要ではない箇所に時間をかけて、先生に「要らぬ」と言われ、途中まで作りかけたものを幾度も手放していました。挙句、研究テーマと同じような実装をしているアプリケーションを見つけてしまい、研究テーマを1から再検討した次第です。結果、M1の研究は、力を入れて活動していたものの、無に帰したのでした。あるある。

研究テーマが無に帰して精神的に参っている中、病みイベントと名高い就職活動も進めなければなりません。私はソフトバンクの早期選考を最終面接で落とされ、6社のお祈りメールを乗り越えて、7社目でようやく内定を頂きました。書類選考にはすべて通っていたので、面接で落とされすぎて自分自身のコミュ力や経歴を疑いましたね。開発歴などに強烈な自身があったため、絶対に嘘をつかない方針を貫いていました。しかし、SPI試験を複数人で不正しながら問いたり、嘘をつきながら内定をもらう人を見ながら、何が正しいか疑い悩み病んでいました。

個人開発はFFKBであったり、webサービスも実装したり、新しい基板の設計もこの時期にしていました。なんだかんだ余裕があったと言えます。研究活動でくすぶっていた分、その他では活躍しようと意地でも働いていたのでしょうか。

ume-boshi.hatenablog.jp


修士2年 前半

M2前半は、修士研究・学会発表準備・個人開発・ブログ1か月連投チャレンジ・親知らず3本同時抜歯などをしていました。

M2前半では、研究活動が軌道に乗ってきたこともあり、そちらの被験者実験を主軸にバリバリと実装を進められました。投稿する学会と被験者実験内容が決まっただけで、ずいぶん事を運びやすくなるものです。活動しやすさから、肉体的にも精神的にも余裕がありました。実験もしていない状態で、論文執筆を無理な締め切りで応募しても、無理をすれば何とかなる。

研究は軌道に乗っていましたので、就職したらできないことをを今年度中に挑戦しようと、ブログ1か月連投チャレンジと抜歯(全身麻酔のため1週間入院)をしていました。本ブログは一応、技術ブログですので、1か月連投しようと思うと必然的に個人開発や専門知識獲得をすることになり、躍進の1か月も過ごしたとも言えます。ただ、記事連投チャレンジ中に祖母が亡くなり、不慣れなイベントが続いたりスケジュールが狂ったりと、ブログを書く意欲をここで無くしてしまいました…
ともかく、研究も個人開発も進められ、文武両道?できた期間でした。

ume-boshi.hatenablog.jp ume-boshi.hatenablog.jp ume-boshi.hatenablog.jp


修士2年 後半

M2後半は、修士研究・個人開発・次年度準備をしていました。普通に、当然のごとく忙しかったです。

国内学会後に様々なやる気を失ってダラダラ生活していたら、いつの間にか研究活動がマズい状態に突入しました。その上、次にどんな被験者実験をすれば良いかは私の研究において最大の難題であり、修論をどうまとめ上げるかを含めて検討しなければなりませんでした。その後は実験設計を短期間で行い、実験用システムの実装をほぼバグが無い状態に仕上げることに注力。さらに、被験者実験を採用したことで大量の被験者実験をし、期間も2週間以内で密にタスクをこなしました。そして取得データの分析と修論執筆、修論発表を着々と進める日々。。。いつの間にか、M2後半が終わっていました。

実験データを集めたりデータ分析を進めると同時に、忙しさを忘れて気まぐれで応募したQiita Advent Calendarの記事を書き上げるために四苦八苦しました(なぜ4記事も応募してしまったんだ…)。各ネタもないのに、自分で計画を忙しくして馬鹿の極みです。 ume-boshi.hatenablog.jp ume-boshi.hatenablog.jp

修論発表が終わってからは、すぐに内定先の手続き・引越し準備・実家の部屋を引き払い・車の売却・研究の引継ぎと、大忙しの日々を過ごしました。


つまりどんな生活だったか

振り返ってみると、他大学院進学後に遊び惚けることは困難でした。

  • M1前半は、肉体的疲労が強く、人によっては環境変化で病む人も出てくるでしょう。
  • M1後半は、精神的疲労が強く、時間的な余裕が少しだけできます。
  • M2前半は、研究テーマさえ確立されていれば、肉体・精神・時間的に余裕があります。色々ガッツリ進められるでしょう。
  • M2後半は、時間的余裕が無くなり、研究活動に肉体的・精神的にも披露する期間といえるでしょう。どれだけコツコツ進められる人でも等しく忙しいと思います。

自身の研究内容は、まとまった結果が出るまで学会発表がしづらく、どうも研究で満足できる発表成果は残せませんでした。その分、娯楽として個人開発にも力を入れたとも言えます。もし、国際学会やジャーナルへの投稿を目標として研究活動をより真面目に行うのであれば、休んでいる暇はほとんどなく、QoLも極めて低下していたでしょう。




他大学院進学の印象

これまで、個人的にNAISTでの活動を振り返り、その特徴について述べてきましたが、一個人の生活内容には需要が無いと思います(なぜ書いた)。

なのでもう少し一般的な目線で、NAISTでの活動の特徴や印象をまとめていこうと思います。

研究スケジュールについて

〇 モチベーションが高い人が多いため、常に若干焦りながら活動可能

様々な大学からの優秀近い学生がNAISTには集まってきているようで、研究活動を真面目に行っている人は当然多いです。全員が大学院受験していることからも、当たり前の態度だとわかりますね。さらに技術的に優れた人が多く、モチベーションと相まって定期的にちゃんと進捗を出してきます。私が焦って負けじと研究を進めようと思っても、隣の芝生は青く見えるようで、周りも焦って研究を加速していきます。

同期だけでなく、先輩も後輩もすごい人ばかりなので、焦りを抱き続けるのは簡単です。まともな人であれば、この環境では自然と研究成果が生まれてくるでしょう。

〇 研究時間のやりくりが容易

研究室にも依りますが、情報系ではコアタイムが無い研究室が多い印象です。私は完全に昼夜転回型の生活習慣でしたが、深夜に大学に行っても誰か居たり、深夜に大学から帰るときには大量の研究室で電気がついているのが見えます。これは、徹夜するほど忙しいのではなく、スケジュール的に自由が利く研究室が多い証拠でもあると思います。

私が個人開発やブログ1か月連投チャレンジをしたり、適当な時期に入院できたりしたので、やる気に波がある人でも長期的には上手く活動できる環境です。

〇 学内でのバイトが多くあり小遣い稼ぎ可能

学内バイトは他大学でもあるでしょうが、NAISTではその頻度が多いと思われます。というのも、よくある授業TAやRA(Research Assistant)、共同研究の補助に加えて、サマー/スプリングセミナーやインターンシップでバイト代がもらえます。これは、NAISTに入学する前の学生さんが、研究室に仮配属して雰囲気や研究対象などを知れる仕組みなのですが、長いときには1か月くらい来訪があるのです。また、オープンキャンパスや学外イベント補助などの活動でもバイト代がもらえたりします。

がっつりバイトできない環境下では、こういった小遣い稼ぎはありがたいものです。

× 授業課題の多さや就活の必要性から、研究時間が少ない

一般的に、他大学院への進学をすると授業課題については、どれだけ努力すれば単位が取れるかの基準が変わるため手抜きできない上に、院進≒上位の学歴 であることで、課題の難易度や量が増えるでしょう。就職活動では、進学後の研究テーマや成果が安定していない中で、他の研究が変わっていない学生たちと戦わなければなりません。まとまった研究成果を就職活動でアピールしづらい傾向があり、開発経験が深く豊富に無い人は就職活動で苦労すること間違いなしです。

わざわざNAISTに進学したくせに、研究活動に時間を費やしきれないのは無意味で致命的な問題です。修士卒で就職するのであれば、研究活動に満足な時間をかけられない可能性があることを必ず覚悟しましょう。

× テーマ決めが遅いと、残せる研究成果が小規模化

大学院受験して専門分野をずらすということから、新しく修論研究テーマを検討するための知識量が不足し、右往左往しがちです。私の同期は、M1のときに決めた研究内容が、修論内容に直結していたのは8人中3人だけでした。研究テーマの決定が遅れれば研究期間が短くなり、成果も小規模化します。自分で研究内容を決めない場合はもっと安定して進められますが、それはそれで面白くない。




生活の印象について

〇 娯楽が研究寄りになり、成果としても消化可能

NAISTの研究は、専門外の分野を傍から見ても面白いです。研究内容を派生して、色々試しているだけでも楽しいですし、研究チックにも変えられます。個人開発が好きな人であれば、自身の専門性と研究室の専門性を少し絡めるだけでも、自然と娯楽が研究化可能です。実際、私が趣味で作ったFFKBや群ロボットは、やる気が持続して入ればHCIやロボティクスの分野で研究化しようと思っていました。この ↓ 記事の内容も、修論研究のちょっとした派生です。

ume-boshi.hatenablog.jp

分野に依るのかもしれませんが、一応 "先端" 的な技術から派生させるので、少ない苦労で新しい研究が生まれやすいのでしょうかね。

〇 金銭感覚がバグるほどの研究費

NAISTの研究費利用の審査がザルなのか、新しい機器をバンバン購入してくれます。1台100万円近いデバイスを突然4台買ったり、45万円くらいの某ARグラスを学生1人ずつに行き渡るように大量に保持していたり、謎に光熱費がかかりまくる施設で1人が研究するためだけに毎月数十万支払ったりと、とてつもない懐の広さを目の当たりにしてきました。ちなみに、世代によっては、大学からMacBook Airが貸与されたりもしました。
今年度に限っては、研究室の環境改善費用として各研究室に200万円が支給され、空気清浄機やyogiboや昇降机などが大量導入されたりしました。

先端恐怖症になってもおかしくない。

〇 国際交流した意欲が湧く

NAISTは外国籍の留学生が多く、日本人:留学生=7:3くらいの比率でいます。単純に言語が違うだけならいいですが、多言語を扱える人がほとんどで、技術水準も高く、休みなく永久に研究を続けています。比較的、中国国籍の方が多いのですが、必ずと言っていいほど英語か日本語を話せて、そのどちらかでミーティングに参加できるほどです。その人たちが寮生活していることも相まって、日本で時々観光する以外は毎日研究室に来て活動しているのです。これは日本の大学で日本人よりも留学生の方が成果を残しうるという恐ろしい現象を生んでいる気がします。

ともかく、留学生の水準が高さに感化されて努力しなければと思うとともに、文化交流を図りたいなぁと意欲が湧いたものです。コミュ障なのであまり話せませんでしたが…

× 娯楽施設が少なく交通の便が悪く、研究と娯楽のメリハリが付けづらい

娯楽と言えば、カラオケとかボーリング場、ラウワンとかを想像するかもしれないですが、そもそもまともに外食できる場が徒歩圏内にありません。大学の学食は、自称グルメの同期からはマズいと評されており、外食先は決まって車で10分ほどのラーメン程度です。外食するためには、誰か車所持者が犠牲となって研究活動を止めて運転手になる必要があります(まじウザかった)。運転手のスケジュールと気分が合わなければ、外食の話も当然無しになります。
その結果、最終的に行き着く先は、事前にお弁当を買っておいてそれを研究室で食べるスタイルです。これでは研究と娯楽のメリハリもへったくれもありません。

個人的には、NAISTの学食で売っている幕の内弁当はおいしいのですが、舌が肥えた人には耐えられないのでしょうね。




まとめ

さて、ここまで長々と語ってきましたが、NAISTは研究組織として優れていることが伝われば幸いです。ただしその長所も、日本の就活システムと2年間という時間的制限、精神的負荷などの問題で、うまく活用しきれないことがあることも知らなければなりません。

これからNAISTや他大学院進学を検討する方は、就職活動や研究速度、自身の趣味などのどこに重点を置くかを綿密に想像しておきましょう。苦労して大学院進学したのに、何も為せない無駄な期間とならないことを願っています。



それでは。

f:id:ume-boshi:20220331195113j:plain:h450
NAISTよ、なんだかんだ楽しかったぞ。ありがとう。

年度末処理が忙しいなかの、梅観光による安寧

こんにちは。ume-boshiです。

修論発表が終わったら、就職前の休暇が得られるんじゃないかと期待していたのですが、どうにも忙しい2月を過ごしていました。

2月は修論発表に始まり、引っ越し手続きで10日間ほど奈良と東京を行き来していました。さらには、研究のデモ用システム実装や国際学会用の論文を執筆したり、自動車の売却手続きをしたり、実家の部屋の明け渡し用に掃除をしていました。

忙しい忙しいとは言いつつも、だらだら活動しているから悪いのであって、きびきびと活動と休暇を繰り返していれば苦労しないはずです。そんなこんなで(は?)、先日、大阪城公園に梅を見に行ってきました。


梅の時期の大阪城公園には、できるだけ毎年観光しています。理由は単純で、高2の時にはじめて観光したときに見た梅が、めちゃくちゃ綺麗だったからです。しかし、それ以来の観光では毎回開花時期が微妙にずれてしまい、満足いく観光はできていませんが。。。


今年度の写真

私は高校時代3年間写真部に入っていたのですが、大学以降はほぼ写真を撮りに行くことなく過ごしていました。ずいぶんヘタクソになっていますが、よく撮れたものだけ乗せたいと思います。

f:id:ume-boshi:20220228014435j:plain:h450
南高梅です。

f:id:ume-boshi:20220228014509j:plain:h450
その日のBest Shot?

f:id:ume-boshi:20220228014620j:plain:w450
ありがちな大阪城shotも載せておく



高校生全盛期のBest Shot

今年度の写真はすこし見苦しいので、全盛期の写真も貼り付けておきます。当時、HDR写真にぞっこんだったので、2枚目はチカチカしてますね。3枚目もHDR写真ですが、歴史モノのゲームに出てきそうなくらい怪しい雰囲気を醸し出していて、僕は好きです。

f:id:ume-boshi:20220228015232j:plain:w450

f:id:ume-boshi:20220228015652j:plain:w450

f:id:ume-boshi:20220228015658j:plain:h450



おわりに

このブログ、なんとなしに毎月投稿を続けていましたが、2月は技術記事を書くネタもなく、面白いエッセイ的記事も書く気力がない状態でした。
とはいえ、連続記録が途切れるのはなんだかもったいないので、一番雑な記事を上げてしまいました。罪悪感。

来月からは、気合を入れてまた頑張りたいです。。。

audibleが聞き放題になったので、実用的で激面白いことを伝えたい

こんにちは。

修論発表に追い込まれているべきはずなのに、こんなブログを書いているume-boshiです。家族にブログがバレました。はいはい、オワタオワタ。

さて、今回はaudibleという「本を聴くことで読書」できる、amazonが提供するサービスについて紹介したいと思います。 audibleを知らない人、もしくはaudibleを知っているものの利用したことがない人向けの記事です。

f:id:ume-boshi:20220129225949p:plain:w450
サムネ


概要

audibleは、有名な本をプロの声優さんの朗読で読みすすめられるサービスです。audibleはamazonのサービスではあるものの、定額サービスを利用するためにはamazonのprime会員などとは別に、audibleだけに月額1500円のサブスクリプション登録をする必要があります。サブスクに登録すると書籍が聞き放題になり、さらにaudible特製のポッドキャストをすべて無料で聞くことができます

audibleの中には、1冊で6000円を超えるような大作が存在しており、そのような本をたった1500円で何冊も手に入れられることになります。聞き放題になったので、書籍が面白くなかったり、声優が自分に合わなかったり、もう一度読み返すほどのものではなかったりしたときには、すぐに別の作品に切り替えることができますし、お金や時間の無駄も発生しません!
ただし、すべての書籍を聞き放題というわけでは無いようで、一部の書籍で追加購入コンテンツな位置付けが新しくできたようです。例えば気になっていた本で言うと、「逆ソクラテス」や「起業のすすめ」、「The Four(日本ではGAFAの本?)」とかですね。聞き放題対象外である条件はわかってないです。(個人的にはコイン制は制限が無かったからそっちのほうが良かった

気になる朗読の内容ですが、基本的には各書籍につき声優さんは1人だけという体制が取られています。そのため、大抵のストーリー形式の書籍では、男性キャラも女性キャラも1人が演じる体制です。プロの声優さんを起用しているため、誰が何を喋っているかはちゃんと区別がつくようになっています。さすが😎 (男性声優が女性役をするときにはオカマっぽくなりますが)
1冊あたりの時間は平均7時間ほどであり、1冊の本を1か月かけてのんびり聞く感じになります。
書籍には、内容をわかりやすくするための図表が各所に散りばめられていますが、アプリからその内容を確認することも可能です。まあ、大抵は面倒くさいのでイメージで補うのですが。


読破経験

記事用に一瞬使っただけではないことを伝えるために、私がaudibleで読んだ書籍を紹介しておきます。特別面白かったものには⭐を付けました

  • ISSUEから始めよう
  • シン・ニホン
  • 人生は楽しいかい?
  • ビジネスエリートになるための教養としての投資
  • リーダーの仮面
  • 超雑談力
  • ⭐⭐夜は短し歩けよ乙女
  • 絶対に面白い化学入門 世界史は化学でできている
  • 世界のエリートがやっている 最高の休息法ーー「脳科学×瞑想」で集中力が高まる
  • 営業の魔術 この魔法を手にしたものは必ず成功する
  • 「不連続な変化の時代」を生き抜く リーダーの挫折力
  • (途中)史上最強の哲学入門
  • (未読)嫌われる勇気 ー-自己啓発の源流「アドラー」の教え
  • (未読)1兆ドルコーチ シリコンバレーのレジェンド ビル・キャンベルの成功の教え



手に入れて返品したのは下記の4つです。

  • ホモ・デウス(上巻) ← 話し方が宗教っぽく聞こえて返品
  • モモ ← なんかダラダラして聞こえたので返品
  • 夢をかなえるゾウ ← ゾウがうざかったので返品
  • 「後回し」にしない技術 「すぐやる人」になる20の方法 ← 聞かずに返品




1年以上使用して感じたメリットとデメリット

メリット

  • 頭に入りやすい
  • 読書と違い、完全に手を使わなくても読める
  • 物語形式で進められる実用書には強く引き込まれる
  • 声優の語りが上手いので、つい聞き入れる
  • 本を無限に読めるという、お得すぎる満足感

  • 熱などでぶっ倒れているときの暇つぶしになった

  • 運転中などの隙間時間にも読めて無駄がない
  • データ量が意外に小さい + ストリーミングできるようになった

デメリット

  • 一部の声優さんに抵抗がある場合に聴いてられない → 聞き放題で無問題に
  • 図表などの資料がすこぶる見づらい
  • メモを取りながらの勉強向きではない(?)
  • 特定の箇所を読み返すのくそ面倒
  • 車で流したら家族に引かれた。
  • ポッドキャストは聴かないかな
  • 話においていかれたらやる気無くす
  • 1冊あたりの時間が長いので音を聞き続ける習慣がある人しか使えないかも




おすすめの書籍紹介!

自己啓発 + 物語系

まず、私がaudibleの良さが色濃く出ていると感じた「自己啓発 + 物語」系の書籍を紹介します。まず、書籍リストを上げましょう。

www.audible.co.jp

www.audible.co.jp

www.audible.co.jp

www.audible.co.jp

自己啓発書は大体どれも「こうすれば成功できる」と文章で書いてあるだけであり、便所の落書きと一緒でありがたみが感じられませんでいました。ところがaudibleでは、声優さんが話すことでキャラクターの言動に感情移入しやすい状況下で、はじめは能力が低かった主人公と優しい指導者との会話を聞き進めていくことになります。そうすると、字面では影響を感じなかったアドバイスの言葉に、「なるほど!勉強になります!」としっかり啓発されてしまうわけです。

「人生は楽しいかい?」のゲオとあんちゃんの楽しそうな掛け合いを聞いているだけで、自己啓発に関係なく楽しめますし、「営業の魔術」の主人公が初めて契約を取れたときには普通に感動して泣きました。
あと、3つ目の本については、「最近よく聞くマインドフルネスって胡散臭い...」という我々と同じ視点から物語が始まります。みるみるマインドフルネスに助けられていく登場人物たちを見て、安全で有益なものだと思い知らされたりもしました。
4冊目は感情移入しすぎて、ゾウのガネーシャの奇行に腹が立って読むのをやめました


がっつり物語系

続いて、声優さんの語りが良すぎた作品です。

www.audible.co.jp

audibleの物語系がどんなものかと、モノは試しに気まぐれに購入してみたところ大当たりでした!
この本は主人公の独特な堅物による語りで進められ、おもちろい個性的なキャラクターが登場し、奇想天外な事件に巻き込まれていくものであり、audibleじゃなくてもきっと面白いのでしょう。ただ、声優さんの実力あって、主人公の女の子がめちゃくちゃかわいい路傍の石ころに甘んじていた男主人公が、緻密にのろのろ距離を慎重に縮めたくなる理由もわかります。自分が男主人公の親友のような立場にあるように感じられ、ずっと応援していました。

audibleを始めて使い始めるという人には、本当にお勧めの作品です。


日本人は全員読め

タイトルの通りです。技術後進国に成り下がってきた日本にはどんな敗因があって、どんな長所を持っているから今度こうすれば発展できるよ。という重大なことが書かれた本です。全政治家にレターパックで送りつけてやりたいぐらい、日本の未来を考えて行動したくなる本です。読め~~~~

www.audible.co.jp


教養になる系

「時の偶には本で勉強でもしようか」と思って本を買っても埃がかぶるまで放置している皆さん、こんにちは。audibleでは、そんな本も興味ない部分は時間が経てば勝手に終わっているので、読み切ることができます! さて、そんな教養になる系の本でオススメを上げておきましょう。

www.audible.co.jp

www.audible.co.jp

「化学」や「哲学」は興味はあるものの、いざ読もうとしたら寝てしまう分野として有名ですよね。上記に上げた本は、どちらも少し難解な内容ですが、興味がないところは頭に入って来ず、無視できるので読み終えることができます。そして普通に書籍の内容も面白いので、BGMとして流すつもりだったとしても、ちゃんと聞き入って教養になるでしょう。




amazonが顧客をつかみ取る秘訣

audibleを体験するにあたって、新規顧客を固定客にするための秘訣がいくつも込められているように感じました。

音声で読書をするという新しい概念について、利用者がそれに慣れるまでには少しの時間が必要です。特に、始めからよい本に巡り合えなければ、新規顧客は離れてしまうでしょう。そんなときに、audibleは下記のことをして新規顧客を固定させる策略がありそうです。

  • 複数の本を最初から手に入れられる
  • 3か月の半額期間!
  • ゲーミフィケーション(ときどき称号がもらえる)
  • // 休会(新しい運営体制でどうなっているか不明)




おわりに

私はaudibleを聞く以前、ラジオ(ANN)を聴く習慣があり、結構時間を無駄にしている感覚がありました。そのラジオ感覚で本を読めたら教養が少しは見につくだろうとaudibleを試したところ大正解。今ではすっかり生活に根差しており、非知的な手作業をしているときは良く聞いています。コイン制だった当初、書籍選びは賭けみたいなところがあったため、良い作品にたどり着けるかすごく不安に購入していました。今じゃ聞き放題なので、そんな心配もいらないですね!
本記事でおすすめの書籍も紹介しましたし、試しに何かを聞いてみたいという方はぜひご検討くださればと思います!

では。


聞き放題以前のaudible

本当は聞き放題になる前に投稿しようと思ってた本なので、聞き放題になる以前の状況を書いていました。せっかくなので文章で残しておきます。 聞き放題になる以前は、毎月1コイン(1冊を自由に購入できる権限)が支給されるほか、毎月特定の1冊(全会員が固定の本)を無料で手に入れられるという特典内容でした。毎月得られるコインでは、ストア内のあらゆる書籍が購入可能でした。また、コインで購入した書籍は一定回数は返却可能。そのため、所持できる本の冊数は固定でしたが、現状と同様にすぐに別の作品に切り替えれた感じですね。

おしゃれな家庭栽培キット「eggling」のための自動水やり装置を作成したよ ;)

こんにちは。

この記事は、M5Stack Advent Calendar 2021最終日25日用の記事です!

今回は、クリスマス + 自身の誕生日ということで、なんかおしゃれに光る系のシステムを1日で作ってみました!

今回対象とするのは、「eggling」という卵の形をした家庭栽培キットです。loftをブラブラ散策しているときに見つけて、面白いアイデアだと大興奮して購入しました。僕が購入したのは食用のバジルの卵です。

f:id:ume-boshi:20211225144643j:plain:w500
バジル栽培用eggling

ところが、私は植物の育成ができません。以前豆苗を栽培してみたときに、水をときどき交換してあげるだけで十分なものを ↓ この状態にまでしました。なんで死んでしまったん...?

f:id:ume-boshi:20211225144954j:plain:w500
しおしお豆苗




egglingの育成方法について調べてみたところ、どうやら①土が乾かないように水やりをすべきこと、②が適切に当たる環境下にあるべきらしいです。

mabomabo-yakata.com

は?こんな私がまともにegglingを育成できるとでも? つまりが、全自動化された育成マシンを作成する必要がありますね(達せていないが)。




システム概要

egglingへの自動水やり + 光照射マシンを作成したいと思います。組み込み界隈では擦られまくっている水やり装置ですが、既存のシステムをはるかに下回るシステムを作り上げましたよ! 誕生日なので雑なのは許してください。

以降の節からは、開発の過程をじゃんじゃん載っけていこうと思います :)

①egglingを割る

ここが今回の開発で一番楽しかったところです。卵の頭を固いものでカチ割ってあげることで、土が露わになります。この殻の割り方は個々人で好きな形に調節できます。

中にある紙帯は種子が入っているものみたいです。egglingの製品には、付属の種子もちゃんと用意されているため、やり直しも効くため安心感がありますね。

f:id:ume-boshi:20211225164418p:plain
卵割り。結構硬い。


②水流の制御

まず、水やりのためにペットボトルから水を運びだそうと考えています。そのために、まずペットボトルのキャップにチューブを取り付けます。今回はグルーガンで仮止めし、のちに木工用ボンドで補強しました。

f:id:ume-boshi:20211225163808j:plain:w500
キャップに穴をあけ、チューブを取り付ける

チューブを介して水を運びますが、その流水の制御のためにチューブを折り曲げる方法を用いました。
左のように折れ曲がっていないときは水が流れ、右の折れ曲がっているときは水が流れません。

f:id:ume-boshi:20211225163240p:plain
チューブを折り曲げることで水流を止める

↑ の状態を作り上げるために、サーボと針金を用いて引っ張る仕組みを作りました。サーボが回転すると針金が引っ張られて、うまいこと水流が制御できます。

f:id:ume-boshi:20211225164142j:plain:h400
割りばし細工


③水が溜まったことの検知

卵は外皿の上に置いた状態にあります。この際、適した水量は、外皿に水が溜まるくらいのようです。

ということで、水が溜まったことをセンシングしなければなりません。世の中には専用のセンサがあるのでしょうけれど、所持しておらず購入するお金もあまりない貧困学生なので、すでにあるものを組み合わせてセンシングしようと思います。



ということで、いつもながら適当なセンサを作ります。
使用するのはフォトリフレクタとティッシュ、黒い紙です。まず、黒い紙をティッシュにくるんでセロハンテープで貼り付けます。

ティッシュは基本的に白ですが、水を灌ぐことでティッシュ内の黒紙が透けたり、生地による光の散乱が生じることで反射率が低下します。そのため、水が溜まっていない状態では画像左のように電位が高くなっており、水が溜まってティッシュに浸透した状態では画像右のように電位が低くなります。

f:id:ume-boshi:20211226132202j:plain
ティッシュの濡れによる水のたまり検知


④光の照射

種子が発芽するまでは、室内でよく光が当たる環境に置くべきであり、発芽後も日光下に置く必要があります。最近では、家庭菜園としてLED光を照射することで発芽後も育成できていますよね。

ということで、本システムでも同様に発芽後まで使用できるようなLED光をegglingに照射してあげます。その際、クリスマス + 自身の誕生日を祝うためにイルミネーション風に光らせてあげようと思います。



LEDの照射光を決定するために参考にしたのが下記のサイトです。
この記事で引用している内容によると、LEDでの育成をする場合の色は下の条件が実験的に良いといわれているようです。

論文では赤+青に24%まで緑の光を混ぜると生育が良くなる。 しかし、50%以上、緑を混ぜると生育が悪くなる。

bambooborny.hatenablog.com



そこで私がegglingに照射する光も、赤100%+緑25%+青100%の頻度にしました。実現のためのLEDには、マイコン内蔵であり1本の信号線で大量の素子を制御可能な、WS2812BのLEDテープを採用しました。

www.amazon.co.jp



これを3個ずつ同じ色で、ランダムに点灯しました。さらに、イルミネーション風にするために、1/5の割合で時々LEDを消灯してあげました。

写真で見てみると、光がすべて加法されてしまい、怪しいマゼンダ色になっていますね。肉眼で見ると、ピンクでない色も多少は見られますが、大体同じ色に見えています。

f:id:ume-boshi:20211226142257p:plain
LEDテープのイルミネーション的配色

f:id:ume-boshi:20211225161745j:plain:w500
怪しい雰囲気を醸し出す卵バジル


⑤システムの統合

ここまでに紹介した実装内容をベースとして、システム全体を統合して作り上げました。それが ↓ の写真になります。上から針金でつるしたペットボトルに水が入っており、そこから下にあるegglingに水を注いでいきます。その段階で、②で作成した水流制御の仕組みが動作するわけです。水がegglingに注がれて浸透し外皿にたまると、③のケースに張り付けたティッシュ濡れセンサが反応し、水流を止める判断ができるわけですね。最後に、④で作成したLEDを照射してあげることで、家庭内菜園が可能となります。シンプルながら完璧な挙動をするはずです!

f:id:ume-boshi:20211225161526j:plain:h500
統合した結果。めちゃくちゃ。


完成品動画

そして完成したものが ↓ この動画(1:40ごろから)になります。初めての水やりは、カメラから見やすいように光を照射せずに行いました。

youtu.be

結果として、一番初めの水やりでは浸透が非常に遅く、上から溢れ出てしまうという結果となりました...
まあ、正常にセンシングができて水流の制御も動作しているようで、種が流れ出さえしなければ許容範囲かなと思っています(この適当さじゃ豆苗も枯らすわな)。


おわりに

クリスマス + 自身の誕生日 ということで自動育成マシン 兼 イルミネーションシステムを作成しました。一番初めからうまく動作してくれるだろうと「THE FIRST MAKE」をしてみましたが、HW開発は実際に動作させてみないと挙動がわからないですねぇ。

こんな悔しい日には、egglingを鑑賞しながら、大好物の「タマゴロウ」を食べてお別れしましょう。

f:id:ume-boshi:20211226143723j:plain
めりくり!はぴば!

「人造先生はチョークを投げるのか」システムを作った

こんにちは!

今回は、「身の回りの困りごとを楽しく解決!【PR】Works Human Intelligence Advent Calendar 2021」の19日目の記事です。このAdvent Calenderでは、「実際に身の周りに起きた困りごとを技術で「楽しく」解決した話」について募集しており、僕もアイデアを解消するいい機会だと思い予約しました。とはいっても、当初予定していた社内雑談用ツールを実装するのはやめて、もう少し面白そうなものを作ってみました。


概要

今回対象とした「身の回りに起きた困りごと」は、オンラインの勉強動画で集中力が全然持たない問題です。昨年、リモート授業onlyだった大学院生活を送りましたが、先生の目が光っていない状況だとものすごく眠たくなるんですよねぇ。集中力も持たないし。

それは既に社会人の皆さんでも、Udemyなどのオンライン学習ツールを用いている方は共感していただけるのではないでしょうか。Udemyなどでも、多少は教師の顔表情が見えたりはします。ただ突然、「ここテストに出るよ」と重要な点を述べたり、悪い学生に対して癇癪を起したりはしないので低刺激なわけですね。

ということで今回は、家にも物理的な先生を用意しよう!というアプローチで解決を図ってみたいと思います!



実装内容

①人造先生の錬成

まずは物理的な先生がいなければ話にもなりません。適当な人形があればそれでいいのですが、手短に存在しなかったので針金で人造先生を錬成しました。人生初の針金工作であり、バランスよく形状を生成することは困難を極めましたが、何重かにワイヤを張り巡らせてネジネジすることで、十分な強度でそれっぽいものが作れました。

手には綿棒を無理やりくっつけており、これが指示棒となって我々への指導をわかりやすくしてくれているわけですね。学生が効率よく学べるためのたゆまぬ工夫、素晴らしい指導者です。

f:id:ume-boshi:20211219033751j:plain:w450
私が針金人造先生だ


②教員のランダムな移動

我々学生は、教鞭のために熱意がある姿勢を持つ先生に惹かれる傾向があるはず。ということで次に、積極的に重要箇所を指すように,先生には教壇の上で移動してもらいます。

そのために、ただのラジコン的な移動体に先生を乗せ、その左右移動を制御していきます。移動速度や制御タイミングは、ある特定の値から一様乱数を用いて適当にランダムにしています。移動用ロボットは、ESP32を用いた自作基板のを利用しています。開発環境はArduino IDEです。ただ、それだけでは先生が授業をボイコットして、どこか遠くまでダッシュで逃げてしまうため、移動の限界を設けました。

f:id:ume-boshi:20211219035529p:plain
左右に移動する先生のシステム

この ↑ 写真の、オレンジで囲ったものが左右に移動するロボットであり、緑の銀色マーカに囲われた範囲のみを移動できるようになっています。

移動ロボットは、↓ 過去に紹介したものを利用しています。

ume-boshi.hatenablog.jp


左右の限界位置の検出には、ライントレーサなどにも使われるようなフォトリフレクタを用いるために、高反射性の素材が望まれます。そのために、今回は人造先生一押し!うまいチュウのイチゴ味の包装紙裏側を用いました。(我が家にはアルミホイルが無いのです)

f:id:ume-boshi:20211219041205j:plain:w450
うまいチュウおいしいです。



そして,左右に移動するだけの動画は ↓ になります。もう少し移動量が少なくてもよかったかもね。

youtu.be


③1/f ゆらぎを用いた振り向きの実装

さて、ただ左右に移動するだけの先生は、ただ説明に焦っているだけの先生でしかありません。これからはもっと生徒と向き合ってもらわなければなりません。生徒の理解度も配慮できてこそ、本当にすぐれた教師といえるでしょう。ということで、サーボを用いて先生をYaw角方向に回転させていきます。

サーボを動作させるための回路は、下図の通りです。サーボ(SG92)の定格は4.8Vなので、ESP32の駆動電圧が3.3Vと異なるため、25番ピンからPWM信号をトランジスタのベースに流して制御してあげます。

f:id:ume-boshi:20211219051837p:plain:w250
サーボ用回路

より人間らしい動きとするために、自然界に存在する1/f ゆらぎに基づくランダム性を付加しました。1/f ゆらぎの実装には、間欠カオス法というものを利用すれば簡易的にできるみたいです。これについては、↓ の記事を参考にしました。

satotoshio.net



さて、この1/f ゆらぎを間欠カオス法で求める場合、0 ~ 1の浮動小数点で導出されます。これを0 ~ 90に適応して、サーボに適用してあげただけです。そしてそのサーボをいい感じに設置してあげれば完了です。

youtu.be

一気に人間らしさが出てきましたよ。針金ならではの振動がいい味を出していますな。




完成品

youtu.be

一度、あまりの熱弁が裏目に、教壇を超えた指導に走ってしまいましたが、これも人造先生ならではの良さとはいえるのではないでしょうか。人造先生の熱弁によって新猫論の講義を真剣に聞くことができ、中生代後期白亜紀トリケラトプスも大歓喜、バジル君も驚きのあまり口をあんぐりですね。




おわりに

このシステムを今後発展させるつもりはないですが、寝ている人に叱責をしたり、肩をポンポンたたいてきたり、「○○、ここの答えは何だと思う?(威圧)」と声をかけたりするように、使用者との相互作用を増やしていくことで、より現実の授業のような感覚を各家庭に届けられるかもしれませんね。
(Works Human Intelligence様。プレゼント用にルンバが欲しいです、よろしくお願いします!)


こういう、短期で実装する系の開発記事は久しぶりに書いた気がしますが、結構気楽にネタを含められて楽しかったです。来年度、社会人になってからはこういう突発的な記事が増えるかもしれませんね。

それでは ;)




付録:プログラム

#include <ESP32Servo.h>

/*モータ制御用ピン * 8の定義省略*/
#define Photo 35
#define BACK 0
#define FRONT 1

/* pwm definition 一部省略*/
#define LEDC_CHANNEL_0 0
#define LEDC_TIMER_10_BIT 10
#define LEDC_BASE_FREQ 5000
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */

float animParam = 0.1;
float mInterval = 1.0;
float animInterval = 1.0;
long startTime = 0;
long startMTime = 0;
float speed;
Servo humanServo;


void setup() {  
  Serial.begin(115200);
  /* speaker pwm init */
  ledcSetup(LEDC_CHANNEL_1, LEDC_BASE_FREQ, LEDC_TIMER_10_BIT);
  ledcAttachPin(BLIN2, LEDC_CHANNEL_1);
  /*その他PWMピンの定義は省略*/

  int minUs = 500;
  int maxUs = 2400;
  humanServo.setPeriodHertz(50);
  humanServo.attach(25,minUs,maxUs);
  pinMode(Photo,INPUT);

  /*初期状態はモータを止める*/
  ledcWrite(LEDC_CHANNEL_0,0);
  ledcWrite(LEDC_CHANNEL_1,0);
  ledcWrite(LEDC_CHANNEL_2,0);
  ledcWrite(LEDC_CHANNEL_3,0);

  animParam = 0.1;
  startTime = millis();
  speed = 0;
}


void loop() {
  /*だいたい1000 ms ごとに先生をサーボで回転*/
  if(millis()-startTime > 1000*animInterval){
    /*間欠カオス法による 1/f ゆらぎ*/
    if(animParam> 0.5) animParam = animParam + 2*animParam*animParam;
    else  animParam = animParam - 2*(1.0-animParam)*(1.0-animParam);
    if(animParam < 0.1 || animParam > 0.9){
      animParam = random(10, 90) / 100.0;
    }

    humanServo.write(int(90 * animParam));
    delay(10);

 animInterval = random(5, 15)/10.0//次にいつ回転させるか 
    startTime = millis();
  }

  /*だいたい1000 ms ごとに先生の移動速度を決定*/
  if(millis()-startMTime > 1000*mInterval){
    speed = random(-10, 10)/10.0;
    Serial.println(200*speed);

    // 前後どっちかのモータしか回転してません
    actMotorFB(FRONT, 200*speed, 200*speed);
    actMotorFB(BACK , 200*speed, 200*speed);
    
    mInterval = random(5, 15)/10.0;
    startMTime = millis();
  }

  /*左右の移動制限に達したときに,移動方向と反対に制御する*/
  int val = analogRead(Photo);
  if(val>3100){ 
    Serial.print(val);
    Serial.print("  ");
    Serial.println(speed);
    if(speed > 0){
      actMotorFB(FRONT, -300, -300); 
      actMotorFB(BACK , -300, -300);
    }else{
      actMotorFB(FRONT, 300, 300);
      actMotorFB(BACK , 300, 300);
    }
    delay(400);
    actMotorFB(FRONT, 0, 0);
    actMotorFB(BACK , 0, 0);
  }
}


//モータの制御用.
void actMotorFB(int ForB,int speedL,int speedR){ 
  int R1,R2,L1,L2;
  if(ForB == FRONT){
    R1=LEDC_CHANNEL_2; R2=LEDC_CHANNEL_3;  
    L1=LEDC_CHANNEL_0; L2=LEDC_CHANNEL_1;  
  }else{
    R1=LEDC_CHANNEL_6; R2=LEDC_CHANNEL_7;  
    L1=LEDC_CHANNEL_4; L2=LEDC_CHANNEL_5;  
  }

  if(speedL >= 0){
    ledcWrite(L1,speedL);
    ledcWrite(L2,0);
  }else{
    ledcWrite(L1,0);
    ledcWrite(L2,-speedL);  
  }

  if(speedR >= 0){
    ledcWrite(R1,speedR);
    ledcWrite(R2,0);
  }else{
    ledcWrite(R1,0);
    ledcWrite(R2,-speedR);    
  }
}

【自作VIVE tracker計画】BaseStationの座標を推定したい編 Part1

こんにちは.修論に時速320km/hの速さで追われているume-boshiです.

このブログで進捗報告をしていた自作VIVE trackerについて,5月末から全く触れてきませんでしたがその件についてです.

ume-boshi.hatenablog.jp

ちなみに,Seeeduino UG Advent Calendar 16日目用の記事です.2度も期日を伸ばしてしまったこと,申し訳なく思っています. 本記事の当初の目標はこの試作品の完成だったので,キリのいいところまで開発したかったのですが,完成のメドが立たないので現状までの実装内容の紹介となります.

まあ,そもそもseeeduino XIAOをマイコンに使っているだけで,対して特性を活かしているわけでもないので,あまり期待せずにご覧ください.


今回の進捗

Base Stationの座標を推定するために,trackerがもつ4つのフォトダイオードでの受光を行い,trackerまでのベクトルを求める段階で詰まっています.

そもそもなぜBase Stationの座標が必要かというと,既知の位置・姿勢がわかっている2つのBase Stationがあり,そこから発される赤外線信号をVIVE trackerが読み取ることでtracker自身の座標を推定することができるからです.この際,Base Stationの配置は人が適当に行うため,そのたびに手作業で独自の座標空間を定めて位置姿勢を求めることは現実的ではありません.そのため,trackerがもつ複数のフォトダイオードから,Base Stationの座標を逆算して推定してあげる必要があります.

このBase Stationの位置姿勢の推定のためには,4つ以上の位置関係がわかっているフォトダイオード(以降,センサ)が必要であり,そのセンサが受光するわずかな時間差をもとにして算出していきます.

ume-boshi.hatenablog.jp




実装内容

フォトダイオードを追加

まず,何をするにもセンサが存在しなければ意味がありません.
そこで,3つのセンサを追加しきれいに配置ました.センサ用の小型基板には,φ2.1mmの穴をあけていたため,ねじ止めが可能です.いい感じの剛体がなかったので,ユニバーサル基板をちょっとだけ加工して取り付けました.

f:id:ume-boshi:20211213051937j:plain
4つのフォトダイオードを追加・固定した.

後に,センサ同士の位置関係を基地の値として取り扱うため,センサはきれいに配置するに限ります.
ということで今回は,縦40 mm,横 54 mm,対角 67 mmほどの距離に配置されることになりました.

f:id:ume-boshi:20211213051926j:plain
長方形を成すように配置




センサごとにBase Stationからの赤外線信号を受信

赤外線信号を複数のセンサから受信する際のプログラムは,ただの力業で実現しています.要するに4つのセンサがあれば,4つの割り込み用関数を用意して,グローバル変数に保持された値を更新していく形です.Base Stationの信号は120Hzで受信され,センサ同士の距離が近い場合はほぼ同時に割り込み処理が生じます.正直,割り込み処理の仕様は知らないのですが,割り込みで呼び出される関数で処理する内容はできるだけ軽いほうが良いと思われます.

そこで,複数のセンサで統一できるデータは同じものを使用し,必要な値のみを割り込み関数で取得します.そこで,信号処理のメインとなるセンサを1つ用意します.今回ではphoto1.それ以外はサブセンサとして捉えます.

以下が,割り込み関数における2つのセンサの抜粋です.

void photo1interrupt()
{
    photo1.signalStatus += 1;
    long now = micros();
    if (digitalRead(photo1.PIN) == 1)
    { //HIGHになったタイミング
        /* ------------ ① ------------ */
        photo1.highStart = now;
        photo1.lowMicroSec = (now - photo1.lowStart);
    }
    else
    { //LOWになったタイミング
        photo1.lowStart = now;
        photo1.highMicroSec = (now - photo1.highStart);

        if ((photo1.highMicroSec > 50 && photo1.highMicroSec <= 135) && photo1.lowMicroSec > 1500)
        {
            photo1.signalStatus = 1;
            photo1.signalStart = photo1.highStart;

            // 色々と初期化処理を記述する!
            LH[0] = false;
            LH[1] = false;
        }

        /* ------------ ② ------------ */
        if (photo1.signalStatus > 5)
        { // 受信を受け付けない
            threeTiming = false;
            photo1.signalStatus = 10;
            LH[0] = false;
            LH[1] = false;
            return;
        }

        // micro secではなく、tickで考える
        ticks = (int)(photo1.highMicroSec * 48.148);
        if (photo1.signalStatus < 6)
        {
            if (ticks > 2500 && ticks <= 6500)
            { //はじめの2パルス
                // 最初のパルスがどれかを判断するために、
                // パルスの長さと前回からのパルスの経過時間を用いる

                /* ------------ ③ ------------ */
                //Serial.printf("Ticks:%d ", ticks);
                pulse = (int)((ticks - 2501) / 500.0); //0~7の値のはず
                axis = pulse & 0b00000001;
                dataBit = (pulse)&0b00000010;
                skip = (pulse)&0b00000100;
                //            Serial.printf("Pulse = %d ********** Axis = %d, bit = %d, skip = %d  \n", pulse, axis, dataBit, skip);

                // Lighthouseからのデータを溜めていく OOTX形式
                /*省略*/

                // Lighthouse A,Bのどちらかのうちskipしない側の、LighthouseをLH配列で覚えておく
                // 1つ目のHIGHを1, 2つ目を3, 2つ目を5の状態
                if (skip == 0)
                {
                    LH[(photo1.signalStatus - 1) / 2] = true;
                    photo1.nextAxis = axis;

                    if (photo1.signalStatus == 3)
                        threeTiming = true;
                }
            }
             /* ------------ ④ ------------ */
            else if (photo1.signalStatus == 4 || photo1.signalStatus == 5)
            { //xy軸方向のレーザであるはず
                if (((LH[0] & LH[1]) != true) && ((LH[0] | LH[1]) != false))
                { //片方だけが更新であるとき
                    int t = now - photo1.signalStart;

                    if (t > 1222 && t < 6777) {
                        updateBaseStationSum(&photo1, 0, t);
                        threeTiming = false;
                    }
                }
            }
        }
        else
            photo1.signalStatus = 10;
    }
}


void photo2interrupt()
{
    long now = micros();
    if (digitalRead(photo2.PIN) == 1)
    { //HIGHになったタイミング
        photo2.highStart = now;
        photo2.lowMicroSec = (now - photo2.lowStart);
    }
    else
    { //LOWになったタイミング
        photo2.lowStart = now;
        photo2.highMicroSec = (now - photo2.highStart);
        // micro secではなく、tickで考える

        /* ------------ ⑤ ------------ */
        if ((photo1.signalStatus >= 3) && (photo1.signalStatus < 6))
        { //xy軸方向のレーザであるはず
            if (((LH[0] & LH[1]) != true) && ((LH[0] | LH[1]) != false))
            { //片方だけが更新であるとき
                int t = now - photo1.signalStart;
                if (t > 1222 && t < 6777)
                {
                    updateBaseStationSum(&photo2, 1, t);
                }
            }
        }
    }
}

以前の記事でも取り上げた個所が混ざっている気はしますが,ちょっと説明をしておこうと思います.

①Base Stationからの信号について,HIGHとLOWになった両者の時点でデータを取得しているのですが,HIGHになったタイミングでは,パルスの時間を判断できないため時間を覚えること以外は何もすることがありません.これは複数センサを用いている場合でも同じで必須です.

②Base Stationから受信する信号が,時々正常に読み取れないことがあります.その場合,1フェーズの開始を読み取れず,本来より多くのパルスを受信できてしまいます.したがって,HIGHとLOWの割り込み回数が,合計で5以上になったもの(3パルス以上読み取っている)については,エラーとして除外しています.

③同期信号からaxis, data, skipの情報を取得することは,1つのセンサからのみで十分です.ここが一番短縮できる部分であり,他のセンサにおける割り込み処理を軽くするポイントになります.

④センサ側は,受光量が少ない場合(?),HIGHとLOWのどちらかしか反応しないことがあります.そのためsweep信号は特に読み取りづらく,厳密にすべて正常に受信できた時のみに処理を行うのでは,かなりのロスが生じます.したがって,できるだけ緩くパルスを見張るために,3パルス目の立ち上がり(4)と立ち下がり(5)のどちらでも受光したことを認めます. // もしかしたら2重処理の回避処理書いてない?w

⑤サブセンサでは,メインセンサとは座標が異なるため,受光するタイミングも異なります.受光が発生しうるのは,メインセンサの信号取得フェーズにける3以上6未満(3パルス目の前後を許容)の場合のみであり,その条件に当てはまったらsweep信号の受光成功とみなします.



はじめは結構厳密に処理をしようとしていたのですが,ノイズや環境行の問題によってデータロスをすることが非常に多かったため,緩めの処理をしたら結構うまくいくようになりました.センサの選定とかをすると,もっと厳密なままで処理を進められるのかもしれません.




200点平均をとり,Base Stationから各センサへの方向ベクトルを求める

次に,受信時間のノイズをできるだけ排除するための,200点平均を導出します.200点平均する対象は,Base Stationから見たセンサ位置のx, y角度です.4つのセンサがある場合,4 * 2軸 = 8個の角度を保持することになり,それぞれ別々に集計していきます.

void updateBaseStationAngle(PhotoSensor *p, int index, int t){
    float rad = 0;

    //LighthouseA (Base Station)
    if (LH[0])
    {
        p->axisTime[0][int(photo1.nextAxis)] = t;

        // radianで求めている
        /* ----------- ① -----------*/
        rad = 240 * (int(p->axisTime[0][int(photo1.nextAxis)]) - 4000) * 3.141592 * 0.000001;

        /* ----------- ② -----------*/
        if (calib[0] < 8)
        {
            if (p->getCount[0][int(photo1.nextAxis)] < CALIBRATIONMMAX)
            {
                positionAverageA[index][int(photo1.nextAxis)] += rad;
                p->getCount[0][int(photo1.nextAxis)]++;
            }
        }
        // 現在の角度を保存
        p->bsAngle[0][int(photo1.nextAxis)] = rad;
    }
    /*いろいろ省略*/



// CALIBRATIONMMAX = 200 点平均の導出
if (p->getCount[0][int(photo1.nextAxis)] == CALIBRATIONMMAX){
        positionAverageA[index][int(photo1.nextAxis)] = positionAverageA[index][int(photo1.nextAxis)] / CALIBRATIONMMAX;
        p->getCount[0][int(photo1.nextAxis)]++; //複数回出力を防ぐために1つダミーを足す
        calib[0] += 1;
        //printRays(index, int(photo1.nextAxis), positionAverageA[index][0], positionAverageA[index][1],0);
}
    /*いろいろ省略*/


/* ----------- ③ -----------*/
// 2つの角度から方向ベクトルを算出
void calcRayFrom2Angles(double src[3], double phi, double theta)
{
    src[0] = -cos(theta) * sin(phi);
    src[1] = sin(theta) * cos(phi);
    src[2] = -cos(theta) * cos(phi);
}
    /*いろいろ省略*/


double v0[3] = {}, v1[3] = {}, v2[3] = {}, v3[3] = {}; 
calcRayFrom2Angles(v0, positionAverageA[0][0], positionAverageA[0][1]);
calcRayFrom2Angles(v1, positionAverageA[1][0], positionAverageA[1][1]);
calcRayFrom2Angles(v2, positionAverageA[2][0], positionAverageA[2][1]);
calcRayFrom2Angles(v3, positionAverageA[3][0], positionAverageA[3][1]);

①信号受光タイミングから,Base Stationから見たセンサへの角度を求める式です.前回のRay可視化に関する記事では,計算式を間違っていました.120Hzでsweep信号は回転していることから,120 * 2 pi (rad/s)の速度で回転しています.そして,データ取得の単位時間はusなので,0.000001を乗算することで角度radが求められます.

②calibというグローバル配列には,記録すべき8つの角度について,どこまで取得し終わったかを保存しています.変数名が変でごめん;)

③Base Stationから見たセンサがある角度 theta, phi がわかると,センサまでの方向ベクトルが導出できます.2つの平面が交わる直線が求めたい方向ベクトルであり,これは外積によって求められます.再掲載ですが,↓ のイメージです.

f:id:ume-boshi:20210519224746p:plain

(私はここで,方向ベクトルのx, y, zで求めるべきものを,theta, phi だけの値だけで進めてしまい,その後の処理で全くうまくいかない失態をかなりの時間を浪費してしまいました.どんまぴよ.)




Base Stationからセンサまでの距離k0, k1, k2, k3を代数方程式で求める

ここまでが下準備で,ここからようやくBase Stationの座標を推定し始められます.とはいっても,まずはBase Stationと各センサまでの距離k (mm)を求めなければなりません.そのために代数方程式を解く必要があります.

過去の再掲載となりますが,下記の連立方程式を満たすような距離k0, k1, k2, k3を探索します. f:id:ume-boshi:20210523213418p:plain



既存の計算方法を考えるのが面倒くさかったので,自力で適当に実装したのですがそれが多分いろいろ問題になっている気がします.というのも,私が実装した手法は,10 x 10 x 10 x 10 の区切りで適当な範囲を逐次探索し,その探索範囲を徐々に最小二乗誤差を狭めていくような手法を取りました.この手法は,局所解に陥らない場合に有効ですが,残念ながら今回対象としたセンサへの距離の組み合わせ問題は,局所解が存在して効果的でないみたいです.他にニュートン法などで実装すべきなことはわかっていますが,とりあえず若干正常に動作することもある現状の計算方法を紹介しておこうと思います.

 if (calib[0] == 8 && (k0 + k1 + k2 + k3 == 0))
    {
        // 多分cmでkを定義できている
        float minRange = 0, maxRange = 1000;
        float kRangeMin[4] = {minRange, minRange, minRange, minRange};
        float kRangeMax[4] = {maxRange, maxRange, maxRange, maxRange};
        float kNear[4] = {};            // 計算中の二乗和が最少になったときのk値を保持
        double diffSumMin = 1000000000; // 計算中の最少二乗和
        double diffSum = 0;
        int searchInterval = 10;
        int searchCount = 0;

        /*定義回りの省略*/
        /*デバッグ用表示省略*/

        // 広範囲から徐々に狭めてn回かけて逐次推定
        for (searchCount = 0; searchCount < 5; searchCount++)
        {
            float searchLength = (kRangeMax[0] - kRangeMin[0]) / searchInterval;

            /* ----------- ① -----------*/
            for (k0 = kRangeMin[0]; k0 < kRangeMax[0]; k0 += searchLength)
            {
                for (k1 = kRangeMin[1]; k1 < kRangeMax[1]; k1 += (kRangeMax[1] - kRangeMin[1]) / searchInterval)
                {
                    for (k2 = kRangeMin[2]; k2 < kRangeMax[2]; k2 += (kRangeMax[2] - kRangeMin[2]) / searchInterval)
                    {
                        for (k3 = kRangeMin[3]; k3 < kRangeMax[3]; k3 += (kRangeMax[3] - kRangeMin[3]) / searchInterval)
                        {
                            double d01 = k0 * k0 + k1 * k1 - 2 * k0 * k1 * v01 - r01 * r01;
                            double d02 = k0 * k0 + k2 * k2 - 2 * k0 * k2 * v02 - r02 * r02;
                            double d03 = k0 * k0 + k3 * k3 - 2 * k0 * k3 * v03 - r03 * r03;
                            double d12 = k1 * k1 + k2 * k2 - 2 * k1 * k2 * v12 - r12 * r12;
                            double d13 = k1 * k1 + k3 * k3 - 2 * k1 * k3 * v13 - r13 * r13;
                            double d23 = k2 * k2 + k3 * k3 - 2 * k2 * k3 * v23 - r23 * r23;

                            /* ----------- ② -----------*/
                            diffSum = d01 * d01 + d02 * d02 + d03 * d03 + d12 * d12 + d13 * d13 + d23 * d23;
                            if (diffSum < diffSumMin)
                            { // 最小二乗誤差の更新
                                diffSumMin = diffSum;
                                kNear[0] = k0;
                                kNear[1] = k1;
                                kNear[2] = k2;
                                kNear[3] = k3;
                                Serial.printf("%.2f, %.2f, %.2f, %.2f,   ", k0, k1, k2, k3);
                                Serial.printf("k0**2:%.2f, k1**2:%.2f, k0**2+k1**2:%.2f, ", k0 * k0, k1 * k1, k0 * k0 + k1 * k1);
                                Serial.printf("-2k0k1v01:%.2f, r01**2:%.2f, diffSumMin: %.2f", -2 * k0 * k1 * v01, r01 * r01, diffSumMin);
                                Serial.println("");
                            }
                        }
                    }
                }
            }

            // rangeの更新処理
            for (int i = 0; i < 4; i++)
            {
                /* ----------- ③ -----------*/
                if (minRange != kNear[i])
                    kRangeMin[i] = kNear[i] - searchLength;
                if (maxRange != kNear[i])
                    kRangeMax[i] = kNear[i] + searchLength;
            }
            Serial.printf("searchCount: %d, diffSumMin: %f, searchLength: %f  ", searchCount, diffSumMin, searchLength);
            Serial.printf("K=  %.2f, %.2f, %.2f, %.2f", kNear[0], kNear[1], kNear[2], kNear[3]);
            Serial.println("");
        }
        Serial.println("");
        Serial.printf("searchCount: %d, diffSumMin: %f, K=  %.2f, %.2f, %.2f, %.2f", searchCount, diffSumMin, kNear[0], kNear[1], kNear[2], kNear[3]);
        Serial.println("");

        float p[4][3] = {
            {kNear[0] * v0[0], kNear[0] * v0[1], kNear[0] * v0[2]},
            {kNear[1] * v1[0], kNear[1] * v1[1], kNear[1] * v1[2]},
            {kNear[2] * v2[0], kNear[2] * v2[1], kNear[2] * v2[2]},
            {kNear[3] * v3[0], kNear[3] * v3[1], kNear[3] * v3[2]}};
    }

①ここでは,徐々に範囲を狭めていく逐次探索を実装しています.直感的に処理量がかなりかかることが予想できると思いますが,その通りです.分割数searchIntervalは15ぐらいが限界かなと思います.10くらいで区切り,範囲を狭めながら4回繰り返すとかだと,(104) * 4 = 4000 回程度のループ回数なので,意外にも10秒もかからず処理できちゃいます.seeeduinoが優秀なのだろうか.

②適当なknの値を当てはめた結果は,二乗誤差を見ることでよい値であるかを見極めます.最小二乗誤差を少しずつ更新していき,そのたびにknの目星をつけていき,調べる範囲を狭める参考にしています.

③調べる範囲を狭める際,初期設定した範囲外にならない処理を設けています.別に無くても困らんけどな.




現状の実行結果(文字だけ)

実装した点について実行してみた結果ですが,それっぽい値を導出できているように見えます.下記がその出力です(図で可視化できなくてすみません).

start calibration!!!
----------Base StationのSweepを受信した角度 theta, phi----------
A02,   -0.012,   0.123
A12,    0.016,   0.151
A22,    0.033,   0.165
A32,   -0.035,   0.182
----------Base Stationからみたセンサへの方向ベクトル----------
A03,    0.012,   0.123
A13,   -0.015,   0.150
A23,   -0.032,   0.164
A33,    0.035,   0.181
----------方向ベクトル同士の角度①----------
v01: 0.999256,  deg = 2.209931
v02: 0.998150,  deg = 3.485393
v03: 0.997968,  deg = 3.652790
v12: 0.999738,  deg = 1.312491
v13: 0.998205,  deg = 3.433950
v23: 0.997562,  deg = 4.002042
----------代数方程式の計算(ただの逐次探索)②----------
searchCount: 0, diffSumMin: 16750572.217250, searchLength: 100.000000  K=  900.00, 900.00, 900.00, 900.00
searchCount: 1, diffSumMin: 5474216.397448, searchLength: 20.000000  K=  840.00, 860.00, 800.00, 820.00
searchCount: 2, diffSumMin: 3444616.012254, searchLength: 4.000000  K=  828.00, 844.00, 792.00, 800.00
searchCount: 3, diffSumMin: 3331278.016753, searchLength: 0.800000  K=  825.60, 840.80, 788.00, 796.00
searchCount: 4, diffSumMin: 3313374.906096, searchLength: 0.159998  K=  825.12, 840.00, 787.20, 795.20
searchCount: 5, diffSumMin: 3313374.906096, K=  825.12, 840.00, 787.20, 795.20
----------base stationからセンサまでの距離を考慮したベクトル----------
p0x: 9.731887,  p0y: 101.528069,  p0z: -818.791077
p1x: -12.947811,  p1y: 125.983315,  p1z: -830.395508
p2x: -25.369558,  p2y: 129.214645,  p2z: -776.096619
p3x: 27.727877,  p3y: 144.187103,  p3z: -781.510132
----------センサ間の距離について,rnmが実際の距離で,pnmが推定値を示す③----------
r01:40.000000, r02:67.000000, r03:54.000000, r12:54.000000, r13:67.000000, r23:
p01  len: 35.314171,  err: -4.685829
p02  len: 61.818077,  err: -5.181923
p03  len: 59.443401,  err: 5.443401
p12  len: 55.795258,  err: 1.795258
p13  len: 66.148849,  err: -0.851151
p23  len: 55.433006,  err: 15.433006

この出力について「それっぽい値」を出力しているものの,怪しいところが実はたくさんあります.
まずそもそも①方向ベクトル同士の角度が最大で4° もあり,「これは誠の値か?」と疑いたくなりますね.
次に②距離knの単位は mm なのですが,Base Stationからセンサまでの距離が,大体 80 cm となっていることがわかります.実際に距離を測ると,100 cm以上は離れているため,異常な値をはじき出しているわけです.
最後に,③センサ同士の推定距離pnmについて,その誤差が最大で15 mmあるようです.これはうまく最大5 mm未満で計算できることもあるのですが,自身のアルゴリズムの悪さを示しているわけですね.ちなみに,10 mm以上の誤差が存在する場合,「うまく200点平均の値が取れていないよ」と海外のエンジニアが書いていたので,どこか間違いが存在することは確実そうです.

trmm.net




終わりに

「実装してそれっぽい値が出たけれど結局狂っています」というくそ記事でした.色々と問題はありそうですが,とりあえず現状の計算式の状態でBase Stationの座標・姿勢を今後推定してみて絶望していきたいなと思います.

それでは,Part2をすぐに書けることを願って頑張っていきます~

【M5swarm】安い小型マイコンを使ったオリジナルArduinoボード環境の作りかた【Qiita Advent Caldendar 2021】

こんにちは。

修論研究が大詰めのume-boshiです。卒業するために被験者実験をしなくちゃならないのに、理由もなくQiita Advent Calendarに申し込み、誰にも読まれない記事を書いています。おわた~~~(:3 」∠)



さて、今回はATMEGA328P AUという安価な表面実装のマイコンブートローダを書き込んで、オリジナルArduinoボード環境を作る方法について紹介します!

対象の基板

以前、ATMEGA328P AUを対象とした自作基板について、↓ 基板設計面で紹介しました。

ume-boshi.hatenablog.jp

この基板に採用したATMEGA328シリーズのマイコンですが、実際にArduino系で利用されています。その中でも、表面実装できるATMEGA328P AU小型マイコンであり、230円と安いメリットがあります(およそESP32-WROOM-32の面積比1/4、値段1/2)。

akizukidenshi.com

ただ、Arduino用のブートローダを書き込んでいない状態では、Atmel Studioなどでしか開発できません... もちろんそのまま使うのも粋なものでしょうけれど、慣れ親しんだArduino IDEで開発できるほうが便利ですよね!

ということで今回は、以前紹介した基板を用いて、ATMEGA328P AUArduino IDEで開発するための環境を整えていこうと思います。
最終ゴールは、ATMEGA328P AUArduino Pro miniのブートローダを書き込み、Arduino IDEのsketchを普通に書き込める状態です。




Arduino as ISPの書き込み機を用意する

まず、ATMEGA328P-AUブートローダを書き込むための書込装置を作成します。

我々が普段よく使っている(?)書込装置は「AVRISP mkII」というものですが、これはFT232RLのような書き込み機で使われています。{Tx, Rx, RTS, DTR}ピンを使っているやつですね。
それに対しブートローダの書き込みには、「Arduino as ISP」という書込装置を用います。この規格では、SPI通信で使われるような{MISO, MOSI, SCK, RESET}ピンを用います。詳細は他の人に任せます...

そして、このArduino as ISPの書込装置を作成する際には、既にArduino IDEで動作させられるマイコンが必要になります。これはKumanなどの互換品のArduino Unoで大丈夫です。
用意したArduino系のマイコンには、「ArduinoISP」というサンプルスケッチを書き込んでおきます。いつも通りボードとポート番号を正しく指定して、「マイコンボードに書き込む」機能を使ってください。

f:id:ume-boshi:20211206005809p:plain:w450 f:id:ume-boshi:20211206005806p:plain:w450
スケッチの選択とボードへの書き込み設定


ブートローダを書き込みたいマイコンを接続

作成したArduino as ISPの書込装置を、ブートローダを変えたいATMEGA328P AUに接続していきます。Arduino Unoを用いた際の対応表は ↓ の通りです。

index 役割 Arduino Unoのピン ATMEGA328P AU側のピン
1 電源 3V3 3V3
2 電源 GND GND
3 RESET 10 PC6
4 MOSI 11 PB3
5 MISO 12 PB4
6 SCK 13 PB5

f:id:ume-boshi:20211206015510p:plain:w300
データシートより抜粋
※ATMEGA328P AUのデータシート



Arduino Unoを採用した場合、下図のようにピンが配線されることになります。赤が3V3、黒がGND、青がRESET、茶がMOSI、黄がMISO、緑がSCKです。
// MISOが茶色じゃないんかい!(大爆笑の嵐 ;) )

f:id:ume-boshi:20211206014304j:plain:w450
Arduino as ISP側のピン出力位置



そして、読者さんには全く役立つ写真ではないですが、自身の基板では下図のように配線した状態になりました。
この基板を設計した当初は、ブートローダをSPIピンだけで書き込めると勘違いしていたため、RESETピンをつなぐ用の場所を用意していませんでした... ということで青線は、RESETボタンのハンダ部分に手で押さえつけています ¯\(ツ)

f:id:ume-boshi:20211206021456j:plain:w450
毎回接続先を忘れるので写真を撮っておくことをお勧めします


ブートローダを書き込む!

それでは、実際にATMEGA328P AUブートローダを書き込んでいきます。Arduino IDE側の設定は下図の通りです。

f:id:ume-boshi:20211206022407j:plain:w450
設定

①まず、ATMEGA328P-AUに書き込むブートローダを選択します。今回は「Arduino Pro mini」を選択します。プロセッサについては、電源電圧を気にしなくて大丈夫です。今回は16MHzのクリスタルを実装しているため、 「ATmega328P(5V, 16 MHz)」を選択しています。

②書込装置を選択します。これは先ほど作成した「Arduino as ISP」を選択します。※「ArduinoISP」では動作しなかったのでご注意を!

ブートローダを書き込みます。この際、ATMEGA328P-AU側のRESETボタンを1回押してから、「ブートローダを書き込む」をクリックします。Arduino Uno側のLEDが激しくチカチカし始め、Arduino IDEに「ブートローダの書き込みが完了しました」と出力されたら成功です!
※③におけるRESETを押すタイミングによっては、うまく書き込めないことがあります。失敗しても何度か試してみてください。

これ以降、Arduino as ISPは不要なので、取り外して問題ないです。


Arduino Pro miniとしてプログラムを書き込む

それでは、ブートローダも正常に書き込めたことですので、適当なプログラムを実行して動作確認したいと思います。定番のBlinkを書き込みましょう。

まず、FT232RLの書き込み機を接続します。親切に接続対応表もつけておきましょう。

f:id:ume-boshi:20211206033015j:plain:w450
FT232RLを接続

index 役割 FT232RL側のピン ATMEGA328P AU側のピン
1 電源 3V3 3V3
2 電源 GND GND
3 Tx Tx PD0(Rx)
4 Rx Rx PD1(Tx)
5 RESET DTR PC6(前回記事画像C20の位置)



Blinkのプログラムを書き込む際には、元の書き込み設定に戻す必要があります。そのため、シリアルポートはFT232RLのものを選択し、書込装置は「AVRISP mkII」を選択します。実際に書き込む際は、赤で囲ったいつものボタンをクリックします。

f:id:ume-boshi:20211206032927j:plain:w450
書き込み規格をAVRISP mkIIに戻す



動画のように、目的の動作がされたら成功です!Lチカの速度を適当に変更して動作確認しています。(delayを1000 -> 100 -> 10 -> 10000に変更して書き込んだ) youtu.be

もし書き込めなかった場合は、FT232RLとの配線の確認や、プロセッサの ○MHzの値があっているかなどを確認してみてください。




おわりに

今回はATMEGA328P-AUArduino系ボード化する話でした。表面実装でないATMEGAをArduino化する記事は結構あるのですが、今回対象にした表面実装のは見つからなかったので記事にした次第です。

記事では簡単に説明できているものの、私が書き込みに成功するまでには1週間以上がかかりました... どれもこれも、ちゃんとクリスタルを選定してから基板設計しなかったことと、デバッグ用LEDを用意していなかったことが原因です。つらいつらい。

ということで、この記事が皆さん参考になり、Fakedeino開発がはかどれば幸いです!

それではまた ;)


参考文献

【ATMEGA328P-AU】M5Stackで4つのモータを動作できる基板を作成した件【基板設計編】

こんにちは。

先日、M5swarmという作品を各所のコンテストに応募しました。

protopedia.net

作品の実装の際に、M5stackを群ロボット化するための足回り基板を作成ました。機能としては、M5Stackからの命令を受け取るマイコン部分と、電源分離しながらモータ4つを独立に動作させられるものです。

今回は、そのモータを動作させるための基板実装について紹介していきたいと思います。


機能概要

足回り用の基板を設計する上で、モータ制御用のマイコンを搭載することで、制御ピン数の面で有利になると考えました。そのため、外部のメイン処理部分からシリアル通信で命令を受け取り、その命令を基に足回り専用のマイコンでモータを制御するようにしています。そして、利便性向上のため、半固定抵抗によるパラメータ調節の機能や、移動精度向上を目的としてエンコーダの追実装もできるようにしました。
搭載機能を列挙すると下記の項目があります。

  • ATMEGA328P-AUを用いた処理部分
  • 4モータを動作させられるモータードライバ
  • 各種シリアル通信(SPI、I2C)
  • 2相エンコーダ用ピン
  • 半固定抵抗2つ分
  • 電源分離


回路について

回路概要

はじめてATMEGA328P-AUを用いたため、その動作が正常にできるかが心配で、特別こだわった点はありません。ちなみに、ATMEGAに正常に書き込みできるようにと気を取られすぎて、PWM周りのピン割り当てをミスってしまいました。(ATMEGA328ってPWM用のピンが6個しかないのかよ... ESPに慣れすぎて全部のピンでPWMできる気でいたわ...!)

下図が実際に動作させた基板の回路図です。

f:id:ume-boshi:20211125081000p:plain
M5Swarmの足回り基板の回路図です。

回路図説明

適当な解説ですが、左下にあるのがATMEGA328P-AUです。プログラムを書き込むために必要なのは、マイコン右側にある7, 8番ピンのクリスタルと、29番ピンのRESET用ボタンです。クリスタルが必要な理由としては、ATMEGA328P-AUには内蔵クリスタルが8MHzのものしかなく、16MHzに変えたい場合などに使うためです。個人的には、今使っているY2の部品よりも、セラコンまで内蔵している3パッドのクリスタルのほうが、実装時の領域を削減できるのでオススメです(基板の説明箇所に製品リンク)。ちなみに、Arduino Pro miniでも3パッドのものが使われています。

リセットボタンについては、単純にプログラムを再動作するために必要です。さらにATMEGA328P-AUArduino Pro miniのブートローダを書き込む際にも29番ピンへの接続をしなければなりません。なので、ジャンパー線接続用のピンを1つ出しておくことをオススメします。

それ以外の要素として、左上がモータを動作させるための外部電源の供給と、3V3への降圧をしています。なんで降圧する必要があったかは、今見返すと不明なのです。
右下はモータドライバ動作用の箇所です。U19とU20がフォトカプラのアレイで、ここで電源分離を実現しています。U19の左側がマイコンで、右側がモータ側です。U19等の右側から、さらにU17とU18のモータドライバの方に接続しています。このモータドライバは1素子で2個のモータを制御できます。

右上の大量のピンについては、他の基板などと通信するための箇所です。J32とJ33はume-boshiオリジナル基板(メイン処理部)に、J34はFT232などの書き込み機に、J35はI2C用に、J28とJ31はエンコーダ用に配線しています。

回路設計の反省点

今回、はじめてATMEGA328P-AU用の基板を作成しましたが、ちゃんと動作しているか確認することにすごく手間取りました。というのも、Debug用のLEDを1つも配置しないという失態をしていたためです。状態の可視化は大切ですよね。

また、モタドラ基板としての致命的なミスとして、マイコンの選定そのものが間違っていました。ESP32感覚で全部のピンでPWM出力できるとなぜか勘違いしており、今回は適当に配線してしまいました。しかしATMEGA328P-AUはPWMの本数が6本しかありません…
4つのモータをPWMで制御しようと思うと、8本のPWM信号線が必要です。 滑稽なことにピン数が足りていないのです。結果、ソフトPWMを実装して乗り切りましたが、大失態を犯したのであります。

最後に、個人的には不要な回路があった点です。Encoder用のピンで4本確保していますが、そんなに要らない要素ですね。Encoderの代わりに、マウスに搭載されているようなイメージセンサを用いれば、軽量に2軸方向の移動が分かってしまいますし、それで位置推定するほうが良かっただろうなと後悔しています。




基板について

概要

特にこだわった点は無いです。しいて言えば、人生初のマイコン斜め配置に挑戦したぐらいでしょうか。5cm x 5cmの基板サイズでは配線に限界があったので、リフローが必要なレベルの表面実装部品が使えるようになりたいものです。あと、抵抗アレイも使ってみたいです。

f:id:ume-boshi:20211125081003p:plain:w400
基板図。こんなの見ても参考にならないですね。

主な使用部品

完成品

f:id:ume-boshi:20210522230836j:plain
M5Swarmの足回り基板写真。裏にATMEGA328P-AUがあります。

おわりに

研究活動が忙しくて、2か月くらい記事を書くのをめんどくさがっていました。でもいざ手を動かしてみると、以外にすぐ書けるものです。

次はATMEGA328P-AUArduino Pro miniのブートローダを書き込んで、一番最初のプログラムを書き込む話です。QiitaのAdvent Calendar用の記事。

では、皆さんお元気で。

スタートアップの不確実性を排除できるノウハウが詰め込まれた参考書「入門 起業の科学」を読みました :)

こんにちは。

先日、「入門 起業の科学」という書籍を読破しました。今回はその紹介記事となります。

この本は、「起業の科学」という同著者の書籍をベースに、アイデアをブラッシュアップし企業をし始めるまでをまとた書籍です。前著には実例がかなり盛り込まれており、図表は講演資料から抜粋したような少々雑なものが多くぶ厚い本でした。結構読み進めるのが苦痛だった印象です。

それに対し本書は、図表がかなりわかりやすく丁寧に作り直されており、事例を少なく端的に説明するようになっていました。実際に起業をする際は、この本を片手にトレースしていく形で進められるような、無駄の少ない作りになっています!


概要

科学

起業の”科学”として、起業開始時において失敗しないための手順が紹介されています。"科学"という言葉は、ある程度「プロセス化できて再現性がある」ことを本書では意味していますが、科学と言えるほどのものかは起業経験がない素人には判断できませんでした。読んでいた印象としては、"科学"というよりは「事業化する内容の効果を、起業する前にとことん慎重に事前調査する沢山の手法」をまとめているような印象です。ともかく、慎重に全てを明かしてから進めていくので、この本に従えば「ここまでしたら失敗しないだろう」という自信は確実に着けられそうです。

スタートアップの不確実性を排除

この書籍で取り扱っている起業は、スモールビジネスではなくスタートアップの方でした。

スモールビジネスとは、既に需要が明らかになっているため、決まったサービスを提供するだけで安定して儲かるような、小売店や飲食店・サービス店などです。「おいしいラーメン」や「車を駐車したい」こと、「何かを専門職の人に修理してもらいたい」などの需要は既に存在しており、それに対応する職業も既にあるため、それを真似できる比較的容易な起業です。
それに対し、スタートアップは需要は潜在的なものであり、この世に無かった新しい仕組みをつくることで莫大な利益を出せるような、Uber EatsやNash・DiDiのようなサービスのことを指しています。スタートアップは需要がわかっていない上に、新しい概念を顧客に浸透させる必要があったり、類似サービスが極めて少なかったりするため、成功することが比較的難しい起業です。ただ、利益面でいうとロマンのある起業ですよね。

f:id:ume-boshi:20211011052432p:plain
利益にロマンがあるスタートアップ


このスタートアップの難しさは、需要予測や顧客のペルソナ、好まれる機能の選定などの不確実要素を明らかにすることで排除できると考えられます。

本書ではそのための手法として、「リーンキャンバス」や「ストーリーカンバンボード」のワークシートや、見込み客への聞き取り調査におけるコツなどが盛り込まれています。どれも実用的で、簡単に取り組めることばかりなので、実際に起業をする際には非常に参考になりそうです。起業するかを、顧客に受け入れられない恐怖で思いとどまっている人には、ぜひオススメしたい書籍でした。


感想

起業の本を読んでいると、結構「スモールビジネス」に焦点を当てたものが多くて、参考にできず読み終えることがよくありました。この本では、スタートアップに焦点を当てて、慎重に不確実要素を取り除く姿勢が詳細に述べられており、欲しい情報が満足に得られた印象です。リーンキャンバスやペルソナなどのツールは、書籍を読む前は単語すら知らなかった有用な概念で、それを知れただけでも大満足です。



さて、私も1人のゴミクリエイターとして起業には興味があるのですが、書籍を見ていてもやる気は出ないものですね。むしろ、「この事務的な(?)計画作業をしなければならない」という煩わしい気持ちが湧いてしまいます... そして結局「こんなこともやる気がでないのか」という、アイデアへの悪印象と自己嫌悪感で辞めてしまうんですよね。
私は座学でワクワクすることがほぼ無いので、実践に移すとまた変わってくるのかもしれません。

ということで模擬的に実践すべく、専用のデジタルノートを用意しただけなのであった... (To be discontinued...)

ume-boshiたる所以を語ったった :)【はてなブロガーに10の質問】

こんにちは。

最近は全然記事を投稿していないものの、下書きに色々書きだめているume-boshiです。以前の記事で、技術記事を4つほど投稿する宣言をしてしまったものの面倒で放置しているので、他の記事を上げるのもどうかな... と思っていたところです。



そんなところ、はてなブログの10周年記念イベントで「万年筆をプレゼント!」などと言われたものですから、つい投稿した次第です。
べ、別に、モノで釣られたわけじゃないんだからね!

さて、取り組むお題は、"はてなブログ10周年特別お題「はてなブロガーに10の質問" というものです。それでは1つずつ回答していきましょう。


10の質問!

ブログ名もしくはハンドルネームの由来は?

ブログ名は、作りたい作品のアイデア(机上の空論)が大量にあろうとも、開発に着手しない自分自身を世間体に晒上げて厳しく戒めるためのものです。

私は各所で"ume-boshi"という名前を利用していますが、実は酸っぱい梅干しは苦手です。
(甘いはちみつ系の梅干しのお菓子は大好物で、毎日服用するほどですが)

さて、ume-boshiを採用した由来ですが、さかのぼること5年ほど。
「PUBG mobile」がリリースされたときに、ニックネームを決めかねていたところ、当時ハマっていた梅干しシートというお菓子が目に入り、気まぐれでそれを選びました。

www.lawson.co.jp

https://www.lawson.co.jp/recommend/original/detail/img/l589520_4.png

※LawsonのHPを参照

以来、"sheet"の部分は邪魔だったので取り除き、時々使っており、ブログの開設とともに本格的に使い始めました。


はてなブログを始めたきっかけは?

NAISTに進学する1か月前にブログを始めたのですが、そのころ教授から「アウトプットを続けていると、そのうち努力が実って新しいイベントが生まれたりするよ」とアドバイスをされたことを思い出します。教授は私がNAISTに進学する年に大学をご退官したのですが、有名な雑誌の特別枠を執筆する機会があったらしいです。

言ってることは間違ってないなと思い、普段からモノづくりをしている身としても、アウトプットをしないのは知識の共有がされず勿体ないなと感じたためブログを始めました。
// 就活用という話も。

(果たして、現在知識の共有がまともにできているんでしょうか... 疑問です。)


自分で書いたお気に入りの1記事はある?あるならどんな記事?

↓ これですね。

ume-boshi.hatenablog.jp

この記事を投稿するまでは技術記事を書くことが多く、経験から得た思想をしっかりと書いた記事はほぼありませんでした(今もほぼ無いけど)。 スタートアップが失敗する例について、ちょっと炎上するかなと怯えながらも愚直に感情を書いたことを覚えています。本記事はTwitterでちょっとだけ拡散されたのですが、滅多に発さない持論を認めてもらえたことはとても嬉しいことでしたね。


ブログを書きたくなるのはどんなとき?

満場一致(1人)で、研究室の嫌な作業が溜まっているときですね。

研究室では人目があり、娯楽となるデバイスが少ない分、記事を書くことに集中できるのです。
あと、研究室で使っているキーボードの方が入力しやすくて心地よい。

umeboshi-lab.hatenablog.com


下書きに保存された記事は何記事? あるならどんなテーマの記事?

当ブログで16記事、別のブログの方で6記事もありました笑 どれもほとんどタイトルだけメモしたものですが...

当ブログのは、基本的には技術記事ですかね。本の紹介とか、途中まで手を付けて放置している開発の話とか、そういうのばかりです。
別ブログの方では、商品・映画のしょうもない紹介をするつもりで放置したものと、死ぬまでにしたい100のことを書き溜めています。死ぬまでにしたい100のことは、本ブログでも「ブログ1か月連騰チャレンジ」としてちょっと出てきていましたね。

ume-boshi.hatenablog.jp

自分の放置癖には飽きれたものです ;)


自分の記事を読み返すことはある?

技術記事は自分のために書いているといっても過言ではないです。自作のVIVE Trackerの位置推定の理論なんかは、自分のブログが日本語で馴染みもあるので良く見返しています。

ume-boshi.hatenablog.jp

あと、NAISTの受験記は、人に教えることが良くあるので、有利になりすぎる情報がないかは度々見返すようにしています。

ume-boshi.hatenablog.jp


好きなはてなブロガーは?

ブログ開設時からずっと見てくださっているkaraageさんは、このブログを続けるモチベーションにもつながったLegendです。(時々、karaageさんにtwitterで拡散してくださるときは閲覧数が爆上がりするので、「何が起こったんだ!?」と冷や汗をかきながら、動揺して日常生活に支障をきたす状態に陥ります。)技術ブログ界隈の頂点だと勝手に崇めて、友人にも広めているのですが私の影響力はごくわずかです。
karaageさんの記事で個人的に好きなのは、エンジニアパパ + 家族の生活を投稿しておられるところでしょうか。技術系のブログも最新の流行の実践や、変なデバイスをハッキングしたりと大変為になるのですが、技術者がいる家庭の日常風景は、自分の将来像を妄想してしまう良い機会になるのです。mi band宝さがしなどは、技術者家族ならではの団らんで、憧れるところでもあります。

それ以外に面白いなと思う人は、京太郎さんです。この方は技術系ではなく、非モテの話やフェミニズムなどの社会問題について考察しておられます。思想を述べたブログはおそらく大量にありますが、京太郎さんはその文章から知識の深さが見て取れます。毎回の記事が、大量の参考文献に基づいており、一般人にできる芸当ではないなと、いつも尊敬しています。固めの文章に惹きつけられたのは初めてかもしれません。


はてなブログに一言メッセージを伝えるなら?

新しい万年筆をください!


この10年を一言でまとめると?

人間不信で友達がいない喋れないゴミ陰キャから、arctanみたいな軌跡で成長して見せました。

13年前の私は中学2年生でした。当時、中国から日本に帰国した私は、友達の不登校や、いじめられそうになったことによる同学年の子とのコミュニケーションの断絶から家族と先生しか喋らない人でした。そこから、高校生になって最高の仲間に触れ、写真撮影を初めてこの世のすばらしさを知り、周囲の良いところを見ることができるようになりました。大学生ではプロジェクト活動やロボコン出場/運営を経験して、技術的に尊敬できる人の存在ができ、実践的な活動の方法を学び、人を動かす立場として挑戦できました。そして今は修士2年の大学院ですが、学会やブログ、開発コンテストでアウトプットすることを覚え、研究室でプレゼンテーションの方法を知り、偉大な論文達から蓄積された知識の偉大さを学んできました。

これで、多少はまともな人間になれたのではないでしょうか。


この10年を10文字でまとめると?

「最底辺人間からの脱却」

(別の局所解に陥った可能性も?)




おわりに

はてなブログ企画のイベント記事を書くのは、今回が初めてでした。自分のブログと人生を振り返る良い機会ができました。ありがとうございます。

唐突な自分語りは、以降の記事では慎んでいきます...

公式ストアでOculus Quest2を購入して厄介な目に遭った話

こんにちは。 もうすぐ社会に旅立ってしまうume-boshiです。

学生中の時間があるうちに、HMDを使った開発の勉強をしておきたいなと思い、Oculus Quest2(とOculus Linkケーブル)を購入しました!

f:id:ume-boshi:20211016072624j:plain
ブロガーなのでちゃんと写真を撮るようになってきました

Oculus Quest2の購入の際に、公式ストアから購入したのですが、色々と面倒なことがあったので記録しておきます。 Quest2の購入の際に生じた問題として、クレジットカードの制限がかかったこと、住所を無理やり変更されたこと、2重発送になってしまったことの3点がありました。

// 結論から言うと、Quest2の実機を買うときはAmazonから購入するのが一番楽だと思います。Oculusのアプリを買うときに問題がある分には、配送の問題はなさそう。


生じた問題1: カード会社のセキュリティ制限

1つ目のクレジットカードの制限について、Oculusの公式ストアでの支払い方法はVISAなどのクレジットカードを用いた方法か、PayPalを用いた方法が用意されています。この支払の際に、カードが拒否される問題が高頻度であるようです。クレジットカードを選択した場合は、支払いボタンをクリックした時点ですぐに弾かれるので、私はPayPalで支払いをしました。

数日が経ち、なぜかOculus Linkケーブルのみで支払いが成功し、デスクトップPCもHMDも持っていない我が家に、1万円のケーブルだけが届きました。

f:id:ume-boshi:20211016074359p:plain:w450
これだけ届いてもなぁ・・・

原因としては、高額な支払いであるためか海外?への送金であるためなのか、一部の製品において銀行のセキュリティ制限に引っかかったためのようです。 この問題を解決するために、Oculusのサポートセンタに英語で質問してわかった解決方法ですが、やはり、銀行会社に問い合わせて一時的にセキュリティ制限を解除して貰う方法しかないらしい。

ということで、制限を一時的に解除してもらい、再度購入手続きをしました。


生じた問題2: フォームで住所が無理やり変更される

2つ目の問題として、購入時の個人情報入力の際に、住所を勝手に書き換えてくることです。Oculusの公式ストアで購入する際は、全て英語入力しなければならないのですが、住所のフォーマットがおかしいと自動修正してきやがります。この自動修正機能は商品の購入時には無効にできるので、特に問題はありません。

ただ、脳死状態で変更を適用してしまうと、存在しない住所が登録されることになるので注意が必要です。おそらく数字に反応しており、例えば、「○-MACHI 10-532, hogeマンション-11235」は 「10-53211235, ○-MACHI」みたいな感じで修正されます。厄介極まりないです。

たまたま私の場合は、正常に自宅に商品が届きましたが、高価な買い物をしているときに起こる問題としては恐ろしいですね。



また、次の問題で返送したことを説明しますが、その返品申請時にも住所入力欄では自動修正機能を無効にできませんでした。 スマホ・PCの両方から登録してみたのですが、両者で同様のUIであり、どうも必ず変更しないといけないようです。

ということで、自動修正機能に引っかからない誤った住所順番で、とりあえず全ての項目を入力して登録しました...


生じた問題3: 二重注文

カード会社で支払い制限を解除してもらったときに、「もう一度購入手続きをしてください」と言われたので素直に従ったところ、二重注文されてしまいました。Oculusの公式ストアでは、厳密に支払いが確定したタイミングではメールで連絡してくれず、発送されたタイミングでメールが届きます。そのため、二重注文されたことに気づかず、2つのQuest2が家に届く羽目に...
時すでにおすしですし🍣

f:id:ume-boshi:20211016072957j:plain
2つ届いた記念写真

さて、Oculus公式ストアで購入する場合、すべての商品は香港の倉庫から届くようです。Oculusのサイトから返品手続きを行うと、FedExというサービスで返送するための書類がメールで送られてきます。 FedExでは、モノを集荷してもらう際に、家まで受け取りに来てくれるのですが、そのためには電話で問い合わせて日程を予約してあげなければなりません。このFedExの集荷予約の電話が混んでいて繋がらない...

無事に電話で予約ができると、指定の日程にFedExの人が来て、{梱包したモノ + メールに添付されたものを印刷した書類}を渡して終了です。


まとめ

色々と問題がありましたが、これらが生じた原因は下記のことが考慮されていなかったためだと考えられます。

  • 国ごとの住所の特性が違うこと、アパート名の未考慮
  • 自動修正機能が間違っている可能性の未考慮
  • 決済が完了した通知が無い
  • 銀行会社から信用されていない?


おわりに

9月末に購入し、まともに全部の処理が終わったのは10月10日を超えていました...
皆さんがOculus Quest2を購入する際は,Amazonで購入するのが手っ取り早くて安心できると思います。転売ヤーには注意しなければならないですけどね!

では、素晴らしいVRライフを送りたいと思います!