Go Lang

今時の言語のGoをしばらく触ってみます。とりあえずのゴールはラズパイでWebサーバー構築してM5Stackとやりとりすること。node.jsと同等の機能が実現できるはずで、コンパイラーであるだけ高速でしょう。

とりあえずM1 Macと初代に限りなく近いRaspberry PIにインストールしてみました。

— M1 Mac環境 —

<install>

% brew install go

するだけ、

% go version

go version go1.19.4 darwin/arm64

が今の最新版数のようです。

VScodeはGoマークの拡張機能を二つインストールしただけでソースコード編集ができます、追加で必要ならばインストールを要求されます。

 

<directory>

ユーザディレクトリ直下にgoという名前で作成されます。

 

<初めてのソース>

定番ですが、

package main
import "fmt"
func main(){
	fmt.Println("hello Go")
}

WordPressのアドオン古くてGo言語選択できないので近そうなC++にしています。

import “fmt”は他言語のimport “sys”のようなものでしょうか。

 

<module>

そこそこの規模になるとモジュール化が必要ですが、先ほどのソースディレクトリにモジュール用のディレクトリ作成してモジュールを作成します。

% mkdir mod
% cd mod
% go mod init mod 
% cp ../hello_go.go ./
% go mod tidy
% go build
% ./mod
hello Go

全体のディレクトリ構成は以下のようになります@VScode

通常の% go runでは実行ファイルはテンポラリに作成され実行後に削除されるようですが、実行ファイルを残すためには% go buildを使います。

このようにgoコマンドのパラメータ指定で作業をコントロールできるようになっています。

 

— RaspberryPI 環境 —

https://zenn.dev/ysmtegsr/articles/20d6e0c7159be2

を参考にインストール、

$ wget https://go.dev/dl/go1.19.4.linux-armv6l.tar.gz

$ sudo tar -C /usr/local -xzf go1.19.4.linux-armv6l.tar.gz

/usr/local/go/bin/に移動して、

$ ./go version
go version go1.19.4 linux/arm

まだパスが通っていない、

パスを通す、

$ echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
$ echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.bashrc

$ source ~/.bashrc

Macと同じような簡単なソースファイル(main.go)をgo_prj配下に作る、

package main

import "fmt"

func main() {
    fmt.Println("Hello world")
}

$ go mod init go_prj

$ cd go_prj

$ tree ../go_prj
../go_prj
├── go.mod
├── main
└── main.go

$ go run

コンパイル実行だとRaspberryPIでは数秒待つから遅い

$ go build -o main

$ ./main

で実行ファイル呼び出すと高速(当然ですが)、この起動時間待ちの雰囲気はnode.jsの起動でも同じだからRaspberry PiではGoをbuildして使うのが普通になると思います。

 

Go言語の第一印象

随所に現代的(ビルド環境やソースコードの検証など、まだイントロしか読んでいないからこの程度)なところを見ることができます。既存の言語はバージョンアップでも過去との互換を考えないといけないわけですが、新たな言語はその時点で必要な機能を順位づけして言語仕様や開発環境を決めることが出来るわけですから。

 

admin

LTspice

現在は普通にSpiceとして使えるシミレーションソフトも無償なんですね。ただのものはとりあえず入れてみました。

ただ、MacだとU.IがWindowsとは別物で、右クリックで出てくるメニューから機能を設定することになります。従って初期画面は素っ気ない感じです。

使い方は例えば回路CADであるKiCadなどと類似ですね。所詮回路図作成必要だから。

図は、L/C/Rの直列回路に正弦波のバースト信号を入力した時の応答シミレーションです。

電圧源と抵抗とインダクタの接続点の二箇所の波形を表示させています。デバイスのSpiceモデルは潤沢に整備されているから、アナログ回路のシミレーションには使えます。とはいっても道具ですから、基本的なリテラシィがないと結果が無意味なのは他も同じ。

 

admin

M5Stack内蔵のADCを使ってみる

ネット記事によると精度はそれほどでは無いようですが、実際に使ってみました。

M5StackのAD変換できるピンは35/36になりますが、35ピンに可変電源を1/2.5分割して供給。

測定精度は数%の誤差はあるようですが、用途によっては使えるでしょう。たとえば倒立振子のモーター駆動用電池の電圧をチェックするような目的には。

ノイズは結構盛大にありますから、何回か測定して平均値を取るようにしたほうがいい。

アナログメーター風の表示にはArduinoIDEのサンプルプログラム(TFT_Meter_liniear)を改造して使用。

 

admin

倒立振子(その後)

モーターの駆動にはNiH単三4本使っていますが、電池電圧が低下するとトルクも変わってくるのでPIDパラメータもその変化に対応して変えないと安定状態を保てなくなるという当然の結果。

現実的な対応としては、

① 電池電圧を安定化する

少なくともDD-CON必要だし、電圧ドロップ考えると電池電圧足りないだろうからハード作り直しレベル

② 電池電圧の変化に応じてモーターへの最低印加電圧を可変する

float MOTOR_POWER_MIN = 100;
float MOTOR_POWER_MAX = 250;

ここでMOTOR_POWER_MINを変えてやる、今はモーターが回り始めるぐらいの電圧になるように調整していますが、当然電圧低下するとこの値を変えてやらないとダメなのは自明。

理屈ではM5StackのADコンバーター使って可変することはできそうだね、やるやらないは別にして。

 

admin

M5Stackでオンメモリのイメージデータを表示する

SDカードを使うのは、特に書き込み中のデータ保証ができないのがM5Stack、メモリはそこそこ余裕があるのでメモリ上にイメージファイルを展開してそれを表示する関数、

M5.Lcd.drawJpg(const uint8_t *jpg_data, size_t jpg_len, uint16_t x, uint16_t y)
を使うとオンメモリでの画像表示ができます。M5Stackはjpg形式のデータしか表示できないようですが、フリーのpng形式イラスト素材を持ってきてPhotoScape Xでjpgに変換して、以下のリンクのPythonスクリプトで配列データに変換します。
変換した配列データをソースコードとして扱いコンパイルすると画像データを含むバイナリが作成できます。
こんなデータを使いました。
ソースコードは、
M5Stack上の表示は、
こんな感じで表示できます。
admin

3Dプリンタノズル終了

造形中に例のガタガタ音(フィラメント送りモーターの脱調)、そのうち治るかと思っていても悪化して全くノズルから出てこない状態になってしまった。

ノズル清掃して、その後の送りでもダメだから寿命と思ったほうが良いだろう。

写真は取り外したのもの、Flashforgeのサイトで購入(Adventure3用)したけど、正月明けになるよね。定格400時間と言ってるけど、そこまでの使用時間は無いけども。

外観見る限りは、この中にヒーターと温度センサーが仕込まれているようです。

 

admin

倒立振子(PI制御でハンチングなくした)

P制御だけだと、どうしてもハンチング(オーバーシュート)が発生してしまいますが、I制御を追加することでそれを抑止できます。

D係数は外乱対応とのことですが、調整方法を理解していないので後日触りますが、とりあえずの目標の静止状態は作ることができました。

スピーカーから盛大にノイズが出てますが、これはスピーカー制御端子(25)をモーター制御に使っているから。逆にこういうノイズが出ているときには静止状態であると言えます。

設計データは以下のリンクへ、

https://github.com/chateight/reversed_pendulum

パラメータ(PID)調整はリコンパイルでは時間の無駄なので、Processingを使って動作中に書き換えできるようにしています。

admin

Naim

割と選曲が独特なラジオ局、ビットレートは300Kbps未満で270Kpbsとか変則的ですが、音はそこそこ良い。

どこの曲かと調べるとUKですね、どうりでIona Brownとかの曲がよく流れてる。

実はNaimはオーディオ機器もプロダクトラインにあって、日本でも過去には代理店もあったようです。今は存在しないようですが。そういう意味ではLinn radioと似ています、Linn radioは未だ休止状態継続ですが。

 

admin

 

倒立振子(P制御のみ有効化)

チューニングで問題だったのは、M5Stackの画面にログ用に表示させている数値の表示がやたらに遅いこと。ジャイロ・加速度・pitch/role/yawを画面表示させているとloop()の処理時間がおよそ50msも掛かっていました。これではフィードバックが間に合わないので、ログ表示を削除すると10ms以下に収まってます。

P値も大きすぎるとリンギングは盛大になるので、適当なところで止めてみたのが以下の動画です。まあ、振動はしてるし、外乱にも対応できない状態ですが。

 

admin

 

倒立振子パラメータ調整の準備まで完了

倒立振子の製作を始めてますが、PID制御のパラメータ設定を簡単に行えるような準備も整ったのであとはコード全体を書いて調整に取り掛かるだけ。

PIDパラメータは調整のおおまかな方針は決まっているようですが、最後の微調整はカットアンドトライになるので、いちいちソースコンパイル、ダウンロードでは効率が悪いので、ダイナミック(on the fly)でパラメータ書き換えの手段が必須になります。

やり方はM5Stack側はUDP受信で、送信側はProcessingを使って更新します。

<Processingのコード>

/*
set PID parametor to the m5stack using UDP

*/
import hypermedia.net.*;
import controlP5.*;

UDP udp;
ControlP5 cp5;

final String IP = "192.168.1.15";
final int PORT = 3002;
String msg = "10, 3, 1";  // P, I, D

void setup() {
  size(200, 200);

  cp5 = new ControlP5(this);
  udp = new UDP( this, 3002 );

  ControlFont cf = new ControlFont(createFont("Serif",20));

  cp5.addButton("UDP_Msg")
    .setFont(cf)
    .setLabel("send")
    .setPosition(50,50)
    .setSize(100,100);
}

void draw() {
  background(200);
}

void UDP_Msg(){
  udp.send(msg, IP, PORT);
}

SENDボタンでUDP送信、パラメータの書き換えはソースコードで行いますがコンパイル時間は見えないので、実質瞬時に対応できます。

<M5StackのUDP受信処理抜き出し>

M5StackのRegExp処理はいまいちよく分からなかったので、受信テキストは文字列分割で対応。カンマ区切りの3個のパラメータだけなので文字分割でも対応できますが、その分コードは美しくない。このコードをloop()処理中に記述して、ダイナミックにPIDパラメータ変更を実現します。

UDP通信とパラメータ格納の該当部分だけ抜き出したコードです。


#define N 1024

// PID variables
float P_val = 50;
float I_val = 2;
float D_val = 10;

WiFiUDP udp;

	  char packetBuffer[N];
	  int packetSize = udp.parsePacket();

	  if (packetSize){
		  int len = udp.read(packetBuffer, packetSize);

      String pid_data = String(packetBuffer);
      //Serial.println(pid_data);
      int index = pid_data.indexOf(",");
      P_val = (pid_data.substring(0, index)).toFloat();     // P
      pid_data = pid_data.substring(index + 1, pid_data.length());
      index = pid_data.indexOf(",");
      I_val = (pid_data.substring(0, index)).toFloat();     // I
      pid_data = pid_data.substring(index + 1, pid_data.length());
      D_val = pid_data.toFloat();                           // D

 

admin