hirotakaster のすべての投稿

道中記

久しぶりのサンフランシスコで本当に色々あった。初の別室送り・首切りジェスチャーを初体験・SFMoMa・Musee Mecanique…とかを記しておく。

米国入国で初の別室

確か入国時に顔認証でサクッと通れた記憶があって、普通に通れると思ったら別室送りになった。理由はもちろん分からない。ただ、別室に送られるのは、本当にオススメできない体験だった。

というのも…最悪は強制帰国、荷物没収、そして開放されるまでの時間の長さだ。
開放されるまでの時間は、自分の場合は1〜2時間程度だった。その後に予定を組むスケジュールだったら、外部に連絡も出来ないまま遅れてしまうことになる。
まさか別室行きにされる前提で予定を組むことは無いだろうからね。

■ 体験の流れ
・サンフランシスコ国際空港に到着して入国審査に向かう。
・入国審査で受け答えて、審査官もにこやかにOKっぽかったのに…
・別の人が来てパスポート没収、「お前はこっちな」と別室に案内される。パスポートを没収されると、地味に焦るですよ。
・別室はイミグレを出て少し歩いた先にある。
・おしゃべり禁止・ケータイ禁止。ケータイは出した人はそっこー怒られていた。
・他にもアジア圏・中近東系の人とか、自分より先に10数名、長椅子に座って待たされていた。どうやら、個別に審査されてOKな人から抜けていくような感じだった。
・自分の待ち時間は約1時間、何もやる事もなくひたすら待たされる。
・航空会社の人がきて「お前の荷物はBaggage Claimの所に置いといたからな」と伝えられる。どうやらこの便で別室送りになったのは自分だけのようだ。
・ひたすら待たされたあと、名前を呼ばれた。よーやく順番が来たみたいで、奥の尋問部屋みたいな所に案内される。
・部屋は四方が白で囲まれた部屋で、奥に銀色のテーブルが1個あるだけの無味無臭な雰囲気だった。いわゆる映画とかで「こーゆー部屋見るよね〜」みたいな雰囲気
・ここから、デカイ白人の入管 180cm 100kgくらいとトークタイム。
・尋問は10分くらい。もちろん、この時もケータイを出したりするのはNG。受け答えはこんなかんじ、

入管) ヲマエは何をしているやつだ。
ワイ) プログラマーしている。
入管) USにきた予定は?
ワイ) 市内に泊まってから、ナパの方に行って、また市内に戻ってくる。
入管) ナパに何しに行くんだ?
ワイ) イベントで展示してくる。名前はMaker Faire Bay Area
入管) じゃ、そのチケットみせろ。
ワイ) これな(紙でEventbriteのチケットを印刷しといて良かった)
入管) あぁ?ここナパじゃねーだろ(Vallejoという場所)
ワイ) けど、ナパの近くだろ。
入管) あぁ、そうだな。じゃ、泊まるホテルにはどこだよ?

こんな感じで延々に続いていく。相手の表情から反応らしきものは全然なかった。気を使って貰ったのか、めちゃくちゃヒアリングしやすく話してくれた。ただ、尋問は淡々と続いてゆく…

・終わったら、また元の待ち合い部屋に戻って待たされる。
・また名前を呼ばれて、OKだからあっち行け、とパスポートを戻してくれる。
・あっち行け、と言われたドアを開けると、細長い白い通路を少しの間、1人で歩いていく。細長い通路の幅は人ひとり通れるくらい狭い。
・この時、監視カメラでデータを間違いなく取られてるんだろーなー…と思いつつ一人歩いて行く。
・出ると手荷物ピックアップの裏っぽい所に出る。
・荷物がポツンと置かれていて、回収完了でほっと一安心。

■ 原因として考えられること
直接的な原因は分からない。ただ、敢えて…だと次のような原因が考えられる。

1. 荷物怪しすぎ
こればっかりはしょうがない。自分でも普通の感覚だと「こいつ、ぜってー怪しい」と思う。
荷物はイベントで使う、クラド二図生成ようのスピーカーx9、鉄板9枚、RPi4 x16、電源タップ大量、SDR大量、HUB、謎の3Dプリンタで作ったパーツ…etc

まぁ、怪しいかどうか?でいったら、普通に怪しい上に「おいこら、お前何しに来たんだよ?」と言いたくなる気持ちもワカル。

2. 過去にSSSS
過去にSSSS(二次的保安検査選出)でチェックをされている。この時も物を大量に持って行った時だった。靴下を脱いて身ぐるみ全部チェックされた。
というのもあって、またこのパターンかよ…的な感じもしなくは無いけど、SSSSとの違いは…

SSSS : 搭乗前に呼び出しされてチェック、時間的拘束も短い
別室 : 入国時にチェック、時間的拘束がめちゃくちゃ長い

という差はある。

原因は不明だけど、情報をちゃんと伝えれば問題なく開放されると思っていたから大きな不安はなかった。ただ、パスポートを没収されると、地味に焦る。

初の首切りジェスチャー

生きていれば色々と体験できるもので、まさかリアル・首切りジェスチャーをされるとは思ってもみなかった。ただ、助けられるパターンだけどね。

場所はVallejoのモーテルで。ちなみに周辺環境はこんな感じ。

モーテルの裏には広い駐車場がある。スーパーと酒屋さんとか、お店があってここで食事を調達することが出来た。なぜかクラッシュした車が置いてあったりして、なかなかの感じがする。

■ 体験の流れ
Maker Faire Bay Areaというイベントの最終日に体調を崩して、モーテルで休んでいたのだけど、ちょっと外に出たら謎の白人に話しかけられた。場所はモーテルの裏口のところで、こんな感じのところになる。

面倒くさいなーと思って少し聞いたら、どーやら謎のタバコを吸わせたいみたい。超怪しいから「今熱あるし、喉痛だし、近くに寄るな」って言っても寄ってくる。これは完全に怪しい…

そのとき、あれ?と視界に入ってきたのが、モーテル内の上り階段あたりのフェンス越しに居たアフリカ系の若い人だった。彼は首と手を横に振りながら関わるな!!と合図してくれていた。
そして、首切りジェスチャーしたあと、手を横に振りながら関わるなと声は出さないで合図していて、さすがに刺激的だった。

ワイ)「いま病気(本気で風邪です)だから近寄るな」
相手)「俺も病気だから」

はい、こいつ完全にやべー奴じゃん!!
って事で距離を取って逃げた。その後、モーテルのロビーで少し心を落ち着かせてから部屋に戻った。

声に出さなくても「お前、やられるから止めろ!!」みたいな合図を出してくれた彼に感謝。ヤバい人もいれば、こういうヘルプを出してくれる人も居るもんですね。

SFMoMa・Musee Mecanique

時間があったので少しSFMoMaとMusee Mecaniqueに寄ってきた。

■ SFMoMa
KUSAMA YAYOIの特別展がやっていたけど、全てソールドアウトで企画・常設展の方を見て回ってきた。

■ Musee Mecanique
始めてMusee Mecaniqueに寄ってきた。アンティーク・レトロゲームのオンパレードで25-50セントで動作させて遊んだり見ることが出来る。

色々な機構で動くものがあってめちゃくちゃ興味を引かれた。自動演奏ピアノを50セントで動かしてみたのがこちら。

https://www.hirotakaster.com/wp-content/uploads/2023/10/571530242ea6d99cbe602584896aef65.mp4

その他

どこかに行くとその現地で教会を巡るのが好きだったりする。しーんと人が全然いない中で、美しい壁画・ステンドグラス・アーチ状の構造物・壁画を見たりする。

どちらもとても美しかった。何年か前にグレース大聖堂に寄ったあと、クリントン元大統領にエンカウントするという事があって、もしかして今回も誰かあったりするかなー?と思ったら、市内で渡邊雄太さんが。

バスで移動するまえに、出待ちしているファンたちにサインしたり応えていてビックリした。

あと、猛烈な物価高・円安にはめちゃくちゃ引くものがあった。話では聞いていたけど、実際にカードを使ったときに円請求で来るメールを見て驚きというか、途中から気にするのを止めた。

街なかで食べたバーガー&ソーダで3,000円、バーで食べた食事で7,000円、イベント会場で食べたチャーハンのプレートが3,000円と日本の感覚の2-3倍はあった。
現状の日米金利格差からの円売りから、円安が進んで150円/$にもなっている。そして、ドル建てGDPで世界4位に転落したというニュースも出ていたりする。恐らく早年内にはYCC・マイナス解除を見通してたりもするだろうし(現状は実質10年ものが日本:0.8%、米国:4.8%、短期は0.07%とゼロ)、日銀の長年の悲願でもある金融正常化に向かって、この辺も是正されていくとは予想はしている。ただ、そう簡単に短期は上げられないし、バブル崩壊〜失われた30年のツケを払うには長い年月が必要になるんだと思われ。

Maker Faire Tokyo/Bay Area 2023

Maker Faire TokyoとBayareaに参加してきた。備忘録として残しておくことにする。

日程

まず日程と場所は…

Maker Faire Tokyo 10/14(土), 10/15(日) 東京ビックサイト
Maker Faire Bay Area 10/13-15, 10/20-22 Mare island

Bay Areaは2019年を最後に終わったのが、また再開というのがニュースになった
すでにMaker Faire Tokyoは決まっていたものの、勢いでエントリーした。2019年の最後に行ったときのはこちらで、あまり深いことは考えないで、再開するなら行ってみよ!!と脊髄反射的にエントリーをした。

問題は日程が被っていることだった。とりあえずスペシャルリクエストのところに、「10/13-15 はMaker Faire Tokyoで参加できないから、10/20-22でお願いしたい」というような事を書いたと思う。
ただ、実際はAcceptされたあとの情報入力で、

みたいな感じで選べるようになっていた。これで安心して、この週の前半は東京で後半はBay Areaという、毎週末はMaker Faireというスケジュールになった。

場所

東京ビックサイトは普通に電車で行ける。
Mare islandってそもそも、どこ?というのは、まずGoogle Mapで調べてみると…どこここ?というかナパじゃね?という遠さだった。

フェリーか車でしか行けないようなところで、まぁ行く手段があればなんとかなるか、といった感じだった。
Maker Faire Bay Areaといえば、サンマテオのEvent Centreと思っていただけに、なんかBay Area?みたいな感じはしたけど。

実際にフェリーでサンフランシスコ市内から向かうときは、San Fransisco Bay Ferryのアプリがあるから、それをスマホに入れておいた。フェリーの場所・運行状況・チケットも買えたりできて便利だと思う。実際はClipperカードでフェリーに乗った($10)。

Maker Faire Tokyo 2023

持っていったのは、大量のソレノイドを使ったもの
作り方・回路図とかはそのまま書いてあったり。

作った理由を聞かれたりしたですが、深い理由はなくて、ソレノイドを100個使ってみたかった…という動機だったりする。気づいたら300個買っていたのがアレだけど。四角と丸で作ってみて、他にも円環だったり色々と作ってみれば良かった。

個人的にテーマ・実験していたのは混雑状況での通信検証だったりした。
ビックサイトは非常に混雑する。その中でBluetooth/WiFi類はほとんど使い物にならなくなるという、イベントあるあるな事になる。そこで、ESP NOWを使ってどれだけ耐性があるか?実験することにした。
構成は次のとおり。

・ソレノイドx16個の箱 x 4 (M5Stamp C3 x4)
・ボタン用 x1 (M5Stamp C3)

全部で5台のM5Stamp(ESP32)でボタンを押したら、他の4台に順番にパケットを投げてソレノイドを動かすといった感じにした。混雑状況でESP NOW(P2P)がどれだけ機能するか?検証を行う場として適していた。

ボタンが押されたら各端末にパケットは1回だけ送信する、ということにしてやってみたところ、接続不良は全くなかった。各端末の距離が近いというのもあるものの不具合はゼロだった。ESP NOWは接続台数が限られているものの、混雑状況で直接通信を行うといったケースでは非常に有用だと感じた。

回って見て、写真を撮ったりして面白いなーと思った作品などなど。

コロナの5類感染症移行後のMaker Faire Tokyo 2023、写真を取り忘れたり、たくさんの人垣が出来ていてちゃんと見れなかった色々なユニークな作品の数々に出会えてとても刺激になった。

Maker Faire Bay Area 2023

久々のMaker Faire Bay Areaというのでテンションも非常に上がった。ただ、持っていくものを決めたのは良いものの、どうやって持っていくか?というのがまず最初に課題になった。持っていったのはこちらの未踏でやったものになる。

これを全部どうやって持っていくか?というのは準備段階からの課題だった。というのも、物品だけで余裕で23kgを越えて、着替えとかも考慮するとトータルの重量は34kgになることが分かった。自分の体重と合わせて、100kgを超える重量になった。
何でこれを持っていくことにしたか?というと、持って行って見せてこれるのか?チャレンジしてみたかった…みたいな感じで、特に深い理由がなかったりする。

これだけの重さの物を持っていく場合、「Fedex/DHLなどの航空便で現地に送る」「頑張って手持ち・キャリーで持っていく」のどちらかを考えた。

これは事前に重量と、どうやって空輸するか?手持ちにするか?検証していたときのもの。一応、事前にMaker Faire Bay Areaのスタッフの方に空輸の依頼も出していた。その場合、イベントの翌日が発送デーになって、再度・現地に行って発送の手配をすることになる。

最後の最後まで決めきれなくて、最終的に手持ち・キャリーに詰めて手持ちで持っていった。ポイントは次の通り、

■ 輸送便
・箱に詰めて送るだけで良い。
・30kgを超える物を持って歩かなくて良い。
・空輸コストが非常に高い。
・空輸中に壊れたときの心理的ダメージ(これは手持ちでも同様)
・イベント終了翌日に現地で発送処理をする面倒さ。

■ 手持ち
・非常に重いので、移動時の体力が心配。
・物品的に怪しい物が大量にあるので、出国・入国検査で問い詰められる可能性(実際に色々とあった….のは別に書こうと思う)
・23Kgオーバーの預け入れ手荷物で重量オーバーでの支払い(+1万円)

ざっとこの辺を検討して、最終的に手持ちで行くことにした。荷物は整理したり最低限必要な物以外は持っていかない事にして、32kgまで抑えた。そして、キャリーとバックパックに分散させることで、オーバーサイズの支払いもしなくて良かった。ただし、10kg近い荷物を背負って移動するのはあまり無いので、自分の体力勝負になった。

■ 場所
実際の場所は元は海軍の倉庫だった場所で、奥まで長テーブル7-8個、15-16mくらいの奥行きのあるところだった。自分のところの倉庫はドアが吹き抜け・風が通りすぎて日中はTシャツでも大丈夫だけど、夕方〜日が落ちるとめちゃくちゃ寒くて、何か1枚着るのが無いとキツかった。

全体のマップはこんな感じ。川べりに倉庫が並んでいて、倉庫ごとにテーマが別れていた。例えばElectoric、ダークルーム、Robotics…etc みたいな感じで。

倉庫ごとにテーマが別れていて、何個か倉庫が続いていく

■ 準備〜イベント
実際のイベント・スケジュールはこんな感じだった。

設営日: 10/19  10:00 am – 6:00 pm
学生向け日:10/20
一般公開:10/21, 22

初日、10/20 の夕方からはHappy Hourでドリンク・食事が参加者に振る舞われる。会場横にはビールの醸造所があって、そこのビールをゴクゴクしてきた。

サンマテオで開催されていた時は、屋外の原っぱで巨大なパエリアを頂きながらだった。今回はあるのかな?と思ったら、倉庫の中だったけど、作品を見ながら自由を味わえる瞬間だった。

ちなみに、Maker Faire Bay Areaのチケットはこんな感じの値段感だった。今は150円/$なので、日本円だと大人は7,500円、学生は4,500円でそれ以下は無料。

こんな遠いところまでチケットを買って人がどれくらい来るんだろうか?と心配していたけど、たくさんのお客さんだった。もちろん、以前のサンマテオほどでは無いにしても、ここまで来るというメンタリティは凄いものがある。
メントスコーラでヒャッハー!!!!の盛り上がりもハンパなかった。

めちゃくちゃ写真を撮っていて(300枚も動画や写真を撮っていたw)気になったものを。「デカい・光る・音がなる」まさにコレといった感じ。

安定のファイヤーもの
マッドマックスに出てきそうなバイクからファイヤー
ちびっこ達と楽しそう

ちなみに、開催は10/20-22の3日間になるのだけど、最終日はこういうイベントでは始めて途中撤退をした。
喉痛が酷くて、最初はイベントあるある・説明しすぎて喉がやられたのかも…と思っていたら、発熱もしはじめて、どうやら風邪をひいてしまった。最終日の朝は雨・吹き抜けの倉庫で続けると、更に悪化して完全に倒れてしまう…と思って、運営に説明して作品を撤収・モーテルに帰って倒れて寝込んだ。風邪薬を分けてもらえた鈴木さんには本当に感謝!!

撤収まえの朝の雨

2019年のサンマテオで終わったと思ったBay Areaが再始動した。
体調悪化で微妙な終わり方をしたMaker Faire Bay Areaになったけど、勢いでエントリして行ってきて、4年ぶりに「お久しぶり」といった感じだった。
短期間に2箇所行ってみて、ユニークさのMaker Faire Tokyo、大物・ファイヤーはBay Areaみたいな感じはする。4年に一度の開催くらいのペースでも、再始動してまた来年のどちらでも、Maker Faire Bay Areaは続いていって欲しい。

あと、他にもSFMoMa、Musee Mecanique、大量の荷物を持っていった結果、入管で起きたこととか、道中記は別途書き残しておこうかなと。

GPTbot

このサーバーにもGPTBotのアクセスが来ていた。
どのくらい来ているのかな?と思ってみてみた傾向が次ので、比較として同じ日・時間のGooglebotとのアクセス数の比較で。

GPTBotは来るときは一気にくる(約10秒に1アクセスでガツンと来る)。ちなみに、この日からアクセスは来ていない。Googlebotと比べると、ガツンと来ているのが分かる。

そして、GPTBotのアクセス元はAzureからで、次のような感じのアクセスログが大量に残っていた。

www.hirotakaster.com:443 20.15.240.86 - - [09/Aug/2023:16:58:07 +0900] "GET /?... HTTP/1.1" 200 16887 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)"

GETパラメータ以下は略だけど、「ちょっ…脆弱性診断でもしに来ているのか?」みたいなパラメータが付いていたりで、ほんと何でも取っていく感じがした。これは、自分のサーバーへのログで他の人はまた違うのかもしれないけど、一気に来すぎるのとパラメータをバンバン付けてくるのは、ちょっとお行儀が良くない感じがした。

100

100 solenoids were aggregated and made into one devices.

https://www.hirotakaster.com/wp-content/uploads/2023/06/IMG_6038_Trim-3.mp4

spec
Solenoid x 100(https://www.sparkfun.com/products/11015)
M5Stamp C3(I2C controller), self-designed PCB(16 solenoids assigned on 1 board)

sample source code.

#include <Adafruit_MCP23X17.h>
#define MCP_LEN 1
Adafruit_MCP23X17 mcp[8] = {Adafruit_MCP23X17(), Adafruit_MCP23X17 (),
                            Adafruit_MCP23X17(), Adafruit_MCP23X17 (),
                            Adafruit_MCP23X17(), Adafruit_MCP23X17 (),
                            Adafruit_MCP23X17(), Adafruit_MCP23X17 ()};

/* 
 *  10x10 solenoid x-y matrix position
 *            (9 9)
 *  |---------|  
 *  |         |
 *  |         |
 *  |         |
 *  |---------|
 *(0, 0)
 */
void updown(int x, int y, int d) {
  int s = ( x * 10 + y ) / 16 ;
  int p = ( x * 10 + y ) % 16 ;
  mcp[s].digitalWrite(p, HIGH);
  delay(d);
  mcp[s].digitalWrite(p, LOW);
  delay(d);
}

void testfunc() {
  for (int i = 0; i < MCP_LEN; i++) {
    for (int j = 0; j < 16; j++) {
      mcp[i].digitalWrite(j, HIGH);
      delay(500);
      mcp[i].digitalWrite(j, LOW);
      delay(500);
    }
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("Adafruit_MCP23X17 Test!");
  pinMode(6, INPUT);

  for (int i = 0; i < mcp_len; i++) {
    if (!mcp[i].begin_I2C(0x20+ i)) {
      Serial.print(i);
      Serial.println(" Error.");
    }
    delay(100);
    for (int j = 0; j < 16; j++) {
      mcp[i].pinMode(j, OUTPUT); 
      delay(40);
    }  
  }
  Serial.println("Looping...");
}

void loop() {
  testfunc();
}
I2C test
connect to 100 solenoids.

4×4, circle type enclosure for test.

4×4, circle type enclosure

board designed.
IO Expander : MCP23017
MOSFET : 2SK4017
register: RK73B1JTTD103J 1608(0603)
diode : GS1010FL
connector : S2B-PH-K-S
solenoid : ZHO-0420S-05A4.5(5V)

Solenoid mounter 3D model is three parts.
Top is just an solenoids mounter, 2nd parts holds up to 100 solenoids and guides the cables, 3rd is guide to the output cables.

 

 

Exhibit at Maker Faire Tokyo 2023 with 4×4, 10×10, circle type.

100, 16, circle type.

Ogaki Mini Maker Faire 2022

来月、2022/12/03, 04の土日に開催されるOgaki Mini Maker Faireに出展します。

自分は”ひかりもの“というもので、次の物たちを持っていきます。

Maker Faire Tokyo
Maker Faire Tokyo
なぜ色が変わって同期するか?という仕組み動画のネタバレ感

玉は色々と物を取り寄せてトータルで50個ほど作った。限定10個くらいなら、欲しい!!という人にはあげても良いかなーと思っていたりする。
そして、ESP入り(だった…あまりの激しさに破壊された)フラフープ

Maker Faire Bayarea
上モノ フェス
Exploratorium

そして、かなり改造しまくった LED/LCD ドリルギター

久々の大垣、そしてかなり久々の暗いエリアでヒカリモノ、ちょっぴり楽しみだったりする。

QUIC handshake with mbedTLS

プロトコルを知るには書いて動かしてみれ!!という事で、mbedTLSを使ってQUICのhandshakeまで行ってみることにした。時間があるときに、ちょくちょく調べたりしてmbedTLSを使ってNginX(quic impl)と通信を行うまで確認した。
mbedTLSでQUICの公式サポートは今のところは無い。まぁ、やってみたった的な感じ。みんな色んな言語で実装されている物があったりで、何か楽しそうだなーと思って。

qlog用にrandom/handshake_traffic_secret/application_traffic_secret/exporter_master_secretを吐き出してWiresharkで復号化して見れる。

■ 参考

RFC 9000 QUIC: A UDP-Based Multiplexed and Secure Transport
RFC 9001 Using TLS to Secure QUIC

■ コード

quic 側 : mbedtls_quic
mbedtls側 : mbedtls (mbedtls-3.2.1-quic branch)
mbedTLSは現時点での最新 3.2.1をベースにした。ただし、TLS 1.3への対応はとりあえずは動く程度な感じ。TLS extensionで対応していないサーバと通信すると見事にERRORを起こしたりするし、これから感はしている。
組込みで使われる物だけど、今なら素直にwolfsslを使う方が良い気はする。すでにQUICに対応しているし、みんな大好きESP32にも組込むことが出来る(esp-idfからビルドオプションで組込める)。

■ mbedtls側の改造

mbedtls は mbedtls_config.h をいじってTLSの機能を選択利用する実装になっている。超少ないリソースで動作するから、選択的に機能を選んで組込んでいく感じ。TLS 1.3以外の機能は不要なのでオフって使うことにする。diff はこの程度。改造したポイントは次あたり…

・MBEDTLS_QUIC_TRANSPORT 機能を mbedtls_config.h に追加
 これでQUICの処理が必要な部分についての変更を全体に加える。
・Transport Parameterの実装追加
 RFC 9000 7.4. Transport Parameters
 RFC 9001 8.2. QUIC Transport Parameters Extension
・QUICパケットの暗号化処理部分をフロント側で処理させる

mbedTLSはTLS 1.3のステートマシン・鍵生成器としてだけ使って、最小限の修正に留めている。あとTLS 1.3はTCP Stream想定なのか、複数に分割されたUDPパケット(Handshake時の証明書類が送られてくるところとか)がある場合、いわゆる EAGAIN のようにフロント側にパケット要求するような処理に変更していたりする。

■ QUIC側の処理

とりあえずハンドシェイクまでで、メインのコードはこんな感じ。

#include <stdio.h>
#include "quic_common.h"
#include "quic_mbedtls.h"

int main() {
    int ret = 0;

    // init
    quic_mbedtls_ctx ctx;
    quic_mbedtls_init(&ctx, "www.hirotakaster.com", 8443);
    quic_mabedtls_handshake(&ctx);

    // ouput keys
    dump_handshake_random_byte(&ctx);
    dump_handshake_traffic_secret(&ctx);
    dump_application_traffic_secret(&ctx);
    dump_exporter_master_secret(&ctx);
    dump_resumption_master_secret(&ctx);

    //
    sleep(2);

    return 0;
}

ハンドシェイクを行ってkeylogを出力するまで。ハンドシェイクまでだからコードは2個だけ、
quic_client.c : QUIC側のヘッダ処理類
quic_mbedtls.c : 暗号化類・ネットワークIO類
quic_mbedtls_init の中ではUDPのオープン・初期化処理を行って、quic_mabedtls_handshake でQUIC/TLS 1.3のハンドシェイクを行う。流れ的には次にwrite/readの処理を入れていく感じの想定で。mbedTLSではmbedtls_ssl_handshakeでBIO(mbedtls_ssl_set_bio)でネットワークとのread/writeをハンドリングすることが出来る。
鍵類はIO用の構造体の中に(デバッグ用としても)ざっくり入れている。

typedef struct {
    mbedtls_x509_crt cacert;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context drbg;
    mbedtls_ssl_context ssl;
    mbedtls_ssl_config conf;
    mbedtls_timing_delay_context timer;

    u_char initial_secret[32];
    u_char client_secret[32];
    u_char client_key[16];
    u_char client_iv[12];
    u_char client_hp[16];
    u_char server_secret[32];
    u_char server_key[16];
    u_char server_iv[12];
    u_char server_hp[16];

    BOOLEAN secret_initialized;
    u_char client_random_byte[32];

    u_char client_derive_secret[32];
    u_char client_derive_key[16];
    u_char client_derive_iv[12];
    u_char client_derive_hp[16];

    u_char server_derive_secret[32];
    u_char server_derive_key[16];
    u_char server_derive_iv[12];
    u_char server_derive_hp[16];

    QUIC_MBEDTLS_SOCK sock;
} quic_mbedtls_ctx;
QUIC KeySchedule : https://gist.github.com/martinthomson/c254bbc4214e8b3d4f38372b9afce18d

他のコードを参考にしないで、wiresharkとお友達になって勢いで書いたのもあって、最初にナニコレ?と思ったのが、可変長エンコーディングだった(RFC 9000 16.  Variable-Length Integer Encoding)。先頭の2ビットを見てから値が決まる感じで、パケットのいたる所でこれが利用されている。例えば、Initial Packet だと、

  RFC 9000 17.2.2. Initial Packet
  Initial Packet {
     Header Form (1) = 1,
     Fixed Bit (1) = 1,
     Long Packet Type (2) = 0,
     Reserved Bits (2),
     Packet Number Length (2),
     Version (32),
     Destination Connection ID Length (8),
     Destination Connection ID (0..160),
     Source Connection ID Length (8),
     Source Connection ID (0..160),
     Token Length (i),
     Token (..),
     Length (i),
     Packet Number (8..32),
     Packet Payload (8..),
   }

   RFC 9000 19.6. CRYPTO Frames
   CRYPTO Frame {
     Type (i) = 0x06,
     Offset (i),
     Length (i),
     Crypto Data (..),
   }

この (i) という所が可変長。Destination Connection IDとかに出ているのは、前のLengthから長さが決定されたりする。みたいな感じで、可変長がバンバン出てくる。まぁ、関数( quic_mbedtls_conv_vlie : 可変長読み込み、quic_mbedtls_set_vlie : 可変長書き込み)を用意して、あとはOKだった。

initial packetを送受信して、あれ?と思ったのが ACK だった(RFC 9000 19.3. ACK Frames)。この中にある、ack_delay で時間を測定してるんすね。そして、デバックしながらやっていると、NginX側からPINGフレームが飛んできて、生存確認も行っているのをやってみてから分かった。よく出来ているプロトコルっすねー。
あぁ…そこまでやってらんないぉ…ってことで、固定値を入れているけど、本当はちゃんとマイクロ秒でカウントしないといけないんだけど…そしてPINGを思いっきり無視&スルーしちゃっている><
以下な感じでパケット保護・復号化後のフレームを見ている。

int quic_mbedtls_decode_frame(const u_char *data, uint32_t length, QUIC_FRAME *frame) {
    int i, j = 0;

    for (i = 0; i < length;) {
        u_char d = data[i];
        if (data[i] == QUIC_FRAME_PADDING) {
            i += 1;
        } else if (data[i] == QUIC_FRAME_CRYPTO) {
            frame[j].frame = &data[i];
            i += 1;

            uint8_t o, l;
            uint64_t ov, lv;
            // get crypt frame offset
            quic_mbedtls_conv_vlie(data + i, &o, &ov);
            i += o;

            // get crypt frame length
            quic_mbedtls_conv_vlie(data + i, &l, &lv);
            i += l;
            i += lv;

            frame[j].type = QUIC_FRAME_CRYPTO;
            frame[j].length = 1 + o + l + lv;
            j += 1;
        } else if (data[i] == QUIC_FRAME_PING) {
            // とりあえず無視
            i += 1;

        } else if (data[i] == QUIC_FRAME_ACK_2 || data[i] == QUIC_FRAME_ACK_3) {
            frame[j].frame = &data[i];
            i += 1;

            uint8_t la, ad, arc, far, ar;
            uint64_t lav, adv, arcv, farv, arv;

            // get largetst ack
            quic_mbedtls_conv_vlie(data + i, &la, &lav);
            i += la;

            // get ack delay
            quic_mbedtls_conv_vlie(data + i, &ad, &adv);
            i += ad;

            // get ack range count
            quic_mbedtls_conv_vlie(data + i, &arc, &arcv);
            i += arc;

            // get first ack range
            quic_mbedtls_conv_vlie(data + i, &far, &farv);
            i += far;

            frame[j].type = QUIC_FRAME_ACK_2;
            frame[j].length = 1 + la + ad + arc + far;
            j += 1;
        }
    }
    return j;
}

ペイロードの暗号化・復号化は1個の関数で処理していて、ヘッダの保護を設定・解除したあとに、この関数で処理するようにしている。
ヘッダの保護については、鍵スケジュールで得られるkey/iv/hp類を利用。

int quic_mbedtls_crypt_frame(u_int8_t enc_dec,
            unsigned char *key, size_t keylen,
            unsigned char *iv, size_t ivsize,
            unsigned char *ad, size_t adsize,
            unsigned char *from, size_t fromsize,
            unsigned char *to, size_t tosize, size_t *olen) {
    int ret;
    mbedtls_gcm_context ctx;
    mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
    int output_len;
    unsigned char tag_output[16];
    size_t tag_len = sizeof(tag_output);
    size_t tmpolen;

    mbedtls_gcm_init(&ctx);
    ret = mbedtls_gcm_setkey(&ctx, cipher, key,  keylen);

    ret = mbedtls_gcm_starts(&ctx, enc_dec, iv, ivsize); 
    ret = mbedtls_gcm_update_ad(&ctx, ad, adsize);
    ret = mbedtls_gcm_update(&ctx, from, fromsize, to, tosize, olen);
    ret = mbedtls_gcm_finish(&ctx, NULL, 0, &tmpolen, tag_output, sizeof(tag_output) );

    if (enc_dec == MBEDTLS_GCM_ENCRYPT) {
        memcpy(to + *olen, tag_output, sizeof(tag_output));
        *olen = *olen + 16;
    }

    mbedtls_gcm_free(&ctx);
    return ret;
}

// 暗号化
quic_mbedtls_crypt_frame(MBEDTLS_GCM_ENCRYPT,
                ctx->client_key, sizeof(ctx->client_key)*8,
                nonce, sizeof(nonce),
                send_buffer, plane_header_total_len,
                payload, payload_len,
                enc_payload, sizeof(enc_payload), &olen);

// 復号化
quic_mbedtls_crypt_frame(MBEDTLS_GCM_DECRYPT,
    (QUIC_FRAME_LONG_PACKET_TYPE_MASK(quic_mbedtls_recv_buffer[0]) == QUIC_PACKET_INITIAL ? ctx->server_key : ctx->server_derive_key),
    (QUIC_FRAME_LONG_PACKET_TYPE_MASK(quic_mbedtls_recv_buffer[0]) == QUIC_PACKET_INITIAL ? sizeof(ctx->server_key)*8 : sizeof(ctx->server_derive_key)*8),
            dec_nonce, sizeof(dec_nonce),
            quic_mbedtls_recv_buffer, packet.header_len,
            quic_mbedtls_recv_buffer + packet.header_len,
            packet.length - packet.packet_number_length,
            dec_payload, sizeof(dec_payload), &dec_olen);

■やっみて…

あー、QUIC面白いっすねーという感想だった。とりあえずQUIC/TLS 1.3ハンドシェイクする所までやってみるか…という勢いでやってみたのだけど、コードはぐしゃっと書いたからカヲっているw


組込み向けということで、サイズは731216byte(714kbyte弱)になってしまった…TLS 1.2をガッサリ消してもこのサイズというのは、地味に心が折れてしまう…orz
何とか200Kbyte以下くらいまでには抑えたいのだけど。

あと、mbedTLSの実装も一通り見ながら出来たのは良かったかも。なーんとなく、mbedTLSのオフィシャルにQUIC入るのかなぁ…というのは、ちょっぴり疑問な気はした。直撃でmbedTLSのコードセット類を大改造して、TLS1.3/UDP(QUIC)を両立させるには、かなりの内部修正が必要というか、API・関数類が思いっきり新規になるじゃないかなーと。その場合、何個かワイ的な感じでコードを追加して、mbedTLS用QUICみたいな実装がポコポコ出てきちゃったりするのかしら…みたいな気がしたり。
次はショートパケットでHTTP3でNginXをおしゃべりする所くらいまではやってみよかなと。

RPi4 ROS YDLIDAR X4でSLAM

Raspberry Piとlidar(YDLIDAR X4)を使ってSLAMの環境を作ってみた。手持ちでRPi・Lidarを持って歩いてSLAM・地図の生成からシュミレーションを行うまで。

https://www.hirotakaster.com/wp-content/uploads/2022/08/300873130_2075060639332932_2109182721347563783_n.mp4

■ 環境

  • Raspberry Pi 4 Model B 4Gbyte memory
  • Ubuntu 20.04.5 LST/ROS Noetic
  • YDLIDAR X4
  • モバイルバッテリー(5V/3A出力)
  • WiFi無線機子機

    Raspberry Piについては昨今、かなり入手が困難な状況が続いている。メルカリで出品されている物も、2倍以上の価格だったり、入手が難しい。一体どこで入手できるのかしら….????
    使いたいのに無いなー…という事をTLでつぶやいたら、HDMIが死んでいるRPi4を送って貰えることに。手持ちでRPi1〜4はあるものの、使っている物ばかりで、使えるもの1台も無いところに、世の中には神が居るものですね。

    モバイルバッテリーは5V/3A出力できる物を使うことに。一応、WiFi・LIDAR・GPIO(TB6612FNG Dual Motor Driver: モーターはlipo外付けで)を同時に動かしてみても問題なかった。
    WiFi無線子機は、RPi自体のWiFiは弱いので補強として使えればよき!!ということで、手持ちのがあったので使っている。

■ Ubuntuの設定

VNC経由でRPiを操作できるようにする。リモートでrvizの画面を確認したり、リモートが出来るとHDMI出力がなくても操作できるようになるので、何かと便利になる。
こちらの記事を参考にさせて頂きました。

・LTS Serverに “sudo apt-get install ubuntu-desktop” をインストール
・Setting => Sharing => Screen Sharing から”Allow connections to control the screen”にチェックを入れる
・Reuired a password を選択して、VNCから接続するときのパスワードを適宜設定しておく
・接続するNetoworkを設定しておく

また、WindowsのVNC Viewerからアクセスするとき暗号化がされていると接続できないので、以下のUbuntuの画面共有の暗号化をオフにしておく。

sudo gsettings set org.gnome.Vino require-encryption false

つぎに、RPiはHDMIからディスプレイの解像度を取得しているとのことで、HDMI・ディスプレイ無しでVNC接続するために、ダミーのグラフィックドライバをインストールして設定しておく。

sudo apt install xserver-xorg-video-dummy
sudo tee /usr/share/X11/xorg.conf.d/80-dummy.conf <<EOF
Section "Device"
    Identifier  "Configured Video Device"
    Driver      "dummy"
    VideoRam 256000
EndSection

Section "Monitor"
    Identifier  "Configured Monitor"
    HorizSync 5.0 - 1000.0
    VertRefresh 5.0 - 200.0
    # 1920x1080 59.96 Hz (CVT 2.07M9) hsync: 67.16 kHz; pclk: 173.00 MHz
    Modeline "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
EndSection

Section "Screen"
    Identifier  "Default Screen"
    Monitor     "Configured Monitor"
    Device      "Configured Video Device"
    DefaultDepth 24
    SubSection "Display"
        Depth 24
        Modes "1920x1080"
    EndSubSection
EndSection

Section "InputClass"
    Identifier "system-keyboard"
    MatchIsKeyboard "on"
    Option "XkbLayout" "jp,us"
    Option "XkbModel" "jp106"
    Option "XkbVariant" ",dvorak"
    Option "XkbOptions" "grp:alt_shift_toggle"
EndSection
EOF

これでVNCでリモート接続環境ができあがり。一旦、RPiを再起動してHDMI接続無しでVNC接続できることを確認しておく。

■ ROS Noeticのインストール

次にROSの環境構築を行う。こちらの記事を参考にさせて頂きました。サクッとインストールで自分の場合は放置して気づいたら終わっていた。

# リポジトリの登録
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt install curl  -y
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

# ROS のインストール、ナビゲーション関係のパッケージも同時に
sudo apt update
sudo apt install ros-noetic-desktop-full -y
sudo apt-get -y install ros-noetic-navigation ros-noetic-move-base

# パッケージ類の依存関係をいい感じにしてくれるrosdepの初期化
sudo apt-get install python3-rosdep -y
sudo rosdep init
rosdep update

# 環境変数・パスの追加
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
source /opt/ros/noetic/setup.bash

# ビルドで必要になるcatkinのインストール
sudo apt-get -y install python3-rosinstall
sudo apt-get install -y python3-catkin-tools

■ YDLIDAR SDK/ドライバのインストール

YDLIDARの環境整備で、SDK・ドライバ類をインストールしていく。
YDLidar-SDK、ydlidar_ros_driver の順でインストールして動作確認していく。書いてある通りにインストールすれば良い感じ。

# YDLIDAR SDKからインストール
# 作業ディレクトリは"$HOME/work"以下を想定
sudo apt-get install swig -y
sudo apt-get install python3-pip -y

mkdir $HOME/work
cd $HOME/work
git clone https://github.com/YDLIDAR/YDLidar-SDK.git
mkdir YDLidar-SDK/build
cd YDLidar-SDK/build
cmake ..
make
sudo make install

# YDLIDAR ROS Driverのビルド
mkdir -p $HOME/work/ydlidar_ws/src/
cd $HOME/work/ydlidar_ws/src/
git clone https://github.com/YDLIDAR/ydlidar_ros_driver.git
cd $HOME/work/ydlidar_ws
catkin_make

# 環境変数に追加
echo "source $HOME/work/ydlidar_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

# udevに追加(/dev/ydlidar を使えるようにする)
chmod 0777 src/ydlidar_ros_driver/startup/*
sudo sh src/ydlidar_ros_driver/startup/initenv.sh

ここまでやったら、あとは動作確認ですね。X4 lidarをRPi4に接続して、VNCで接続してそれぞれ立ち上げていきます。

# 左上のターミナル
roscore
# 左下のターミナル
roslaunch ydlidar_ros_driver X4.launch
# 右上のターミナル
rviz

rvizの左下の”Add”から”By topic”タブの、/scan LaserScan を選択。

あとは、左のパネルから
“Global Options” => “Fixed Frame” を”base _footprint” or “laser_frame” を選択
“LaserScan” => “Size (m)” を 0.06 とかに設定
すると、lidarでスキャンした点が表示されますね。これでlidar側の準備はとりあえず完了で、次はSLAMの環境を用意していく。

■ cartographerのビルド

cartographerはGoogleが公開しているSLAMパッケージですね。これをRPi4上でビルドしていきます。

sudo apt-get update
sudo apt install ninja-build stow -y
cd 
mkdir catkin_ws
cd catkin_ws
wstool init src
wstool merge -t src https://raw.githubusercontent.com/cartographer-project/cartographer_ros/master/cartographer_ros.rosinstall
wstool update -t src

sudo rosdep init
rosdep update
rosdep install --from-paths src --ignore-src --rosdistro=${ROS_DISTRO} -y
# ここで100%コケます。libabsl-dev の依存関係が解決できないというエラーで以下が表示されると思います。
# ERROR: the following packages/stacks could not have their rosdep keys resolved to system dependencies:
# cartographer: [libabsl-dev] defined as "not available" for OS version 
# そこで以下のコマンドを実行してlibabsl-devの行を削除して、再度、rosdistro install を実行
sed -i -e '/libabsl\-dev/d' src/cartographer/package.xml 

# cartographer用のコンパイラ、abseil-cpp をインストール
# 5分程度で終わりました。
src/cartographer/scripts/install_abseil.sh

####
# pythonのバージョンを変更
# ビルド時にpythonのデフォルトが2.7だと失敗します。そこで、update-alternatives コマンドでデフォルトのバージョンを3.8にします。
# 既にデフォルトが3.8の場合は飛ばしてOK、3.8、2.7を登録する
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1

# update-alternatives で python 3.8 を選択する
sudo update-alternatives --config python
There are 2 choices for the alternative python (providing /usr/bin/python).

  Selection    Path                Priority   Status
------------------------------------------------------------
  0            /usr/bin/python2.7   1         auto mode
* 1           /usr/bin/python3.8   1         manual mode

Press <enter> to keep the current choice[*], or type selection number: 1
####

# cartographer をninjaでビルド
# makeでビルドすると、RPi4上ではコンパイルでスワップを起こしてビルドが止まったり失敗します。
# 上手い手順もあると思いますが、手順通りninjaでビルドします。
# 巨大なビルドなので他のプロセス&アプリは落としておいた方が良いです。1時間くらいはかかるので、放置しておきましょう。
# また、途中でビルドが止まって動かなくなる事があります。その場合はランレベルをsudo systemctl set-default multi-userに変えて、余計なプロセスを落としてビルドします。
catkin_make_isolated --install --use-ninja
source install_isolated/setup.bash
echo "source $HOME/catkin_ws/install_isolated/setup.bash" >> ~/.bashrc

次にros用の起動・設定ファイルを作成します。こちらの記事を参考にさせて頂きました。

/home/ubuntu/catkin_ws/install_isolated/share/cartographer_ros/launch/ydlidar_2d.launch を編集

<?xml version="1.0" ?>
<launch>
  <node name="ydlidar_lidar_publisher"  pkg="ydlidar_ros_driver"  type="ydlidar_ros_driver_node" output="screen" respawn="false" >
    <!-- string property -->
    <param name="port"         type="string" value="/dev/ydlidar"/>
    <param name="frame_id"     type="string" value="laser_frame"/>
    <param name="ignore_array"     type="string" value=""/>

    <!-- int property -->
    <param name="baudrate"         type="int" value="128000"/>
    <!-- 0:TYPE_TOF, 1:TYPE_TRIANGLE, 2:TYPE_TOF_NET -->
    <param name="lidar_type"       type="int" value="1"/>
    <!-- 0:YDLIDAR_TYPE_SERIAL, 1:YDLIDAR_TYPE_TCP -->
    <param name="device_type"         type="int" value="0"/>
    <param name="sample_rate"         type="int" value="5"/>
    <param name="abnormal_check_count"         type="int" value="4"/>

    <!-- bool property -->
    <param name="resolution_fixed"    type="bool"   value="true"/>
    <param name="auto_reconnect"    type="bool"   value="true"/>
    <param name="reversion"    type="bool"   value="false"/>
    <param name="inverted"    type="bool"   value="true"/>
    <param name="isSingleChannel"    type="bool"   value="false"/>
    <param name="intensity"    type="bool"   value="false"/>
    <param name="support_motor_dtr"    type="bool"   value="true"/>
    <param name="invalid_range_is_inf"    type="bool"   value="false"/>
    <param name="point_cloud_preservative"    type="bool"   value="false"/>

    <!-- float property -->
    <param name="angle_min"    type="double" value="-180" />
    <param name="angle_max"    type="double" value="180" />
    <param name="range_min"    type="double" value="0.1" />
    <param name="range_max"    type="double" value="12.0" />
    <!-- frequency is invalid, External PWM control speed -->
    <param name="frequency"    type="double" value="10.0"/>
  </node>

  <node
     pkg="tf"
     type="static_transform_publisher"
     name="base_link_connect"
     args="0 0 0 0 0 0 /base_link /laser_frame 100"
  />

  <node
    name="cartographer_occupancy_grid_node"
    pkg="cartographer_ros"
    type="cartographer_occupancy_grid_node"
    args="-resolution 0.05"
  />
  <node
    name="cartographer_node"
    pkg="cartographer_ros"
    type="cartographer_node"
    args="-configuration_directory $(find cartographer_ros)/configuration_files -configuration_basename ydlidar_2d.lua"
    output="screen">
  </node>

  <node
    name="rviz"
    pkg="rviz"
    type="rviz"
    required="true"
    args="-d $(find ydlidar_ros_driver)/launch/lidar.rviz"
  />
</launch>

/home/ubuntu/catkin_ws/install_isolated/share/cartographer_ros/configuration_files/ydlidar_2d.lua を編集

include "map_builder.lua"
include "trajectory_builder.lua"

options = {
  map_builder = MAP_BUILDER,
  trajectory_builder = TRAJECTORY_BUILDER,
  map_frame = "map",
  tracking_frame = "base_link",
  published_frame = "base_link",
  odom_frame = "odom",
  provide_odom_frame = false,
  publish_frame_projected_to_2d = false,
  use_odometry = false,
  use_nav_sat = false,
  use_landmarks = false,
  num_laser_scans = 1,
  num_multi_echo_laser_scans = 0,
  num_subdivisions_per_laser_scan = 1,
  num_point_clouds = 0,
  lookup_transform_timeout_sec = 0.2,
  submap_publish_period_sec = 0.3,
  pose_publish_period_sec = 5e-3,
  trajectory_publish_period_sec = 30e-3,
  rangefinder_sampling_ratio = 1.,
  odometry_sampling_ratio = 1.,
  fixed_frame_pose_sampling_ratio = 1.,
  imu_sampling_ratio = 1.,
  landmarks_sampling_ratio = 1.,
}

MAP_BUILDER.use_trajectory_builder_2d = true

TRAJECTORY_BUILDER_2D.min_range = 0.
TRAJECTORY_BUILDER_2D.max_range = 10.
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 5.
TRAJECTORY_BUILDER_2D.use_imu_data = false
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true

POSE_GRAPH.constraint_builder.min_score = 0.65
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.7

POSE_GRAPH.optimization_problem.local_slam_pose_translation_weight = 1e5
POSE_GRAPH.optimization_problem.local_slam_pose_rotation_weight = 1e5
POSE_GRAPH.optimization_problem.odometry_translation_weight = 1e5
POSE_GRAPH.optimization_problem.odometry_rotation_weight = 1e5
POSE_GRAPH.optimization_problem.huber_scale = 1e3

TRAJECTORY_BUILDER_2D.ceres_scan_matcher.occupied_space_weight = 10
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight = 40

TRAJECTORY_BUILDER_2D.submaps.num_range_data = 120
TRAJECTORY_BUILDER_2D.motion_filter.max_distance_meters = 0.1
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.2)

return options

■ SLAM・地図の作成

準備が終わったので実行します。

# 左上のターミナル
roscore
# 左下のターミナル
roslaunch cartographer_ros ydlidar_2d.launch

rvizのコンパネから”Add”=>”By topic”=>Mapを選択する。

■ 地図の保存

出来た地図の保存はmap serverで行う。保存した地図は、同じくmap serverを使って配信する。

■ 3Dモデルでシュミレーション

シュミレーションはgazeboを使う。ここで、gazebo用にSTLのモデルを生成するわけで、便利なmap2gazeboがあったので、このまま使ってみる。mapについてはmap serverでpublishしたままに。
こちらを参考にさせていただきました。

■ Turtlebot3でnavigation

地図とシュミレーション環境が出来たので、Turtlebo3でナビゲーションしてみます。

https://www.hirotakaster.com/wp-content/uploads/2022/09/AGDRec_Trims.mp4

RPi4のスペックだと、かなりもっさりとした動きになるので、PC環境で行います。またROSの環境を作るのは面倒なので、Docker/VNCを利用します。ブラウザで利用できるので、めちゃくちゃ便利ですね。

docker run -p 6080:80 --shm-size=2048m tiryoh/ros-desktop-vnc:noetic

rviz/gazeboで動作させると

https://www.hirotakaster.com/wp-content/uploads/2022/09/novnc2-1.mp4

実際にロボットをlidarで測定した環境で動作させることが出来ました。ROSの先達さん達に感謝です。

elastic 8.x on google colab

here step is how to use elastic 8.3.1 on google colab.

download elastic, cgroup-tools

In [1]:
%%bash

rm -rf elasticsearch*
wget -q https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.3.1-linux-x86_64.tar.gz
tar -xzf elasticsearch-8.3.1-linux-x86_64.tar.gz
sudo chown -R daemon:daemon elasticsearch-8.3.1/
umount /sys/fs/cgroup
apt install cgroup-tools
Reading package lists...
Building dependency tree...
Reading state information...
The following package was automatically installed and is no longer required:
  libnvidia-common-460
Use 'apt autoremove' to remove it.
The following additional packages will be installed:
  libcgroup1
The following NEW packages will be installed:
  cgroup-tools libcgroup1
0 upgraded, 2 newly installed, 0 to remove and 62 not upgraded.
Need to get 108 kB of archives.
After this operation, 393 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libcgroup1 amd64 0.41-8ubuntu2 [42.0 kB]
Get:2 http://archive.ubuntu.com/ubuntu bionic/universe amd64 cgroup-tools amd64 0.41-8ubuntu2 [66.2 kB]
Fetched 108 kB in 0s (1,138 kB/s)
Selecting previously unselected package libcgroup1:amd64.
(Reading database ... 155639 files and directories currently installed.)
Preparing to unpack .../libcgroup1_0.41-8ubuntu2_amd64.deb ...
Unpacking libcgroup1:amd64 (0.41-8ubuntu2) ...
Selecting previously unselected package cgroup-tools.
Preparing to unpack .../cgroup-tools_0.41-8ubuntu2_amd64.deb ...
Unpacking cgroup-tools (0.41-8ubuntu2) ...
Setting up libcgroup1:amd64 (0.41-8ubuntu2) ...
Setting up cgroup-tools (0.41-8ubuntu2) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for libc-bin (2.27-3ubuntu1.3) ...
/sbin/ldconfig.real: /usr/local/lib/python3.7/dist-packages/ideep4py/lib/libmkldnn.so.0 is not a symbolic link

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

start elastic on backgroud

In [2]:
%%bash --bg

sudo -H -u daemon elasticsearch-8.3.1/bin/elasticsearch
Starting job # 0 in a separate thread.

create access password

In [3]:
!/content/elasticsearch-8.3.1/bin/elasticsearch-setup-passwords auto -url "https://localhost:9200"
******************************************************************************
Note: The 'elasticsearch-setup-passwords' tool has been deprecated. This       command will be removed in a future release.
******************************************************************************

Initiating the setup of passwords for reserved users elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user.
The passwords will be randomly generated and printed to the console.
Please confirm that you would like to continue [y/N]y


Changed password for user apm_system
PASSWORD apm_system = 5wJfMLTnkHwSY7lbpr6y

Changed password for user kibana_system
PASSWORD kibana_system = rE40UfIbndtwONL6q33D

Changed password for user kibana
PASSWORD kibana = rE40UfIbndtwONL6q33D

Changed password for user logstash_system
PASSWORD logstash_system = DOJfaIC9toV7tgJB33ON

Changed password for user beats_system
PASSWORD beats_system = iVpsYjuzPaL6wIOZQEKx

Changed password for user remote_monitoring_user
PASSWORD remote_monitoring_user = wsxURS5iLLi3hrSf8qw9

Changed password for user elastic
PASSWORD elastic = Uzv4aE6oFfpMrDVy1nql

check a elastic process, live page. “Enter host password for user ‘elastic’:” use above elastic user password.

In [5]:
!ps -ef | grep elastic
!curl --cacert /content/elasticsearch-8.3.1/config/certs/http_ca.crt -u elastic -H 'Content-Type: application/json' -XGET https://localhost:9200/?pretty=true
root         399     397  0 08:19 ?        00:00:00 sudo -H -u daemon elasticsearch-8.3.1/bin/elasticsearch
daemon       400     399  8 08:19 ?        00:00:14 /content/elasticsearch-8.3.1/jdk/bin/java -Xms4m -Xmx64m -XX:+UseSerialGC -Dcli.name=server -Dcli.script=elasticsearch-8.3.1/bin/elasticsearch -Dcli.libs=lib/tools/server-cli -Des.path.home=/content/elasticsearch-8.3.1 -Des.path.conf=/content/elasticsearch-8.3.1/config -Des.distribution.type=tar -cp /content/elasticsearch-8.3.1/lib/*:/content/elasticsearch-8.3.1/lib/cli-launcher/* org.elasticsearch.launcher.CliToolLauncher
daemon       467     400 33 08:19 ?        00:00:52 /content/elasticsearch-8.3.1/jdk/bin/java -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -Djava.security.manager=allow -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j2.formatMsgNoLookups=true -Djava.locale.providers=SPI,COMPAT --add-opens=java.base/java.io=ALL-UNNAMED -XX:+UseG1GC -Djava.io.tmpdir=/tmp/elasticsearch-8870137204813447444 -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=data -XX:ErrorFile=logs/hs_err_pid%p.log -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m -Xms6493m -Xmx6493m -XX:MaxDirectMemorySize=3405774848 -XX:G1HeapRegionSize=4m -XX:InitiatingHeapOccupancyPercent=30 -XX:G1ReservePercent=15 -Des.distribution.type=tar --module-path /content/elasticsearch-8.3.1/lib -m org.elasticsearch.server/org.elasticsearch.bootstrap.Elasticsearch
daemon       490     467  0 08:19 ?        00:00:00 /content/elasticsearch-8.3.1/modules/x-pack-ml/platform/linux-x86_64/bin/controller
root         621      72  0 08:22 ?        00:00:00 /bin/bash -c ps -ef | grep elastic
root         623     621  0 08:22 ?        00:00:00 grep elastic
Enter host password for user 'elastic':
{
  "name" : "57c066b7e6a1",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "qnlGU_l8RsORus-vwAdpoQ",
  "version" : {
    "number" : "8.3.1",
    "build_type" : "tar",
    "build_hash" : "b9a6b2867996ba92ceac66cb5bafc6db25e7910e",
    "build_date" : "2022-06-29T18:39:55.731992798Z",
    "build_snapshot" : false,
    "lucene_version" : "9.2.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

create sample index.

In [10]:
!curl --cacert /content/elasticsearch-8.3.1/config/certs/http_ca.crt -u elastic -H 'Content-Type: application/json' -XPUT https://localhost:9200/playground -d '{"mappings": { "properties": { "name" : { "type": "keyword" } } } }'
Enter host password for user 'elastic':
{"acknowledged":true,"shards_acknowledged":true,"index":"playground"}

check a index.

In [11]:
!curl --cacert /content/elasticsearch-8.3.1/config/certs/http_ca.crt -u elastic -H 'Content-Type: application/json' -XGET https://localhost:9200/playground/_mapping?pretty=true
Enter host password for user 'elastic':
{
  "playground" : {
    "mappings" : {
      "properties" : {
        "name" : {
          "type" : "keyword"
        }
      }
    }
  }
}

create new data to es.

In [12]:
!curl --cacert /content/elasticsearch-8.3.1/config/certs/http_ca.crt -u elastic -H 'Content-Type: application/json' -XPOST https://localhost:9200/playground/_doc -d '{"name": "HOGE"}'
Enter host password for user 'elastic':
{"_index":"playground","_id":"oLPQ-4EBI_biyaanZoqU","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

M5Stamp C3でBLE Mesh

手持ちで4個(M5Stamp C3/C3U)があるから、これでBLE MESHをやってみたくなった。BLEでメッシュネットワークを構成するというのが前々から気になっていた。大量のノードがあるとき、WiFiだと限界があるし、Zigbeeのように何か通信用のモジュールを追加するより、ESPで使えるBLEメッシュを使う方が自然でもあったりするかなと。
4個だけでやっても迫力に欠けるけど、Publish一発で全台の制御をしたり、試してみるには良さげな気がした。

そんで、今回はVScode/Platform IOから使ってみる。PlatformIOからESP32(C3)の開発だと、ESP-IDFのビルドをサクサク出来て便利ではある。PIOの設定(platformio.ini)はこんな感じ。

[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = espidf

menuconfigもPIOのタスク”Run Menuconfig”から。

そしてビルドも同じくタスクのBuildから。esp-idfの環境を自分で作らなくてもPIO上がよろしくやってくれる。特にWindows環境下なら、VScode/PlatformIOの方がESP-IDFの環境を整備するのが、めちゃくちゃ簡単になると思う。
esp-idf直接の場合もVScodeを使うとは思うのだけど、要は自前で環境を用意する or PlatformIOに自動でやってもらう(ビルドも) という違いになる。

そして実際に動かしたのはこちら。NordicのnRF Meshというアプリ経由で動かした感じ。

https://www.hirotakaster.com/wp-content/uploads/2022/06/b6558f509b6360cafb20b7e246eb0377.mp4

コード類はesp-idfのと同じものを使える。こちら。BLE Meshのサンプルをベースに簡単な感じ。

M5StampC3でLチカ(esp-idf)

M5Stamp C3(RISC-V)をあまり使ってなかったのもあって、ちょっと使ってみようかなと。搭載されているSK6812をArduino IDE経由、いわゆるAdafruit Neopixelライブラリを使って操作するのもだけど、esp-idf経由で動かしたくなった。

https://www.hirotakaster.com/wp-content/uploads/2022/05/movie.mp4

LEDの赤〜緑〜青でループさせている。動かす過程で、esp-idf用の何かライブラリ一発で出来ないかしら?と調べていたら、esp-idf-lib という素敵なものを見つけた。色々なcomponentが入っていて、led_stripというのを使えば何かいけそうな…何となくノリで書いてみたらいけた。

コードはまるっと置いといた。githubから持ってきて、idf.py build〜flashすればOK。CMakeLists.txt の EXTRA_COMPONENT_DIRS を自分の環境に合わせて修正してあげる必要あり。

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "led_strip.h"

void setcolor(led_strip_t *led, rgb_t *color) {
    led_strip_set_pixel(led, 0, *color);
    led_strip_flush(led);
    led_strip_wait(led, 5 * 1000);
    vTaskDelay(10 / portTICK_PERIOD_MS);
}

void app_main(void) {

    // setup led strip
    led_strip_t ledstrip;

    ledstrip.is_rgbw = true;
    ledstrip.length = 1;
    ledstrip.gpio = GPIO_NUM_2;
    ledstrip.channel = RMT_CHANNEL_0;
    ledstrip.type = LED_STRIP_SK6812;

    led_strip_install();
    led_strip_init(&ledstrip);

    // set color r->g-b
    rgb_t color;
    color.r = color.g = color.b = 0;
    int vec = 1;
    int val = 0;
    int stg = 0;
    while (1)  {
        val += vec;
        if (val >= 255 || val <= 0) vec *= -1;
        switch (stg)  {
        case 0:
            color.r = val;
            if (val == 0) stg = 1;
            break;
        case 1:
            color.g = val;
            if (val == 0) stg = 2;
            break;
        case 2:
            color.b = val;
            if (val == 0) stg = 0;
            break;
        default:
            break;
        }
        setcolor(&ledstrip, &color);
    }
    esp_restart();
}

初期化でLED個数、GPIO(SK6812につながっているのは2)とかをしてあげて、あとは色(color)を作って、led_strip_set_pixel, led_strip_flush するいつもの感じ。

ちな、VScode(PlatformIO)、esp32-c3-devkitm-1でも問題なくM5Stamp C3で動いた。esp-idf-lib の components 以下をPIOのlibにごそっと持ってくればOK