雑食エンジニアの気まぐれレシピ

日ごろ身に着けた技術や見知った知識などの備忘録的なまとめ.主にRaspberry Piやマイコンを使った電子工作について綴っていく予定.機械学習についても書けるといいな.

フィラメントホルダーの作成

今回購入した3Dプリンタにはフィラメントのホルダがなかったので,練習がてら作成してみました.
完成品はこちらになります.

f:id:shikky_lab:20180505232522j:plain
フィラメントホルダの取り付け図

3Dプリンタで自作するフィラメントホルダというと
www.youtube.com
様のような形状のものを多く見る気がしますが,サイズ依存が強そうなのと,筐体に取り付けづらそうなところから,今回は軸を通す形式にしました.
さて,先ほどのホルダを部品ごとに分解すると次のようになります.

f:id:shikky_lab:20180505232526j:plain
フィラメントホルダのパーツ

以下,それぞれについて紹介します.なお,設計はすべてFusion360を使用しました.

軸受け

軸受けは外側と内側の二つに分かれています.

f:id:shikky_lab:20180505235051p:plainf:id:shikky_lab:20180505235100p:plain
軸受けの外側(左)と内側(右)
外側のジグザグや内側の段々形状は,接地面積少しでも減らして滑りをよくしようという工夫です.劇的ではありませんでしたが,多少は効果がありました.まぁ軸がいびつなおかげで,内側と軸の間がスムーズに回転して,現状内側と外側は一体に動いてしまっていたり・・・・・・.
ちなみに最初は www.thingiverse.com
様のモデルを使ってベアリングを生成しようとしていたのですが,どうやら私のプリンタではこの精度を出せないようです.設定を調整してそれっぽい動きをするものがいくつかできたのですが,あまりいい動きのものはできなかったので泣く泣く不採用としました.

これは円柱が3Dプリンタで生成できるかどうかのテストです.一応強度のことを考えて横向きで生成しています.気になる側面像を見てみると・・・・・・

f:id:shikky_lab:20180505232531j:plain
軸の側面
これはひどい...
今回の用途的には問題ありませんでしたが,実用性はかなり限られそうですね.調整でなんとかなるんでしょうか.

軸ホルダ

これが一番複雑な形ですね.

f:id:shikky_lab:20180505235054p:plain
軸ホルダ
flsun-prusaの筐体はM4ねじで様々なアタッチメントが取り付けられるのがいいですね.軸ホルダの下部にある穴が固定用となります.また,軸は上部をねじで締めることで固定できるようになっています.この部分を側面からみると以下のようになっています.
f:id:shikky_lab:20180505235057p:plain
軸ホルダ側面図
このように,ナットを埋め込める六角穴をあけています.造形的にこの穴は縦向きになるので3dプリンタ泣かせですね.多少は大きめに設計しましたが,結局ナットはうまくはまらなかったです.とはいえ少し削れば何とかなりそうな程度ではあります(小さめの鑢が手元になかったので試せていませんが).

今回で設計->生成の一通りの流れができたので,いよいよ本格的にロボット製作に入りたいところですが・・・・・・,
現状3Dプリンタのヒートベッドの温度が下がってしまう不具合が発生しているので,早いとここれを解決したいですね.
次の記事はこの問題の解決編になるかもしれません.

ABS樹脂の挑戦とクリアフィラメントの悲劇

今回から,3Dプリンタ~ABS樹脂編~です.
もともとABS樹脂の造形を目的としていたので,これ以降PLAに戻ることはないかと思います.木やTPUなんかはいずれ手を出すかもしれません.

ABS樹脂の特徴

さて,3DプリンタにおけるABS樹脂の(PLAと比較した)特徴ですが,以下のものがあります.

  • 融解温度が高い(220-240℃)
  • 熱による収縮率が高い
    PLAと比べて,温度が下がった際の収縮率が高めとなっています.そのため,最下層の温度が下がった際にベースからはがれることが多くなります.
  • フィラメントの密度が低い
    3Dプリンター用フィラメント PLA|製品情報|株式会社明成化学 様のフィラメント情報を見てもわかるように,PLAは1kgで340mに対し,ABSは1kgで410mになっています.だから何だという話ですが,フィラメントの消費が早いので見ていて少々びっくりします.あとは,エクストルーダの動きが大きくなるのでここのガタが影響しやすくなるとかはあるかもしれません.

ABS樹脂の購入

さて,今回の3DプリンタにはABS樹脂が付属していなかったので,Amazonで以下の透明色のフィラメントを購入しました.
https://www.amazon.co.jp/Pxmalion-3D%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF%E3%83%BC%E7%94%A8%E3%83%95%E3%82%A3%E3%83%A9%E3%83%A1%E3%83%B3%E3%83%88%E7%B4%A0%E6%9D%90-%E3%83%9E%E3%83%86%E3%83%AA%E3%82%A2%E3%83%ABABS%E6%A8%B9%E8%84%82%E6%9D%90%E6%96%99-1-75mm%E5%BE%84-%E3%81%A0%E3%81%84%E3%81%B6%E3%81%AE3D%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF%E3%83%BC%E3%81%A83D%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%E3%83%9A%E3%83%B3%E3%81%8C%E9%81%A9%E7%94%A8/dp/B0727R27KV/ref=sr_1_8
クリアパーツってかっこいいですよね!
それにABSは着色できるっぽいので,カラーリングのしやすさからもクリアフィラメントを選びました.
f:id:shikky_lab:20180505212705j:plain
こんなの買いました.

クリアフィラメントの悲劇

ところでみなさん.シロクマってご存知でしょうか.
https://upload.wikimedia.org/wikipedia/commons/4/45/Polar_Bear_ANWR_1.jpg
正式にはホッキョクグマですかね.その名の通り真っ白なクマです.
ですが,実はあの毛を一本抜きとってみてみると,透明らしいですね.
つまり何が言いたいかというと,こういうことです.
f:id:shikky_lab:20180505213057j:plain
し,白い......
とくに厚みがあるところが顕著ですね.積層型なので当然といえば当然の結果です.1kgも買ったのに...
まぁ,これはABSに慣れるために使いつぶすとしましょう.
余談になりますが,着色するのではなくパーツ毎に色を分ける形で素敵な作品を作っていた方がいました.
3Dプリンターでラッキービーストを作ってみた - ニコニコ動画
プラモデルが自作できる時代・・・・・・.今度はこういうこともやってみたいですね.

ABS用の3Dプリンタ設定

さて,今回ABS樹脂のフィラメントを使うにあたって行った設定は以下のようになります.

  • ホットエンド温度は220℃
    今回買ったフィラメントの適正温度が220-240℃だったため,最下限の220℃としました.215℃でもやってみたのですが,層間の結合が弱くなり割れることが多くなってしまいました.
  • 出力量は100%
    前回のPLAではフィラメントの出力量を90%に落としていましたが,今回それをやると層間で割れることが多くなったので100%にしました.
  • ヒートベッド温度は110℃
    一時ヒートベッドが100℃まで上がらなくなる事態があったのですが,接触を直したことと配線を短くしたことで直りました.100℃でやったこともありますが,50%程度の確率で1層目がはがれて悲惨なオブジェをいくつも生産してしまいました.
    なお現在,ステッピングモータが稼働中にヒートベッドの温度が下がるという現象が発生しています.これについてはもう少し試行錯誤してから記事にしようと思います.

ABSの所感ですが,全体的にPLAより難しいですね.ぶっちゃけかなりテキトーにやってもそれなりに生成できたPLAに比べて,ABSは事故が多い気がします.特に1層目ですね.1層目を制した者が3Dプリンタを制す,みたいなことを言っていた方がいた気がしますが,本当にその通りだと思います.で,1層目をしっかり吸着させるためにいろいろな工夫を凝らした挙句,はがすのが非常に大変みたいな事態も多く起こります.これはもう少しいろいろ研究する必要がありそうですね.
さて次回ですが,ここまで基本的に公開されている3Dモデルを印刷してきたので,自作パーツの印刷について書きたいと思います.よろしくお願いします.

3Dプリンタ所感~2週間目~

とりあえず3Dプリンタが届いて2週間ほど経ったので,感想及びここまでのことを備忘録的にまとめておきます.
※手探りなところが多いので,いろいろ不足や間違いがあると思います.

組み立て編

今回買ったの3Dプリンタはflsunのprusaベースのやつです. 詳細は前記事を参照のこと.
shikky-lab.hatenablog.com
組み立て後の全体像.
f:id:shikky_lab:20180421160207j:plain
所感まとめ

  • 組み立て時間は約12時間(手戻りが思ったより多かった)
  • マニュアルは付属のpdfと,youtubeの組み立て動画
    • 若干内容が異なる模様.動画の方が工程としては組みやすそう.
    • 部品の欠落はなし
      • ただし,LCD周りの寸法にずれ有? 結局斜めに取り付けました.
      • あと,小型のヒートシンクが余ってしまった.どこに使うんだろ.

ソフトウェアのセットアップ

付属のSDカードに一連の必要なソフトが入っていますが,若干古いので別途入れてきた方が良いかと思います.なお今回はオートキャリブレーションモード付のモデルを買ったのですが,これを使うためにはファームウェアを書き替える必要があります.

初生成

初生成は付属SDに入っていたモデルの一つ,ボルトとナットです.こんな感じになりました.

f:id:shikky_lab:20180421160203p:plainf:id:shikky_lab:20180421160200p:plain

見た目はそこそこですが,ナットは最後まで締まらないので精度としてはまだまだ.

キャリブレーション

パラメータ調整にいいモデルがないかと探していたところ,
ピラミッドのキャリブレーション・モデルでプリントの質を改善 – 3Dプリンター作る!
様の記事を見つけたので,ここで使われてるモデルを使ってキャリブレーションを実施しました. f:id:shikky_lab:20180421160217p:plain
左:初回,右:最終
(初回の出来を見たときは結構落胆しました.どうも造形中にノズルがオブジェクトにぶつかっていたので機械精度の方を疑ったのですが,いろいろ設定値を変えるとそのあたりの問題は解消しました.設定値の影響は想像以上に大きいみたいです.) いろいろな調整をしましたが,効果の大きかった順に並べると以下の感じですかね.先ほどのリンクの内容と被ってる部分も多いです.
※なお,フィラメントは付属のPLA,スライサはslic3rを使っています.

  • ホットエンドの温度を下げる
    一般的に融解温度の下限値が良いらしいです1.一層目を185℃,以降180℃としました.糸引きがだいぶ減りました.なおヒートベッドは60℃としています.
  • 吐出量を下げる
    90%にしました.感覚的に吐出量を下げた方が造形自体はきれいになっていきますが,層間の結合がもろくなっていくように思います.
  • プラットフォームシートを使用する
    反りの軽減に効果大です.3Mさんのプラットフォームシートを購入して使用してみました. https://www.amazon.co.jp/dp/B01M11XI4Y/ref=twister_B071WRF5YN?_encoding=UTF8&psc=1 付属のマスキングテープの質の問題かはわかりませんが,プラットフォームシート使用後の安定感はすごいですね.反りが完全になくなります.かなり強く吸着するのではがすのが大変ですが.一応消耗品のようですが,今のところ(2週間)張替の必要性は感じていません.
  • retract量を増やす
    糸引きの軽減に効果大です.フィラメントの質が悪い場合は値を大きくした方が良いらしいです.2mm(default)->4mmにしました.なお,retract速度も早めておくと,糸引きを結構軽減できました.
    なお,retract時などにz軸を持ち上げるlift-zの設定はあまり効果を感じませんでした.
  • external perimeterを先に造形する
    slic3rのprint settings->Layers and Perimeters->Advanced->external perimeter firstです.これは外壁の外側から先に造形する設定で,糸引きが発生した場合に,それを内側に閉じ込めることができる・・・気がします.

もちろん作りたいものによって調整値は変わるとは思いますが,おおむねの設定は出来てきた気がします.

その他設定

  • オートキャリブレーションを毎回実施する.
    初期状態でオートキャリブレーション機能が入っていなかったからなのか,マニュアルで指定しないとこの動作を実行することができません.これはGコードのG29で実行されますので, Slic3r->print settings->Custom G-code->Start G-code
    のG28の下にG29を記述しておくと,毎回動作させることができます.
  • 造形開始前/終了後の糸引きを抑制する.
    造形開始前にホットエンド温度を上げている最中に漏れ出したフィラメントが造形に巻き込まれることがあります.また造形終了後の糸引きはテーブルを汚してしまいます.これらは造形完了後にフィラメントを引き戻しておくことで対策できます.
    Slic3r->print settings->Custom G-code->End G-code
    のホットエンド温度を下げる前に,
    g1 e-5;retract filament
    を入れておくと,造形終了後に5mmフィラメントを引き下げます.
    なおこのまま造形を始めるとフィラメントがセットされていない状態 になってしまいますので,先のstart G-codeにフィラメントを引き出す設定をするか,スカートを多めに出すかしましょう.(私はスカートを3周分出しています.)

はじめのまとめとしてはこんなところでしょうか. 次回はABS樹脂の使用感などについて触れていきたいと思います.

3Dプリンタ購入にあたっての事前調査まとめ

前々から欲しいと思っていた3Dプリンタですが,いよいよ購入を決意したので選定を行いました.
ちなみに,最終的に選んだのはこちらになります.
Flsun 3D Printer I3 Dual Extruder Kits Auto leveling Large Size 300x300x420mm Printer 3D Heated Bed Two Rolls Filament -in 3D Printers from Computer & Office on Aliexpress.com | Alibaba Group
これのauto printerというモデルです.デカルト型でフィールドサイズは20x20x22cmです.
※実際の使用感などについては到着後に再度記事にしたいと思います.

以下に,選定の際に調査した内容をまとめます.

はじめに,3Dプリンタの選定にあたって

私が3Dプリンタに求めることとして,以下のことがあります.

  • ロボット用の部品作りたい

    引っ越ししてから使える工作機械がなくなってしまったので,割と何でも3Dプリンタに頼りたいと思ってます.

  • abs樹脂を使いたい

    ロボット用部品となると,定番のabs樹脂は使いたいですね.

  • 組み立て式でもよい

    完成品は魅力的ではありますが,構造を学ぶ意味も込めて,自分で組み立てる方式もよいかなと思っています.

このような基準の上で3Dプリンタを選定しました.

プリンタ構造の選定

3Dプリンタには大きく分けて,四角い箱型(デカルト型)と,円筒上のタワー型(デルタ型)の2種類があります.

いわゆる一般的な3dプリンタといえばこの形.xyz軸がそれぞれ単独で動くため,デカルト座標系からこの名前で呼ばれている様子.
マシンサイズに対して加工フィールドを大きく取れるという特徴がある.また組み立てや調整がやりやすいため初心者向きとの声が多い.
欠点はフィールド自体が稼働すること.このため,高さのある造形をすると形が崩れることがある.造形速度を下げればやれないことはないのかな.

最近よく見るようになったモデル.パラレルリンクによって構築されている.こういったロボットをデルタロボットというらしく,そこからデルタ型と呼ばれる.
フィールドが固定でノズル部分が動くため,高さのある造形も崩れにくい. 3軸の組み合わせによって動くため駆動が安定しやすいが,誤差も3軸分乗るため精度が高め難いらしい1.
とはいえ,調べているとデルタ型の方が精度が良いという記述もよく見る.実際どーなんだろ.
高さ方向のサイズは大きくなりがちだが,占有面積は小さいため,面積だけでみると省スペースになりやすい.
また構造がシンプルなため安価になりやすい傾向がある.
これだけ書くといいことづくめに見えるが,調整などが複雑なため初心者には難しいらしい.

最終的に,今回は1台目のプリンターなので,初心者向けとされているデカルト型を選択することにしました.

※補足:RepRapについて
上記3Dプリンタの画像はRepRap Wikiというところからお借りしました.RepRapオープンソース3Dプリンタを開発するプロジェクトです.素晴らしいですね.安価な3Dプリンタは大抵RepRapのものをベースに構築されています.
RepRapの中にも様々なモデルがあります.一覧はこちら.
RepRap Machines/ja - RepRapWiki
代表的なものは,デカルト型なら「prusa」,デルタ型なら「Rostock」,「kossel」ですかね.なお,Rostockとkosselの違いは
http://blog.livedoor.jp/comodos/archives/66777020.html
様がまとめてくださっていました. 該当部分を引用させていただくと,

Rostockは、上下の板が合板でZ方向の駆動にシャフトを使用。
Kosselは、上下がアルミフレームでZ方向の駆動にレールを使用 Rostockは組み立ててから誤差に気付いても修正が不可能なのに対して、Kosselならば修正可能な点が素人工作には向いてるのだそう。

とのことです.

フィラメントについての調査

FDM(熱溶解積層)方式の3Dプリンタの場合,PLAとABSが主流.これらの特徴については正直いろんな人がまとめているので割愛します.
なお,これらを含めて3Dプリンタで用いられる素材についての解説はリコーのやつがなかなか詳しいと思います.
3Dプリンターで使用できる材料と素材の特徴~強度や精度を確認|リコー ※FDM以外の方式を対象とするフィラメントも含まれているので注意.

なお,Amazonで取り扱っているフィラメントについては
3Dプリンターのフィラメントの種類について調べてまとめた | あること・ないこと日記 様が綺麗にまとめてくださっています.

3Dプリンタの中にはPLAしか使えないものもあるみたいですが,基本的にRepRap系のやつを選ぶとABS,PLAを含めていろいろなフィラメントに対応しているみたいです.私が最終的に選んだやつも,以下のフィラメントに対応しています.
PLA, ABS,WOOD,HIPS,PVA,Nylon,poly,TPU

WOODは木粉が練りこまれているフィラメントらしいです.生成物を見るに,結構木っぽくて面白そうなのでいつか試してみたいですね.

https://fabcross.jp/news/2015/05/dmln53000000ofzi-img/0501_wood_like_001.jpg
https://fabcross.jp/news/2015/05/20150501_wood_like.htmlより

ところで,polyってのはポリ乳酸のことらしいです.
Polylactic acid - Wikipedia
つまりPLAですね.先ほどの表記は重複してるんですかね.

その他機能を選定

ここまででおおよその形は決まったので,あとはオプション機能について調べていきます.ざっと挙げると以下ですかね.

  • オートレベリング
    ベースフィールドの水平だしを自動でやってくれる.正確にはフィールドの傾きを考慮して補正をかけてくれる機能.・・・・・・だと思う.
  • ヒーティングベッド
    土台のフィールドの温度を上げてくれる機能.ABSを扱う場合は必須.PLAでもあった方が綺麗に行くことが多いらしい.
  • マルチノズル
    その名の通り複数のノズルを持つ.サポート材に別素材を使ったり,色の混じった造形などができる.curaなどのフリーのスライシングソフトでも一応対応しているらしい.
  • レジューム
    途中で造形が停止された場合に,その部分から再開できる機能.緊急時もさることながら,途中でフィラメントの色を変えることなども可能.

ほかにもいろいろとあると思います.オートレベリングとヒーティングベッドについては 3Dプリンター 選び方ガイド ⋆ 3dプリンター ・ 3dプリンター 素材 ・ オンラインショップ
様の記事を読んで,必須なんじゃないかなと思ってます.

以上を踏まえて,デカルト型でオートレベリング,ヒーティングベッド付きのそこそこ安いやつを探していると,上記モデルに行きつきました.

続きは後日レビューします.それでは.

レオリモコンと自前ルータを共存させるために奮闘した話

今回の要旨

  • スイッチングハブ(有線)越しだとレオリモコンが動作しない
  • 有線ハブでも2重ルータになっている可能性がある?
  • アクセスポイントモードにすることでレオリモコンと共存可能

先日レオリモコンなる機器が設置されたレオパレス物件に引っ越しを行いました.このレオリモコンはスマートリモコンで有名なiRemoconの亜種で,家電によくある赤外線のリモコンを一つに集約することができる優れものです.レオリモコンはwi-fi経由でアクセスできるため,これを使うと家電をすべてネットワーク越しに操作できるようになります.素晴らしい.

レオパレス21、グラモと提携し住戸のIoT 化を加速|2016年一覧|ニュースリリース|株式会社レオパレス21

ただひとつ欠点?がありまして,なぜかこのレオリモコン,レオネットに直接繋がないと動かないんですよね.

レオネットというのはレオパレスの用意したネット回線で,安い代わりに糞遅いことで有名です.この糞遅い回線を少しでもマシに使うために,自前のルータ(スイッチングハブ)を使っていました.TP-Linkのやつで,2xの11ac回線とギガビットポートが4つついています.

しかし,いざレオリモコンを使おうとすると,ルータを有線のスイッチングハブとしてつないだ際に動かないんですよね.これが動かないとなると,壁のLANポートに直接レオリモコンをつなぐ必要があるので有線LANの分岐が不可能となります.それは困る.

f:id:shikky_lab:20171214232324p:plain
レオリモコン接続方式の理想と現実

なぜ動かないのか※推測になります.

そもそもなぜインターネットにつながないとレオリモコンが動かないのかという話ですが,レオリモコンにはスマホGPSと連動して,家から離れるor近づくをトリガーとした家電の操作ができます.厄介なことに.

これはつまり室内のレオリモコンに外部からアクセスできているということなので,ポートを開放しているということでしょう.しかし,レオネットはプライベートIPを割り当てる形式の回線なので,ポート開放を行うのはそれらを集約している管理ルータということになります.

つまり,レオネットの管理ルータはレオリモコン越しのポート開放を許可している

ということなんじゃないかと考えられます.

ハブ越しで動かないのはなぜ?

そのうえでハブ越しだと動かない理由を考えると,2重ルータになっている可能性が考えられます.ハブがルータ機能を果たしてしまうと,レオリモコンはこのハブのポートを開放しようとするわけですが,ここを開放したところで外部からは見えないので無意味です.でも有線のスイッチングハブってブリッジと同義なんじゃなかったっけ.

物の試しにルータ設定切り替え.そして成功

そうは思いつつも念のため試してみます.TP-Linkのルータをアクセスポイントモードに切り替えるのはなかなか厄介で,今までWAN側に刺していたケーブルをLAN側に刺したうえで,下記リンクのようにいろいろ設定をいじる必要があります.

www.tp-link.jp

やや面倒ではありますが,こうすることでレオリモコンを操作することができるようになりました.有線でも2重ルータになっていたとは.

何はともあれこれでやや快適な生活を送ることができます.それにしても,せっかくスマートリモコンがあるわけなので,ゆくゆくはGoogleHomeかAmazonEchoでも買って連携させていきたいところですね.それらに手を出せたらまた記事にしていきたいと思います.

Androidで画像のUDP受信とヘッドトラッキング

「GoogleCardboardとRaspberryPiを使って視界を共有できるロボットを作る」解説
第3回はAndroid側のプログラムについてです.一応これで最終回予定です.

なおAndroid開発はほぼ初めてのため,誤りを多分に含む可能性があります.ご了承ください.

 

Android側の主な仕事はUDP通信とヘッドトラッキングです.

はじめにUDP通信についてです.前回のC++Javaになっただけかと思いきや,存外面倒くさかった.

 

Androidで通信を行うためには非同期処理が必須のようです(Android3.0以降).

非同期処理の方法について調べると,概ね次の3つがあるそうで.

・AsyncTask

・AsyncTaskLoader

・Thread

(一応Service使ってもできないことはない気も...)

とはいえ,実際にはThreadがベースになっていて,AsynctaskもAsyncTaskLoaderも,Threadを使いやすくしただけみたいです.

 

※なお,非同期処理については

アプリ開発者を育てるプログラミングスクール Tech Institute(テックインスティチュート)

のVol12がかなり参考になりました.

 

今回のように何度も送受信が必要な処理の場合はAsyncTaskLoaderが適しているようなのですが,一つ問題が.

AsyncTaskもAsyncTaskLoaderも,原則Fragmentにしか使えません(もしくはFragmentを継承したActivity).

後ほど触れますが,GoogleCardboardのAPIを使うためにはメインアクティビティをGvrActivityから継承しなければなりません.

そしてGvrActivityはFragmentを継承していないため,AsyncTaskLoaderは使えないということになります.(ホントかなぁ...)

 

正直この部分については自信がありませんが,どうにも方法を見つけられなかったので今回はThreadを使って実装しました.

UDP通信部分については割愛しますが,受信したデータを変数bufに格納した場合,

Bitmap bmp= BitmapFactory.decodeByteArray(buf,0,buf.length);

でBitmapに変換できます.

あとはこれをUIスレッドにpostすれば受信した画像を表示できます.

......実際この実装でうまく動いているのですが,jpegで送ってbitmapで読み込みって普通に考えて奇妙ですね.decodeByteArrayがうまいことやってくれてるってことなんでしょうけど,どののフォーマットに対応しているのかリファレンス見てもよくわからなかったり.

 

ちなみにAndroidはbounjorに対応していないため,ホスト名のみでIPアドレスを解決することはできません(正確には方法はあるようですが,私にはできませんでした).ですが今回はテザリングで機器間をつなぐため,androidの持っているarpテーブルを使うことで,macアドレスからIPアドレスを解決することができます.取得方法は次のリンクを参考にしました.

java - How to get the IP address of a system using Android phone? - Stack Overflow

 

続いてヘッドトラッキングについて.

これはGoogleCardboardのAPIを使います.

Google VR SDK for Android  |  Google VR  |  Google Developers

本当は画像の表示にもこのAPIを使いたかったのですが,どうにも方法を見つけられなかったので画像は別途貼り付けています.

Google Cardboard Tutorial: How to render images using Cardboard App

(こことほぼ同じやり方です.)

 

というわけで,CardboardのAPIは本当にヘッドトラッキングのためだけに導入してます.

※補足

ゴーグル越しに画面を見る場合,ゴーグルの凸レンズで画面が拡大されてみえるため,樽型のひずみのようなものを予めつけておく必要があります.CardboadのAPIではこのひずみを付けたうえで描画してくれる機能があるのですが,これは3D空間を投影する部分とセットになっているようで,今回のようにもともと2次元の画像を描画する機能は見当たりませんでした.

タオバイザー様の公式アプリでもAndroidのカメラ画像をVR画面として出す双眼鏡モードでは樽型のひずみがついていないので,今のところ方法はないんじゃないかと思います.

※補足終わり

 

GvrActivityを継承したうえでGvrView.Rendererをimplementしたアクティビティを用意すると,オーバーライドが必要なメソッドの一つにonDrawFrameがあります.

このメソッドはフレームを描画する際に呼ばれるものですが,これの第一引数に頭の向きを表すHeadTransformが格納されます.

headtransformはgetterで様々な角度情報に変換して取得することができます.

HeadTransform  |  Google VR  |  Google Developers

オイラー角やクォータニオンなど大体のものが揃っているので,使いやすい形で取り出しましょう.今回の実装ではgetForwardVectorで取り出しました(クォータニオンには自信がないので......).

ForwardVectorは現在向いている方向をxyzの単位ベクトルとして返します.ベクトルをparamという配列に格納したとすると,座標系は次の通りです.

f:id:shikky_lab:20161114125322p:plain

あとはこれをarctanで角度に変えて使っています.

pan=arctan(x/z)

tilt=arctan(y/z)

という感じですね.なおarctanは分母が0のときにエラーを吐くので,arctan2を使うことをお勧めします.

RaspberryPiCamera+OpenCVでカメラ画像のUDP送信

「GoogleCardboardとRaspberryPiを使って視界を共有できるロボットを作る」
第二回はラズパイ側のUDP通信についてです.

C言語でのUDP通信については以下の内容が非常に参考になりました.
http://www.sbcr.jp/books/img/Linuxnet_03.pdf

今回のロボットにおけるラズパイのUDP通信部分は
・カメラ画像の送信
Androidの傾き情報の受信
をそれぞれ行います.なお,簡単のため送信と受信でポートを分けて実装しました.

画像の送信については
OpenCVでWebカメラのストリーミング - kivantium活動日記
様のコードをベースに使わせていただきました.

RasPiカメラモジュールの画像をOpenCVで取得する方法については過去記事参照
shikky-lab.hatenablog.com

#include <iostream>
#include <vector>

#include <sys/socket.h>
#include <netdb.h>//gethostbyname
#include <arpa/inet.h>//inet_ntop

#include <raspicam/raspicam_cv.h>

//ipアドレスの確認用
void print_host_info(struct hostent *host, struct sockaddr_in &send_addr) {
    unsigned int **addrptr;
    addrptr = (unsigned int **) host->h_addr_list;
    send_addr.sin_addr.s_addr = *(*addrptr); //送信先IPアドレス
    /*ipアドレスの確認*/
    char sender_str[256] = {0};
    inet_ntop(AF_INET, &send_addr.sin_addr, sender_str, sizeof (sender_str));
    std::cout << "sendto:" << sender_str << ",Port=" << ntohs(send_addr.sin_port) << std::endl;
}

int main(int argc, char *argv[]) {
    //送信ソケットの設定
    int send_sock;
    struct sockaddr_in send_addr;
    send_sock = socket(AF_INET, SOCK_DGRAM, 0);

    send_addr.sin_family = AF_INET;
    send_addr.sin_port = htons(8765); //送信用のポート番号.好きな値を設定.
    struct hostent *host;
    host = gethostbyname("192.168.43.1");//各自の環境に合わせて.bonjourに対応していれば"ホスト名.local"でも可
    if (host == NULL) {
        return 1;
    }
    print_host_info(host, send_addr);//IPアドレスの確認

    //カメラの設定
    raspicam::RaspiCam_Cv Camera;
    //set camera params
    Camera.set(CV_CAP_PROP_FORMAT, CV_8UC3);
    Camera.set(CV_CAP_PROP_FRAME_WIDTH, 640);//画像サイズの設定.
    Camera.set(CV_CAP_PROP_FRAME_HEIGHT, 720);//後々Android上で分割表示するため,スマホ画面サイズの半分に設定
    std::cout << "Opening Camera..." << std::endl;
    if (!Camera.open()) {
        std::cerr << "Error opening the camera" << std::endl;
        return -1;
    }

    static const int sendSize = 65000; //UDPの仕様により上限は65kB

    cv::Mat image;
    std::vector<unsigned char> ibuff;
    std::vector<int> param = std::vector<int>(2);//jpeg変換時のパラメータ設定
    param[0] = CV_IMWRITE_JPEG_QUALITY;
    param[1] = 80;

    while (cvWaitKey(10) == -1) {//sleepの代用.基本的にはsleepなしでも構わないが,joystickをつなぐと不安定になったりするっぽい?
        Camera.grab();//カメラ画像の取得
        Camera.retrieve(image);
        imencode(".jpg", image, ibuff, param);
        if (ibuff.size() < sendSize) {//上限オーバーの場合は送らない
            sendto(send_sock, ibuff.data(), ibuff.size(), 0, (struct sockaddr *) &send_addr, sizeof (send_addr));
        }
        ibuff.clear();
    }
}

コンパイルの際にはリンカに-lraspicam -lraspicam_cv -lopencv_highgui -lopencv_core -lopencv_imgcodecs のオプションが必要です.

UDPでは1パケットの最大サイズがヘッダ込みで65535byteとなるため送信できる画像は最大で約65kBとなります.ラズパイのカメラモジュールは無駄に?解像度がすごいので注意が必要です.今回は画像の幅と高さをスマホ画面サイズの半分にしたうえで,jpegのクオリティを変えることで調整しています.リアルタイム性を追求するならjpegクオリティを更に落として送信サイズを小さくしたほうがいいです(正確にはAndroid側の受信サイズを小さく).
※補足
jpeg画像は内部でハフマン符号化を行っているため画像サイズは毎回変わります(カラフルな画像ほど大きくなる).そのため効率的な通信のためには送受信のサイズを可変にする必要があります.送信サイズを可変にするのは簡単ですが,遅延に直接影響するのは受信サイズのようなのであまり意味ないようです(少なくとも実感はありませんでした).結局最終的な実装では各画像を10kBごとに分割して送信し,受信側で再構成するという処理を行いました.
※補足終わり

受信についてはマルチスレッドやマルチプロセスで並列処理する方法が理想的なのだと思いますが,いまいち扱いづらかったので
ノンブロッキングソケット:Geekなぺーじ
様の記事を参考にノンブロッキング処理によって実装しました.