gobotを使ってTelloを制御する

gobotはrobotics/IoT用のフレームワークですが、サポートモデルとしてTelloが対象に含まれています。

https://gobot.io/blog/2018/04/20/hello-tello-hacking-drones-with-go/

でコマンド制御と動画再生を行っているので、コードを実行してみました。

動画の再生にはmplayer(これは追加インストール必要でした)を使って、goから引き渡しています。

$ brew install mplayer

でインストールします。go getでもmplayerがインストールできますが、外部アプリなのでこちらではない。

<環境構築>

Goはインストール済みの前提で、gobotの他にdji/telloのインストールも必要。これはmod内でしかインストールできません。

% go get -d -u gobot.io/x/gobot/...
% go get gobot.io/x/gobot
% go get gobot.io/x/gobot/platforms/dji/tello

<Telloから画像受け取り再生するコード>

Telloの素のコマンドは意識する必要はなくて関数でwrapされています。明示的に並行処理は使っていなくて、drone.On()はイベント待ちのように見えます。

package main

import (
	"fmt"
	"os/exec"
	"time"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/platforms/dji/tello"
)

func main() {
	drone := tello.NewDriver("8890")

	work := func() {
		mplayer := exec.Command("mplayer", "-fps", "25", "-")
		mplayerIn, _ := mplayer.StdinPipe()
		if err := mplayer.Start(); err != nil {
			fmt.Println(err)
			return
		}

		drone.On(tello.ConnectedEvent, func(data interface{}) {
			fmt.Println("Connected")
			drone.StartVideo()
			drone.SetVideoEncoderRate(4)
			gobot.Every(100*time.Millisecond, func() {
				drone.StartVideo()
			})
		})

		drone.On(tello.VideoFrameEvent, func(data interface{}) {
			pkt := data.([]byte)
			if _, err := mplayerIn.Write(pkt); err != nil {
				fmt.Println(err)
			}
		})
	}

	robot := gobot.NewRobot("tello",
		[]gobot.Connection{},
		[]gobot.Device{drone},
		work,
	)

	robot.Start()
}

 

動画は外部のアプリを使っているせいか、遅延が大きいのと画素が飛んでいます。gobotの本質とはそれほど関係ないですが、

gobotはこれからも成長していくだろうから、Robotics/IoTアプリ開発はかなり楽になりそうです。

 

admin

 

interfaceの基本(Go言語)

interfaceはGo言語における唯一の抽象型だそうで、抽象型というのは実装の自由度がある訳だから使いこなせば便利でしょう。

ここでは使いこなしではなくて、基本機能の説明です。構造体のデータの操作を行う二つのメソッドを定義しています。

package main

import (
	"fmt"
)

type person struct {
	FirstName	string
	FamilyName	string
	Age			int8
}

type inf interface{
	print()
	exchange()
}

func (p *person) print(){
	fmt.Println(p.FirstName, p.FamilyName, p.Age)
}

func (p *person) exchange(){
	fn := p.FirstName
	p.FirstName = p.FamilyName
	p.FamilyName = fn
	p.print()
}

func main(){
	var i1 inf = &person{
		FirstName: "Mary",
		FamilyName: "Bloody",
		Age: 36,
	}

	i1.print()

	i1.exchange()
}

実行結果は、

Mary Bloody 36
Bloody Mary 36

Goの場合にはJavaやC#のように明示的なimplementは不要です、つまり緩い関係になってます。interfaceで定義したメソッドが全て実装されていればimplementと等価です。

じゃ『interfaceは何が良いの?』というとそれは抽象型だからというところに行き着くでしょう、何故なら実装をどう記述しようがinterfaceに規定したメソッドと同じ形(名称、引数と戻り値)であれば入れ替えできるわけで、実は明示的では無いですが継承や多態性が実現できることになります。それゆえGoにおける唯一の抽象型というのは使い方に多様性があることになります。

例えば、上の例で異なる型を扱う同名のメソッドを定義すれば、それは正しく多態性になる訳だから。

このように型によって緩やかな結合を取ることがGoの一つの大きな特徴であるように思います

P.S. (2023/1/25)

interfaceの型名は~~er(~~するもの)とするのが通例のようですから、命名規則からは外れています。

 

admin

 

Apple SiliconのFusion 360 native対応

https://isehara-3lv.sakura.ne.jp/blog/2022/07/18/m1-macbook-air到着/

昨年夏にM1 MacBok Airも立ち上げましたが、Intel Macが継続して必要だった大きな理由のFusion 360が動かない問題は今年の夏頃にはnative対応できるらしい。

以下、Autodeskのサイトからの引用、

唯一の残りはレーザープリンタードライバーが対応しないから使えないだけ、トナー変えてそれほど使っていないから勿体無いし、そもそもプリンタ使う頻度も少ないからどうするか?

P.S. 2023/1/23

canonのサポート見てみたらLBP6230のVentura対応ドライバーが登録されてたのでインストール。特にApple Siliconの記述はないからユニバーサルか。

 

admin

値を変更するならポインター渡し(Go言語)

https://isehara-3lv.sakura.ne.jp/blog/2023/01/17/goは値渡し言語、/

で、Goは値渡しなので、渡し先で値を変更するならポインター渡しと言っていますがその例です。

以下のソースで関数printPersonにポインターを渡した時はjohnの値が変更されていますが、値渡しにすると元の値は変更されません。

package main

import (
	"fmt"
)

type Person struct{
	LastName string
	FirstName string
	Age int
}

func (p Person) printPerson(p1 *Person) Person{
	p1.Age = 72
	fmt.Println("John : ", p1.FirstName, p1.LastName, p1.Age)
	return *p1
}

func main() {
	var john Person
	john.FirstName = "John"
	john.LastName = "Wein"
	john .Age = 70
	john.printPerson(&john)
	fmt.Println("returned age : ", john.Age)
}

以下はポインタ渡しと値渡しでの実行結果です、確かに値渡しではオリジナルの情報は更新されません。

<ポインター渡し>
John :  John Wein 72
returned age :  72

<値渡し>
John : John Wein 72
returned age : 70

 

admin

 

 

SSDとHDDでTimeMachineの振る舞いが違う

昨年、NASの一台のディスクを容量拡大のためにSSDからHDDに入れ替えましたが、MacのバックアップがSSD(timemachine)では制限値80%で古いファイルは削除されて追加されているようですが、HDD(Timemachine_2)では80%の壁は乗り越えて増殖中。最後にはおそらく削除は発生すると思うけれども、特に設定は無いように思うけれどもなんでだろう?

P.S. (2023/2/5)

結局どちらの領域も制限地では留まらず、

 

admin

Rustの基本的なsyntax

初めてRustをPlaygroungで動かしてみたけど、結構独特だと思う

https://www.rust-lang.org

“for” loopはイテレータ専用

“The for in construct can be used to iterate through an Iterator. “

通常のループ処理は無限ループ使ってcontinuebreakで条件判断

<初めてのRustのコード>

fn main() {
    let mut i = 0;
    loop {
        println!("i = {}", i);
        if i == 10{
            break i;
        }
        i += 1;
    };
}

・mutable変数はmutで宣言が必要、宣言しないとimmutable変数になる

・println!では必ずプレースホルダー”{}”を入れる

loop}にも;が必要、if{}}の後ろにはあっても無くてもコンパイルエラーにはならないけれども

・ifの条件をカッコ()で括らないのはGoと同じ

 

admin

Goは値渡し言語、

タイトルの通りですがGo言語は値渡し、例えば関数に引き渡す引数は値が引数のアドレスとは違う場所の関数内で使用する変数のアドレスにコピーされて使われます。

これでは都合悪い時もあるだろうから、参照渡しのためにポインターが存在知ると考えれば良いかもしれない。ポインターはC/C++の記法そのままで、

package main

import "fmt"

func main() {
	val := 20
	pointer := &val
	*pointer = 23
	fmt.Println("address : ", pointer, "value : ", val)
}


address :  0x14000126008 value :  23

のように使います、しかしGoでポインターを普段使いしたら、変数がmutableになることでコードの見通しが悪くなるから使うのは限定的だろうと思う。Goでは通常の変数はすべてimmutable扱いだから、逆に言えばポインター宣言は明示的なmutable宣言であるということです。

例にあるようなケースでわざわざポインターを使う必要性はないし、こんな使い方はしちゃいけない例です。数少ない例外はインターフェースを受け取るだけだとは『初めてのGo言語』の記載。

 

admin

 

 

 

M5Stackでの表示ちらつきの防止方法、

普通はM5Stackで表示内容の書き換えするときに、一度表示内容を消去する必要がありますが、それが表示がちらつく原因です。

ちらつき防止のためには、画面表示内容をフルで別のバッファーに用意しておいて、それを押し込めば表示消去時間が存在しないことになるのでちらつきの防止ができます。

以下のサンプルから、

https://craft-gogo.com/m5stack-sprite/

をそのまま実装してみましたが、確かにちらつきは見えなくなります。

画面書き換えの処理時間を見るために、前後で時間を取得して処理時間を見てみましたが、画面の押し込み(pushSprite())の処理時間は実は普通に画面を書き込む時間と変わらず、大体40msぐらい掛かっています。要は表示領域を一度クリアしないからちらつきを感じることなくなっているわけで、決してpushSprite()にして画面描画時間が高速化される訳ではありません。

 

admin

 

Raspberry PIのホスト名を変える

ラズパイがネットワーク内に複数台存在しているとmDNSで識別できないから、どれかの変更が必要です。

そのためには、

/etc/hostnameと/etc/hostsを二つとも変更します。

/etc/hostname
/etc/hosts

pi@raspberrypi:~ $ cat /etc/hostname
rasp-b

pi@raspberrypi:~ $ cat /etc/hosts
127.0.0.1	localhost
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters

127.0.1.1		rasp-b

変更後にリブートすれば、サイドのログオン時には設定したホスト名が使えるようになります。

 

admin

 

Goの並行処理と並列処理、

ダラダラと継続中ですが、とりあえずの現状のまとめです。

<条件>

M1 MacBook air

1000万までの素数計算してスライスに格納、昇順は考慮してない

 

<言えること>

・goはシングルスレッドでもマルチコアを使うように(並列処理するように)動く、これは便利だ

・channelのオーバーヘッドはmutexよりも大きい予想通りですが、ただしmutexのように個別の変数に対する考慮は不要だからエラーの入りにくいのがchannel

・コア数を強制的にしてしてやるとこのコードの場合には4コアぐらいが最適、もちろん状況により変わりますが

それぞれのソースコードはこちら、

https://github.com/chateight/go/tree/master/concpara

 

 

admin