t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

Fusion 360でラズパイ3B+ with PoEハットのケースを設計したので手順紹介

今回はネットで入手できるラズパイ3B+の3Dモデルを使用してラズパイとPoEハットがピッタリおさまるケースをモデリングしてみたので、備忘録を兼ねて手順をここで紹介することにした。

Fusion360で私がやりたかったケース製作については参考書を見てもちっともわからず海外のYouTubeとかで調べるしかなかったので、同じ悩みを抱えている方が少しでも操作のヒントが得られると良いなと思って記事にしてみた次第である。

私はCADの素人なのでプロから見ると非効率なやり方をしている部分も多々あると思うがご容赦いただきたい。

目次

モデルの入手

ググると簡単に見つかる。

ダウンロードにはAutodeskのアカウントが必要だが、Fusion 360を使用している時点でアカウントを登録しているはずなのでそれでログインしてダウンロードしてzipを展開しておく。

プロジェクト準備~モデル読み込み

まず適当な名前で新規プロジェクトを作成。

次にダウンロードしたモデルをプロジェクトにアップロードする。

私の環境だとアップロード待機に6分ほどかかった。途中フリーズしたようになる場合もあるが10分くらいは我慢。

アップロードが完了したら一旦ファイルメニューから空のデザインを保存しておく。無題のままだとラズパイを挿入したときにエラーがでる。

ラズパイを見つけて右クリックし、現在のデザインに挿入する。見つからない場合はプロジェクト名をクリックすると読み込まれる場合がある。

ラズパイを基準に基盤サイズの矩形をスケッチ

スケッチ作成を押して原点を展開し、その中のXYを選択する。

作成メニュー→投影/取り込み→プロジェクトを選択し、ラズパイ基盤の4辺を選択する。

ラズパイを非表示にして線分作成モードを選択し、スケッチパレットからコンストラクション(補助線)を選択。
線の切れ端から角度に気を付けて真っすぐ補助線を伸ばす。

同じ方法で左上と右下に補助線を引いて交点を作る。

矩形作成に切り替え、左上の交点から右下の交点までの四角形を作図する。(これも補助線でOK)

作成した四角形の辺をそれぞれ選択した後オフセットツールを選択し、スケッチパレットからコンストラクションをオフ(実線に戻る)にしたうえで外側に1ミリのオフセットを作成する。

これで最初のスケッチは完了。

(余談)ラズパイモデルを参照した押し出し高さの調整

ここでは普通のラズパイケースを想定してモデルの高さを利用したサイズ調整をやってみる。実際にはPoEハットが付くので今回この方法は採用しなかったが、一応紹介。

押し出しツールでスケッチ上面を選択し、方向を両側、サイド1の範囲のタイプをオブジェクトに指定する。
ラズパイを表示させて右手に回り込み少し拡大すると、USBの一番上の直線と曲線のつなぎ目を点として選択できる。

これでその点の高さまで押し出しが実行される。一方で下方向はうまく選択できる点が無かったので範囲のタイプを距離のままとし、そのまま矢印をを引っ張って押し出す。

確定させる前に、両サイドともオフセットを1ミリつける。サイド2は専用のオフセット項目はないが、距離欄は足し算が使えるので+1と書き足す。

これでラズパイを基準にした押し出しは完了。
今作った箱は実際にはケースの内部空間になるので割とラズパイサイズギリギリにしているが、今回私はPoEハットを付けるので別の高さ基準を採用した。したがってこの余談部分はキャンセルして次項から私が実際に採用した作業方法で説明する。

PoEハットを想定した押し出し高さの調整

まずは採寸。ラズパイ基盤裏を基準に一番厚みがでるファンのネジ頭までを計測した結果を切上げて、基盤裏から21ミリを確保することにした。

スケッチモードで原点のYZを選択し、ラズパイを表示させて右手に回り込む。
作成メニュー→投影/取り込み→プロジェクトでラズパイの基盤低面のエッジを選択。

作成メニューから点を選択し、スケッチパレットからコンストラクション(補助線)に切り替えて適当にUSBの上あたりに点を打つ。
スケッチ寸法モードに切り替えて取り込んだエッジからの距離を21ミリに設定し、スケッチを修了する。

後は前項の余談で説明したのと同じ要領で押し出しを行う。その際に今描いたスケッチを表示させておいて先ほど作成した点をターゲットに押し出し範囲を指定する。

今回も下方向は一旦矢印を直接引っ張って調整したが、後で設計ミスが発覚してやり方を変えたので後程紹介する。
※あえて今修正方法に言及しないのは、そのまま作業を進めてしまって後で気づいても手戻りを最小限にする修正方法が存在するためそちらを紹介したい趣旨。

これでラズパイがすっぽり埋めこまれた箱が完成。

※コネクタ差し込み口が若干はみ出しているが後で穴が開くので問題ない。

箱を上下に分けて外殻(シェル)化する

ラズパイを非表示にして修正メニューからシルエットを分割を選択。
ビュー方向に天面を選択した後にボディーを選択し、ソリッドボディ―を分割にしてOKをクリック。

これでボディーが上下に分割される。

上のボディを非表示にしてシェルツールで下ボディの天面(切断面)を選択。

外側・2ミリを選択して完成。元の箱がちょうど収まるような外殻が形成される。

上のボディを表示に戻し、外殻化した下のボディーを非表示にする。
底面が見えるようにアングルを変更した後、上ボディーの底面(切断面)を選択。

同じように外側・2ミリで外殻化させる。

ここまでの成果物がこちら。

設計ミスの修正

今回のケースは底面にインサートナットを埋め込んで六角スタンドオフでラズパイを固定する前提で設計している。

使用予定の六角スタンドオフのネジ部分が6ミリあるのだが、基盤天面からケース底面まで6ミリ確保できていないのでこのままだと底面をネジが突き破ることになる。

これを修正するために、まず選択モードに切り替えて左下のタイムラインからスケッチ2を開き、基盤天面から6.5ミリの位置に点を挿入する。

スケッチを修了すると元の状態に戻るので再度タイムラインから押し出しを開き、サイド2の範囲のタイプをオブジェクトに変更した後に先ほどスケッチした点を選択する。修正スケッチの段階で0.5ミリのオフセットを取ったのでここではオフセットは追加しない。

押し出しの修正を完了させると後続のタイムラインは自動で更新されるのでボディの分割やシェルの作成をやり直す必要はない。
計測してみるとちゃんと基盤天面からケース底まで6.5ミリ確保されている。

ケース底にネジ止め用の固定土台を作成

スケッチモードに切り替えて上ボディ―とラズパイはを非表示にし、ケース内底面をクリックする。

スケッチモードに切り替え後はラズパイを表示させ、投影/取り込み機能で4つのネジ穴の中心点を取り込む。誤ってネジ穴内周を選択しないよう注意。

固定土台のサイズはラズパイ基板のネジ頭用の余白に直径を合わせることにした。

ラズパイを非表示にして取り込んだ4つの中心点を基準に直径6mmの円を4つ配置する。

同様に4か所にそれぞれ直径3.5mm, 3mm, 2.5mmの円を追記する。

これはインサートナット挿入用に次のような段を作る為の準備である。

スケッチを終了して押し出しモードに切り替え、Ctrlキーを押しながら先ほどスケッチした円周の最外周の面を選択していく。
細かいので拡大しながら選択すると良い。Ctrlキーを離してしまうと複数選択が解除されるので注意。


範囲のタイプをオブジェクトにしてラズパイの基盤底面を選択するのだが、ボディが邪魔で狙えないので一旦非表示にしてラズパイを表示させて選択する。

ただしボディを非表示のまま押し出しを確定してしまうと次のようにボディが分割されてしまう。

これを避ける為には再度ボディを表示させた状態でOKをクリックすれば良い。

押し出しが終わるとスケッチは非表示になってしまうので再度表示させて外から2番目のリングを選択する。

先ほどと同様の方法で基盤裏をターゲットに押し出しを行うが、その際にオフセットを1mm指定する。

最後にもうひとつ内側のリングに対して同様の操作をオフセット3mm指定で実施する。

上から覗き込んだときにこんな風に段々になっていればOK。

上蓋のネジ止め穴を設計

実は上蓋のネジ止め穴は試作で失敗を経験済なので反省を活かして下図の右の方式で設計することにした。

下から覗き込むようなアングルに変更して下側のボディを非表示にし、スケッチモードで上蓋の内側天面にスケッチを描く。

余計なスケッチを非表示にしてから前回同様に基盤ネジ穴の中心点を取り込む。

ここでネジ頭とドライバーの直径を計測。

少し余裕を見て穴を6ミリに決定。壁厚は少なくとも1ミリ欲しいので8ミリの柱を形成することにした。

先ほど取り込んだネジ穴中心点を基準に8ミリ円をスケッチしてスケッチ終了。

次に高さを計算。
ラズパイ基盤底面からPoEハット基盤天面までが約12ミリ。(計測位置で微妙に異なるので12ミリとして丸めた)

ここに5ミリ高さのスタンドオフスペーサーを付けるので基盤底面を基準に17ミリの位置に上蓋の柱の先端が来るようにする。

基準点を作るため原点のYZ面にスケッチを作成。

基盤底面を取り込んでから適当な位置に点をスケッチしたら、基盤底面と点の距離を17ミリに指定してスケッチを終了する。

もう一度上蓋を内側から覗き込むアングルに変更して押し出しツールで4つの円をCtrlを押しながら選択。
範囲のタイプをオブジェクトにして先ほどスケッチした点を指定してOK。

次に天面のスケッチ。点の取りこみはいつも通りなので割愛し、円はネジ穴用の3ミリ・ネジ頭の窪み用の6ミリをそれぞれスケッチする。

まず外周の押し出し。4か所選択して範囲のタイプをオブジェクトに指定し、裏面に回り込んで柱の先端を選択。オフセットに-1ミリを指定してOK。

天面に残った円柱部分も同じ方法で押し出すがここではオフセットを指定せずに単純穴にしてしまう。

完成したのがこちら。

プラグ穴の加工

下のボディとラズパイを表示させた状態でケース内側に回り込みスケッチを作成。

スケッチモードに入ったらまずHDMI端子の形状を取り込む。
ボディは邪魔になるので一旦非表示にし、HDMI端子の正面に回り込んで取り込み作業を行う。
真横から見てしまうと奥行が分からず奥の線を取り込んでしまうため角度を変えながら作業する。

ラズパイを非表示にしてボディーを再度表示させ、内側に回り込むと綺麗に取りこみできている。

ただ実は拡大してみると繋がっていない線がある。

また、点が見えるのは拡大して繋がっているように見えても実際には重なっているだけで繋がっているわけではないのでこれを繋げる作業を行う。

まずはオブジェクトとのリンクを解除ておく。

具体的には選択モードに切り替えて後ろから平面で見えるようにアングルを調整し、マウスドラッグで取り込んだ線をすべて選択した後に右クリックメニューからリンクを解除する。

次に拘束メニューの固定を使用して曲線パーツをすべて固定する。

窪み部分の直線のつなぎ目は切れているので下側の点を固定する。

選択モードで固定していない直線を掴んでバラバラにしておく。

更にそれぞれの線の端を掴んで角度も元の角度から変更する。また一番下の辺は2つの線に分かれてしまったので片方は削除して良い。

この後、点と点を一致拘束させることで元の形に戻していく際に角度・長さが同じままだと一致拘束させていない方が重なってしまい選択しづらくなるため、角度の変更はそれを避ける準備である。

注意点として、事前準備で角度を変更した際に水平にしてしまうと自動で水平拘束が入ってしまう場合があるので右クリックして削除しておく。

選択モードで一旦エスケープキーを押して何も選択していない状態にしてから拘束メニューの一致を使って点と点を繋いでいく。最初に基準となるパーツを固定したのはこの為。

これで点がない図形が完成。

選択モードで線全体を選択した後、右クリックメニューから標準/コンストラクションで補助線に切り替えておく。

※実際に押し出し加工に使うのはこの線にオフセットしたラインなので実線のままだとリング状の押し出しになってしまう為。

次に0.5ミリのオフセットを指定する。正しく線が接続されていればチェーン選択で順番にクリックして一周分選択できる。

一部未拘束扱いになってしまったが気にせずスケッチを終了する。

あとは通常の押し出しで穴を空けるだけ。

他の穴も同じように処理していくが、Micro USB端子はどこを外周として取り込むのか難しい形状をしているので最大外周を取り込みつつ、曲線部は小さくなるので直線を固定してから一致拘束で曲線サイズが直線に合うように作りこんでいく。

オフセット含めてできた図形がこちら。

なお、円弧と直線の接続が窪みになっているのに気づくかもしれないが、気にしなくて良い。

拡大作業していると忘れがちだけど、これは0.1ミリ以下の話なのだ。どのみちそんな精度でプリントは出来ない。

イヤホンジャックは単に円形なので割愛し、すべて押し出して完了。

USB・LANポートもスケッチは下側ボディーの内側にする。はみ出してスケッチしても問題ない。押し出すときに上下ボディを合わせて対象にできるのでスケッチは片側でOK。これはただの四角形にするので適当に4辺ずつ取り込む。

角度に気を付けながら辺を延長し、補助線で交点を作っていく。

次に補助線で矩形を書く。始点・終点ともに交点にピッタリ重ねる必要がある。なお画像で△マークが出ているのは線の中点スナップなので今回合わせたい位置とは違う。拡大等も駆使しつつ交点にぴったり位置合わせをする。

次に実線でオフセットをスケッチする。色々と線が重なってオフセット元の図形選択を選択しづらいが、最初に交点を作っていない角は矩形の線しか無いので選択しやすい。

スケッチを終了し、選択モードでCtrlを押しながら全て選んでいく。ボディの切れ目で面が分かれている。

次に上側ボディも表示にして押し出し。デフォルトでは表示されていれば一緒に切り取ってくれるが、切り取るオブジェクトという項目を展開すると指定もできるようになる。片側しか切り取り対象にならない場合は確認してみると良い。

SDカード穴・吸気ファン穴の作成

3Dカード取り出し口は背面にスケッチして押し出しで作成する。
今回は幅18mmで作成することにした。これは私のひとさし指のサイズが17mmだったため。
作成方法はこれまで紹介した手法とそんなに変わらないので詳細な説明は割愛。

押し出し後に修正メニューからフィレットを追加している。

ファンの位置はノギスで測って穴あけ。

底面排気口の作成

底面の内側で穴同士を横に繋ぐ補助線を作成。

このあとスケッチパレットは実線に戻しておく。
作成メニュー→スロット→スロット全体を選択し、上の補助線の中点(△が出る位置)を始点としてクリック、終点をそのまま真っすぐおろして下の補助線の中点をクリックしたあとサイズ3ミリを入力して確定。

作成したスロットを選択メニューから範囲選択で選択し、矩形パターンツールで対称指定する。
横向きの矢印を引っ張って数量を指定、最後は距離・数量を数値で微調整して完了。数量は奇数を指定しないと対象にならない。

私は20ミリの7個を指定した。

いつも通り押し出しを実行して完了。

ゴム脚シールの位置合わせ用の窪みを作成

下ボディーの底面からスケッチで表のミリネジ土台からスケッチを取り込んだあと、正面側の2つはリンクを解除して正面端からの寸法を個別指定する。

押し出しツールで0.4ミリだけ凹ませる。

仕上げ

外側の四隅に2mmフィレットを追加。

印刷に備えて上側ボディをひっくり返す。
具体的には選択ツールで上側ボディを選択してから移動ツールで180度回転と横スライド(くっ付いてなければ距離は適当でOK)を適用する。

これで完成。

STLファイルのエクスボート時にラズパイごとSTL化するんじゃないかと心配してたけどそんなことは無かった。非表示でやったからかもしれない。

免責時効

これを実際に印刷してうまくいくのかどうかはまだ分からない。
個人的にかなり怪しいと思っているのがHDMI上の0.2ミリのところ。

これは印刷できたとしても強度が出ないので取っ払ったほうが良いかもしれない。
まぁダメだったらペンチで切るか。

オーディオジャックの上も0.4ミリしかないので割と怪しいのだが。

印刷してみてうまくいかなかったらタイムライン上でどこがまずいのか試行錯誤しながら修正しく必要がある。

冒頭で書いたように記事公開の目的は操作のヒントを見つけていただくことなのでこの通りやってうまくいかないと言われても責任は負いかねる旨あしからずご了承いただきたい。

以上

Arduino Dueでオシロスコープに画像を表示してみた。

これは前回の続き記事となる。
thom.hateblo.jp

前回は円を描いただけなんだけど今回はモノクロビットマップデータを表示してみた。

といっても適当なモノクロ画像がなかったのでとりいそぎHello Worldと書いた画像を使用。

出力した結果はこちら。

波形はこんな感じ。


これ実は1マス5ミリ秒くらいに調整するとすごく分かりやすい波形になっていて面白い。


ソースコードはこんな感じで配列化した画像データが直書きされている。

※はてな記法でうまくソースコードを認識しなかったので画像で貼り付けました。もし真似したい方がいれば頑張って写経してください。

容量節約のため黒点が存在するところだけX,Yデータを格納している。

今回は150x150サイズの画像を使ったのだがDAC出力には0~4095を指定できるのでそのまま入力するとかなり表示が小さくなってしまう。そこでソースコード上で座標を10倍している。それでもX・Yの最大は1500なので20倍で良かったかもしれない。オシロ側で表示位置とサイズを調整する際にけっこう拡大する羽目になってしまった。


画像をデータ化する際は次のPythonプログラムを利用した。

from PIL import Image

img = Image.open('sample.png')
width, height = img.size

x_list = []
y_list = []

for y in range(0, height):
    for x in range(0, width):
        if sum(img.getpixel((x, y))) == 0:
            x_list.append(x)
            y_list.append(y)

print(x_list)
print("--------")
print(y_list)

PILライブラリで画像の縦・横サイズ分ループでピクセルごとのカラーを取得し、R・G・B値の合計が0なら黒と見做してX・Y座標をそれぞれのリストに格納していくという処理である。

1点注意が必要なのはピクセルのY座標とオシロのY座標が反転して表示されるということ。

プログラム上で工夫しても良いけど、画像の上下反転の方が楽なので今回はこんな感じで反転させてからデータ化した。

当初やりたかった任意画像の表示ができたので今回はここまで。

Arduino DueのDAC機能を使ってでオシロスコープに円を描いてみた。

今回はオシロスコープのXYモードを利用してArduino Dueで円を描いてみた。

オシロスコープのXYモードでは2つのアナログ電圧を座標としてプロットすることができる。

CH1をX軸、CH2をY軸として、それぞれの電圧の位置に輝点が現れる。

電圧を変化させると新しい場所に輝点が現れるが、古い輝点は一定時間画面に残り続けるため、これを使うと任意の絵を表示させることができる。

どれくらいの時間輝点が残るかは波形を同時に表示させると分かりやすい。
円を描くための電圧としてCH1にサイン波・CH2にコサイン派を入力しているのだが、下図では谷・山のパターンが7回ほど繰り返されているのが分かる。


波形の横軸1マスが50ミリ秒という設定にしているので50ミリ×12マスで600ミリ秒。約0.6秒間輝点が残る計算である。
つまり、見ている瞬間は7周分の円が重なって描かれていることになる。

これを1マス10ミリ秒(全体で0.12秒)に変更しても1周分は確保できるのでキレイに円が描ける。

1マス5ミリ秒(全体で0.06秒)では1周分の波形が確保できないので欠けた円がくるくる回転するような表示になる。

原理的にはこんな感じ。

あとはアナログ電圧をどうやって確保するかが問題である。
Arduino Uno R3やRaspberry Piでアナログ電圧を出すのは外部にデジタル・アナログコンバーター(DAC)を準備する必要があるので割と面倒くさい。

Arduino Uno R3のアナログピンは入力専用で、出力の調整はPWMしか用意されていない。PWMでは固定電圧をON・OFFする間隔を調整することで疑似的に出力を落としてモーター速度やLED照度の調整に使っているのだが、これをオシロで見ると出力どおりON・OFFの矩形波になってしまう。

最近発売されたArduino Uno R4には本当のアナログ出力ができるDACが付いているのだが、DACに使えるピンが1つしかないため今回やりたりX・Yの二軸出力はもう一つDACピンが欲しい。

そこで今回はArduino Dueという製品を調達してきた。
これはArduinoシリーズの中でもDACが2つ付いている珍しいタイプで今回の目的にちょうど良い。

他にもESP-WROOM-32にもDAC出力ピンが2つあるらしいのでそちらを持っている方はそちらでも良いと思う。

コードはこんな感じ。

double i=0.0;
void setup() {
  analogWriteResolution(12);
}
void loop() {
  analogWrite(DAC0, 2048 + 2048 * sin(i));
  analogWrite(DAC1, 2048 + 2048 * cos(i));
  i+=0.01;
}

オシロの初期の設定だとマス目の中心が0Vなので右上に小さく表示されてしまう。

オシロのつまみでCH1とCH2をそれぞれ位置とサイズを調整することで画面いっぱいに表示できる。

今回はシンプルなコードでただの円を表示させただけだが、今後はもう少し何か絵っぽいものを表示させてみたい。
また今回は手軽にやりたくてデジタルオシロを使ったけど、アナログオシロの方が味があって良さそうだ。
KENWOODのCS4135というオナログオシロを持っているので暇があれば試してみたいと思う。

参考サイト:
rokkophysicsclub.github.io

以上

Raspberry Pi Picoでマイクラ作業用にマウス・キーボードマクロ作成

久々にMinecraftで遊んでいるんだけど、土台敷き作業が結構面倒くさい。

操作としてはShift+S+D+マウス右を押しっぱなしにするだけなんだけど、気を抜くとシフトが外れて落下するため自動化することにした。

ソフトウェアマクロという手もあるけど途中で手動ホイール操作を加えたり、頻繁にオフにしてアイテムをインベントリにセットしなおしたりするので物理スイッチでオフ・オンできるハードウェアマクロの方が使い勝手が良い。

今回選択したのは買ったまま1年以上眠っていたラズパイPico。
最初はいつもどおりArduino Leonardoで作るつもりだったんだけど行方不明だったので慣れないラズパイPicoを試してみることにした。

構成はシンプルにGP15ピンとGNDをスイッチを繋げただけ。

スイッチは以前作った実験用のトグルスイッチ。シンプルで使いやすい。
thom.hateblo.jp

調べてみるとラズパイPicoでキーボードマウス入力をシミュレートするには標準のMicro PythonではなくてAdafruitから出てるCircuit Pythonを使うらしい。

コードはこんな感じ。

import usb_hid
from adafruit_hid.mouse import Mouse
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import digitalio
from board import *
import time

mouse = Mouse(usb_hid.devices)
kbd = Keyboard(usb_hid.devices)

LED = digitalio.DigitalInOut(GP25)
LED.direction = digitalio.Direction.OUTPUT
SW = digitalio.DigitalInOut(GP15)
SW.direction = digitalio.Direction.INPUT
SW.pull = digitalio.Pull.UP
mode = 2

while True:
     if SW.value == False:
         LED.value = True
         if mode == 1:
             mouse.click(Mouse.LEFT_BUTTON)
         else:
             kbd.press(Keycode.SHIFT)
             kbd.press(Keycode.S)
             kbd.press(Keycode.D)
             time.sleep(3)
             mouse.press(Mouse.RIGHT_BUTTON)
     else:
         if mode == 1:
             pass
         else:
             LED.value = False
             mouse.release(Mouse.RIGHT_BUTTON)
             kbd.release(Keycode.S)
             kbd.release(Keycode.D)
             time.sleep(1)
             kbd.release(Keycode.SHIFT)
     time.sleep(1)

ちなみにmodeが1のときはゾンビピグリンを殴るだけのマクロなので今回は関係ない。

マクロ切替はハードコーディングによる変数値の変更で行うという雑な仕様だけど目的は果たしている。

参考にしたのはこちらの記事。
hf-labo.net

1点だけ私の環境が記事と違ったのはThonny(Pythonエディタ)のポート選択でPicoが表示されず、CircuitPython CDC controlを選択したこと。

これはひょっとするとMicro PythonのUF2ファイルを導入せずに最初からCircuit Pythonを入れたためかもしれない。

おわりに

PCやマイクラに詳しい方からするとこんな単純なことのためにわざわざマイコンを持ち出して自前でHIDデバイスを作るのは大げさだと思うかもしれない。スニーク固定はゲーム機能としてあるし、そもそもロジクールのG304マウスを使っているのでG-HUBでマクロ化するという手もあった。

そんなことしなくてももっと楽にできるのにと思ってモヤモヤした方もいると思うけど、私にとっては今回の一連の作業を含めて道楽の一部として楽しんでいるので大目に見ていただきたい。

それに今回ようやく眠っていたラズパイPicoの活用先が見つかって良かった。
とりあえず買ってみたもののしばらく使っていなかった理由としてはなんだかんだ慣れ親しんだArduinoを手に取ってしまうということが大きい。

Pythonの方がCよりも文法的にスッキリしてて初心者に優しいからPicoの方が初心者向けだという意見も目にするんだけど、発売日の差でネットの情報量では圧倒的にArduinoに軍配が上がるためまだまだArduinoの方が調べやすい。

ただ実際に使ってみたところ欲しい情報や参考にするコードはすぐに入手できたし、そろそろPicoに関してもネットの情報が成熟してきたなと感じる。
次から何かつくるときはPicoを第一候補として選定しても良いかもしれない。

なによりArduinoより安いのに高性能。唯一ネックだった情報量の問題が解消されつつあるなら使わない手はない。

以上

自宅DHCP兼DNSサーバー障害対応

今日20時頃、自宅DHCPサーバーが応答しなくなった。

半年ほど前にもトラブルで再構築した気がするが、これは設定ミスによるパスワードトラブルだったので実際に本番機の再構築一歩手前で気づいて修正した。
thom.hateblo.jp

ということで前回実際に故障したのは9か月前となる。
thom.hateblo.jp

前回もSDカード故障だったが、今回もSDカード故障。
もともと16GB 10枚入りで5,000円という激安のSDカードを使っていたので安かろう悪かろうということかもしれない。
実際にこのSDカードはラズパイデスクトップを入れるとプチフリーズが大きくてサーバー専用で使っていたのだが、本当はサーバーこそ信頼性の高いものを使う必要がある。

急遽トラブルを解消しないと在宅勤務も厳しいのでとりいそぎそのあたりに転がっていたSDカードをピックアップしてラズパイOS Lite をクリーンインストールした。

実はこんなこともあろうかと自宅のGitbucketサーバーにDNS/DHCPサーバーのコンフィグを退避してあり、設定のデプロイもスクリプト化してある。
thom.hateblo.jp

これのおかげで復旧は余裕!と思っていたんだけど、肝心のGitbucketのIPアドレスがDHCPによる固定IP払い出し。。DHCPリース期間は残っているはずだけどそのIPアドレス情報が故障したDHCPサーバーもしくはGitbucketサーバーのリポジトリに保管されていて思い出せない。。しかも最近アクセスしていなかったせいでPCからのDNSキャッシュも切れており、故障したDHCPサーバーはDNSサーバーも兼ねているという八方ふさがりの状況になってしまった。

DHCP側に依存するデメリットは前回の故障時に痛感しているはずなんだけどやはり管理が楽なので復旧に緊急を要するサーバー以外はこれに頼ってしまう。今回の失敗は、構築当時は緊急性がなかったはずのGitbucketサーバーが、DHCP/DNSコンフィグが保管されることによって復旧緊急性が跳ね上がってしまったのにそのときに固定IP設定をしなかったことだと思う。

結局どうやったかというと、Redmine上のCMDBでGitbucketサーバーのMACアドレスを調べて新規構築したDHCPサーバー上にGitbucketサーバーの固定払い出し設定を適当なIPアドレスで記述し、GitbucketサーバーにキーボードをつないでCtrl+Alt+Delでリブートした後GitbucketからDNS_DHCP設定をクローンした。あとはDHCP/DNSサーバーのデプロイスクリプトを走らせてリブートかければ復旧完了。
あとGitbucketは適当なIPを設定してしまったので再度リブート※して正しいIPを再取得。

※ミニHDMIディスプレイとキーボードを繋いで直接端末にログインを試みたのだがキーマップがENになっているためかパスワードが通らずリブートくらいしかDHCPでIP再取得の手段が無かった。

その後Gitbucketサーバーは固定IP化し、これにて一見落着。

ネットワーク障害はかなり焦るけども障害のたびに色々と反省点も見えてくるし、むしろ定期的に何かが壊れた方が構成を思い出すきっかけになるので良い気がしてきた。この障害がたとえば5年後とかだったらもはや何も覚えてなくて更に影響が大規模になっていた可能性がある。

最近安定稼働していたので放置気味だった自宅ネットワークだが、これを機に再度障害への備えについて見直そうと思った。

以上。

10/1追記

スマホからローカルのファイルサーバー(Samba)への接続に妙に時間がかかるようになり、ホームゲートウェイからローカルDNSへの転送を忘れてたことに気づいたので追記。

プロバイダとIPv6通信を契約にしてるとホームゲートウェイのDNSにはIPv6しか指定できないのでローカルDNSのglobal IPv6アドレスを指定しておく。

これでホームゲートウェイがlocal.thom.jp宛ての名前解決を受け取るとローカルDNSに転送してくれる。

E24系列の金属皮膜抵抗セットを1万円で購入

最近とある電子工作の実験で微妙抵抗値が必要になった。

最初はよく分かってなかったのでAmazonで適当な抵抗セットを買ってこれまでそれを使っていたんだけど、どうやら私の買ったのはE3系列というものらしく、ネットのサンプルで330Ωや510Ω等の抵抗値が使用されていると複数を組み合わせて近似値を作らないといけないので面倒だった。

E3系列というのはベースとなる3種類の数字(1・2.2・4.7)を使って、10Ω、22Ω、47Ω、100Ω、220Ω、470Ω、1kΩ、2.2kΩ、4.7kΩ…という風に組み合わされた抵抗のセットである。

対してE24系列はベースの数字が24種類あるというもの。
最大でE192系列まであるようだが、普通はE24まで揃えれば趣味の範囲では十分すぎる。(というかオーバーキルかも。。)
detail-infomation.com


今回買ったのは以下の製品。
eleshop.jp

金属皮膜抵抗はカーボン抵抗と比べて温度変化に強く誤差も少ないが、その分値段が張る。
カーボン抵抗の誤差を気にするほどのレベルの工作をしているわけではないが、想定通りに動かないときに抵抗を疑ってハマるのは避けたいので一応金属皮膜を選択した。

100本セットだと約4万円なので実はちょっと迷った。値段4倍で量が10倍なのでだいぶディスカウントが効いている。
ただどのみちよく使う抵抗値は決まってくるだろうし個人で使う量じゃないのでやめておいた。

117種類10本ずつのセットでこれくらいの箱で届く。

これの10倍とかどこに仕舞うつもりだったんだ。。10本セットで良かった。


さて、ホーザンのパーツキャビネットに収納したいのだがここで一つ問題が。

立てると飛び出し、寝かすとかさばる。

そこで思い出したのが昔買ったクリップシーラー。

袋のサイズを変えたりするのに便利で昔よく使っていたんだけど最近は出番も無く眠っていた。

一旦袋を開封してギリギリのサイズでシールした後、余りをカットしてから再度テープで閉じる。

ちょっとシールが途中で切れてて汚いけど抵抗値は読める。

作業を続けること約1.5時間。3つの引き出しに綺麗に収まった。

とりあえずこれで当面抵抗に困ることはなくなった。
あとは使って減った分を買い足す感じで運用しようと思う。

以上

PID制御でDCモーターのスピードコントロール

今回はPID制御の学習のためDCモーターの回転スピードが一定になるように制御をしてみた。

制御とは

言葉のイメージどおり、対象物を思い通りに操ることを制御と呼ぶ。

DCモーターの場合は電圧に応じて回転スピードが変化するので、回転スピードを計測して遅すぎる場合は電圧を上げ、速すぎる場合は電圧を下げれば良さそうだ。ところが実際にやってみるとこのアイデアはうまくいかない。

実は電圧を上げてもその電圧に応じた回転スピードに達するまでにはタイムラグがあるため、センサーで判定するとまだ出力不足と判断して電圧を上げすぎてしまう。逆に電圧を下げてもすぐに回転スピードは落ちないので、今度は電圧を下げすぎてしまう。これを繰り返すのでモーターの速度はフラフラと上がったり下がったりを繰り返すことになる。

実際に試してみたのがこちら。

想定どおり、電圧変動から少し遅れて回転速度が変わっている。

また、モーターは何かしらの仕事をさせる目的で使う為、普通は負荷がかかる。負荷が変動しても一定スピードで動かそうと思うと工夫が必要になる。

PID制御とは

PID制御とは、Proportional(比例)-Integral(積分)-Differential(微分)の頭文字をとった制御方法である。
数学嫌いにとってはうげっとなるワードかもしれないが概念を理解するだけならシンプルなので安心して欲しい。

P制御(比例)

P制御は基準値からの誤差に比例して操作量を増減させる手法である。
要するに、HIGH(下げる) or LOW(上げる)という単純処理じゃなくて、ちゃんと程度問題として扱いましょうということだ。

ちょっと頑張れば誰でも思いつきそうなアイデアなのでここはそれほど難しい話ではない。
ただP制御は基準値に近づけば近づくほど操作量を減らしていくので、実はいつまでたっても基準に到達しない可能性があるという弱点があるらしい。
※曖昧な書き方なのは、今回私が行った実験では特にその弱点が露呈することが無かったためである。

I制御(積分)

I制御は基準値からの誤差の累積に比例して操作量を増減させる手法である。
累積なんてしたらすぐに基準値を飛び越えてしまいそうだけど、うまく調整しないと実際にその通りになる。
ただ飛び越えた分はマイナスの累積となるため段々波が小さくなるように安定してくる。

I制御は安定するまでに時間がかかるというデメリットがあるが、どんなに小さな誤差でもずっと続くと累積によって大きな力を生むため、P制御と組み合わせるとちょうどお互いの弱点を補完しあうことができる。
PIを組み合わせる場合は基本的にはP制御をメインに据えて、誤差のところだけI制御による累積の力をほんの少し借りるというイメージ。

D制御(微分)

D制御は基準値からの誤差の急激な変化を打ち消すような力を働かせる手法である。
そもそも変化が無ければ働かないのでこれ単体では制御手法として成立せず、PIと組み合わせる安定剤に近い役割。

例えば急激に負荷が増えた時などは通常のP制御ではそのまま失速してしまうことがある。どかっと負荷が増えた際には、瞬間的に電圧を特盛にして早急にリカバリーしたい。そこで、変化の傾きを測定して操作量を柔軟に決めるのがこのD制御だ。


PID制御

それぞれの制御をどれくらい取り入れるかは、それぞれゲインと呼ばれる定数を掛け合わせることで行う。

ゲインの調整は以下のサイトに詳しく書いかれているのでとご参考までに。
controlabo.com

私は今回適当なサンプルをベースに試行錯誤するという方法をとったけど、本当はちゃんとやらないとハードを壊しかねないので貴重な機材や危険な機材でやるときはきちんと理解して適切な値の設定が必要なようだ。

今回は模型用モーターを適切な保護機構がついたモータードライバーで制御してるのでまぁ多少間違えてもそんな大層なことにならない。
またPWMで制御してるのでは最小0、最大255までしか入らない。10,000とかPWMで指定してしまったところで255と解釈されるのでせいぜいモーターが100%出力になって煩い程度である。

実験のセットアップ

セットアップはこんな感じ。

準備物

部品取り用ギア―ドモーター

速度検出センサーのスリット付き円盤に合わせるシャフトアダプタが無かったので購入。

バラしてアダプタ部分だけ取り外し。ネジザウルスとかラジオペンチでねじりながら引っ張る感じだった。

最初は3Dプリンターでの印刷を試したんだけど、今はある程度大きなものを短時間で印刷する為にノズル径を0.8mmに変更してて、スピード設定なんかも最適化してるので小さいパーツの精密な印刷は出来なかった。毎回米粒みたいになる。小物の精密印刷用に調整しなおすのも面倒なので。。

その他

  • Arduino入りブレッドボード
  • モーター駆動用の直流安定化電源

コード

#define AIN1 3
#define AIN2 5
#define ROTARY_ENCODER 2
#define SPAN 250 // milliseconds
#define REFERENCE 20 // rolls per SPAN
const double Kp = 2;
const double Ki = 0.1;
const double Kd = 0.5;
long rolls = 0;

// エンコード用の円盤のスリット数が20なので20カウントしたら1回転。
void detect_turn() {
  static int cnt = 0;
  if(cnt++ >= 20){
    rolls++;
    cnt = 0;
  }
}

void setup() {
  pinMode(AIN1,OUTPUT);
  pinMode(AIN2,OUTPUT);
  pinMode(ROTARY_ENCODER, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ROTARY_ENCODER), detect_turn, RISING);
  Serial.begin(9600);
  analogWrite(AIN1,0);
  analogWrite(AIN2,0);
}

void loop() {
  static double pwm = 0;
  static double e = 0;
  static double e1 = 0;
  static double ie = 0;
  double M;

  static unsigned long last_millis = millis();
  if(millis() - last_millis >= SPAN) {
    e1 = e;
    e = REFERENCE - rolls; 
    ie = ie + (e + e1)*0.25/2;
    M = Kp*e + Ki*ie + Kd*(e-e1)/0.25;

    // 回転数が2未満になると、止まっているか、速すぎて測定できていないかどちらかなので
    // PWM値で状態を判定して停止中なら始動用に300ミリ秒間255(100%)を出力し、そのあと2秒間出力を20にして安定させる。
    // 単に速すぎる場合はそのまま2秒間出力を20にして安定させたのち、pwm値30から再開。
    // 回転数が2以上の場合は通常のPID計算に基づいてpwmを決定。
    if(rolls<2){
      if(pwm<60){        
        analogWrite(AIN1,255);
        delay(300);
      }
      analogWrite(AIN1,20);
      delay(2000);
      pwm = 30;
    } else {
      pwm += M;
    }
    analogWrite(AIN1,int(pwm));
    Serial.print("ROLLS:");
    Serial.print(rolls);
    Serial.print(",");
    Serial.print("PWM:");
    Serial.println(pwm);
    rolls = 0;
    last_millis = millis();
  }  
}

実行結果

実行の結果は以下のようになった。

負荷をかけた瞬間にPWM出力が大きく変動することで逆に回転数の変動は最小限に抑えられ、負荷が無くなった瞬間にPWMも急激に下げることで回転数の急激な上昇を防いでいる。しっかりとD操作が効いている。

苦労した点

今回一番苦労したのはPIDとは関係なくモーターの始動用コードの調整である。
モーターは始動時に大きな電力を必要とするため、一時的に最大出力で始動させる必要があるがそうすると回転数もMAXで始動してしまう。
ただ今回使った速度検出センサーは8,000rpm程度の計測が限界で、それを越えると出力が0になって停止状態と判別できなくなってしまう。
待機秒数を間違えるとうまく始動しなかったり、暴走して延々とpwmが上がり続けたりとなかなかうまくいかない。ここでまる2日ハマった。

所感

私がPID制御に興味を持ったのが2022年6月。以下の記事が初めてである。
thom.hateblo.jp

数学を真面目にやってこなかったので微分も積分もできない状態から1年経過し、ようやくモータースピードの調整までたどり着いた。
結局、微分も積分もできないのは変わっていないけど、言ってることは理解できるようになってきたので実際のコードでやってる近似処理もなんとなく頭の中で結びついてきたといったところ。

やはり何かやりたいことを胸に抱きつづけて少しずつでも、途切れながらでも挑戦をしつづければやがては到達できるんだなと思った。

以上

参考サイト

controlabo.com

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。