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

 

M5stackでTelloを制御

M5stackでUDPが使えるから、Telloのコマンド制御もできるよねと思いましが、既に先人のコードができてます。

https://qiita.com/Mitsu-Murakita/items/b86ad79d3590adb3b5b9

TelloのSSIDだけを追加すれば動きましたが、動作がイマイチもっさり、というのはスイッチの反応が遅くて多少長押ししないとtake offもしない。

で時間待ちの500msを150msにするとほぼイメージ通りに動きますが、疑問点が一つ。

wasPressed()関数って、ボタン押されたことを記憶できる関数のはずなのになぜ長押ししないといけないか?

loop()の中でボタン機能の確認コードを作って確認してみると、どうもdelay()中にボタンを押してもwasPressed()には反映されない模様です、だからdelay()で設定した時間以上にボタンを押していないといけないようです、仕様として変だと思いますが。

~~~
  if ( M5.BtnA.wasPressed() ) {
    Serial.println("BtnA.wasPressed() == TRUE");
  }
  //Serial.println("loop");

  delay(300);
~~~

 

あとフリップ動作は、イマイチスイッチを押すタイミングとM5stackを傾けるタイミングが掴めない。

 

admin

c++でtello制御(コマンドファイル実行とビデオストリーム収録を並行実行)

Telloからビデオストリーム受信して、デコードして表示とローカルファイルに格納、同時にコマンドファイルからコマンド読み出して並行に実行させるバージョンです。

Telloにコマンド送って時間待ち5秒は冗長だろうしスマートでもないから改善余地ありです。

コードはこちら、

https://github.com/chateight/c_plus_video

ctello_stream.cppが今回のソースです。

 

admin

 

c++でtelloを制御する

Pythonからc++のライブラリ呼ぶのは、ライブラリのビルド条件が複雑なので、いっそ全てをc++で作りあげるのが良さそうです。opencvもc++用が存在してるわけだから。

SDK 1.3に記述ある通り、UDPの送信とステータスの受信ポートが違うことは要注意です。

https://dl-cdn.ryzerobotics.com/downloads/tello/20180910/Tello%20SDK%20Documentation%20EN_1.3.pdf

やっていることはPython版と同じく、外部のテキストファイルに記述されたコマンドを順次telloに送ってステータスを受信しているだけです。

実は、コマンドの実行結果を受け取る前に次々にコマンド送ってるから、スプールされてしまっているようだから、作りが変だとは思う。

コードはこちら、

https://github.com/chateight/c_plus_video

ソケット通信の基本の解説は、

https://qiita.com/Michinosuke/items/0778a5344bdf81488114

通信サンプルは、

https://qiita.com/srs/items/c9286b5cff99e741b993

 

c++での制御は、すでに先駆者がいるから、この先はこれを使った方がいいでしょう。

https://github.com/carlospzlz/ctello

P.S. 2022/11/5

これはTello EDU用でした、違いを修正してもいいかもしれないけど。

 

動作は他の手段で動かすのと同じ。

 

admin

h264decoder共有ライブラリはBoost.Python使っている?

以下の記事中で、

https://isehara-3lv.sakura.ne.jp/blog/2022/10/28/telloで画像転送python-h-264共用ライブラリ/

c++のライブラリをPythonから呼び出すためにpybind11のインストールが指定されていますが、c++ライブラリのラッパーファイル(h264decoder_python.cpp)を見てみると、pybind11ではなくてBOOST_PYTHONが使われているからpybind11は不要だよねと思いました。

BOOST_PYTHON_MODULE(libh264decoder)

試しに、仮想環境(v_python)からpybind11をpip uninstall pybind11しても動作はするから、やはり不要らしい。

Boost.Pythonをpybind11と比較するとサイズが巨大らしい。pybind11の方が後発だから、恐らく機能的には優っているだろうしユーザーも多いのではないかと思います。

P.S. 2022/10/31

H.264 decoderのビルドのためのCMakeLists.txtを見てみるとpybind11を探して、もし存在しなければ持ってきてるからビルドには必要とされているようです、何故だろう?

find_package(pybind11)
if(pybind11_FOUND)
  message("Using existing pybind11 v${pybind11_VERSION}")
else()
  message("Fetching pybind11")
  include(FetchContent)
  FetchContent_Declare(
    pybind11
    GIT_REPOSITORY https://github.com/pybind/pybind11
    GIT_TAG v2.5.0)
  FetchContent_MakeAvailable(pybind11)
endif()

P.S. 2022/11/7

今更ながらですが、改めて見るとpybind11使ってました。じゃ最初のソースは何見たんだろう?とりあえずすっきりはしましたが、

PYBIND11_MODULE(h264decoder, m)
{
  PyEval_InitThreads(); // need for release of the GIL (http://stackoverflow.com/questions/8009613/boost-python-not-supporting-parallelism)
  py::class_(m, "H264Decoder")
                            .def(py::init<>())
                            .def("decode_frame", &PyH264Decoder::decode_frame)
                            .def("decode", &PyH264Decoder::decode);
  m.def("disable_logging", disable_logging);
}

 

admin

Telloで画像転送(Python + H.264共用ライブラリ)

以下でコマンドは送信できましたが、画像の受信には追加の手段が必要になります。

https://isehara-3lv.sakura.ne.jp/blog/2022/10/26/dji-telloはudpでテキストコマンド送れば制御できる/

コマンド/レスポンスと画像転送はポートが分かれています、ある意味当然。

DJIのリンクにもH.264デコーダーは掲載されてますが古すぎて(四年前)動かない、そもそもがPython2.x系用だし。

で、環境に合わせてビルド必要ですが、以下のサイトがよくまとまっています。

Windws/Linux/Mac(intel/mx)と全てのプラットホームが解説されています。

https://take6shin-tech-diary.com/tello-video-python3/

<動作環境>

・intel Mac ventura

・Python3.9(環境はAnacondaで構築)

ビルドされたライブラリのディレクトリは、

c++をMacのPythonから呼び出すという情報が名称に含まれています。

 

<動作させてみる>

ドローンは飛行させなくても、画像は送られてくるので最初の画像はTelloの梱包箱。

UIもPythonのライブラリ(tkinter)使って作られます。

次はopencv使って、画像認識らしいことをやらせてみることでしょう。

 

admin

DJI TelloはUDPでテキストコマンド送れば制御できる

TelloにはDJIの提供する標準のアプリもありますが、Tello自体はテキストコマンドで制御できる、つまり専用のドライバなどは不要、ということで簡単にカスタムアプリができます。

UDP/ポート8889でソケット接続すれば、ドライバ不要でテキストモードでコマンド送信とレスポンス受信ができます。

 

<コマンド一覧>

SDK

The Tello SDK connects to the aircraft through a Wi-Fi UDP port, allowing users to control the drone with text commands

https://alfredo-reyes-montero.gitbook.io/tello-dji/sdk

 

<サンプル>

以下のサンプルプログラムを持ってきて、

https://github.com/dji-sdk/Tello-Python

Python2.7なので一部3形に書き換え必要なので書き換えて、

https://github.com/chateight/tello_python

に置いてあります、Python3への変換(except文とprint文)は機械的にできます。

コマンドはテキストファイル(以下ではcommand.txt)で一番単純であろうコマンドを定義して、

command
takeoff
delay 1
land

最初のcommand文はAPIモードに入る宣言として必要です。

 

<実行結果>

コマンドの引数でコマンドファイル(command.txt)を指定します。

% python tello_test.py command.txt
sending command: command to 192.168.10.1
from ('192.168.10.1', 8889): b'ok'
Done!!! sent command: command to 192.168.10.1
sending command: takeoff to 192.168.10.1
from ('192.168.10.1', 8889): b'ok'
Done!!! sent command: takeoff to 192.168.10.1
delay 1.0
sending command: land to 192.168.10.1
from ('192.168.10.1', 8889): b'ok'
Done!!! sent command: land to 192.168.10.1
Traceback (most recent call last):
  File "/Users/xxxxxx/github/tello_python/tello_test.py", line 28, in 
    out = open('log/' + start_time + '.txt', 'w')
FileNotFoundError: [Errno 2] No such file or directory: 'log/2022-10-26 13:06:37.254305.txt'

ログファイルができてないと言われてますが、それはlogディレクトリ作成してないからで、logディレクトリ作成するときちんとログが取れました。

id: 0
command: command
response: b'ok'
start time: 2022-10-26 14:27:31.283580
end_time: 2022-10-26 14:27:31.309985
duration: 0.026405


id: 1
command: takeoff
response: b'ok'
start time: 2022-10-26 14:27:31.310001
end_time: 2022-10-26 14:27:38.808229
duration: 7.498228


id: 2
command: land
response: b'ok'
start time: 2022-10-26 14:27:39.813307
end_time: 2022-10-26 14:27:42.531767
duration: 2.71846

 

ソケットで接続すれば良いだけなので、言語はPythonに限らず今時の言語ならなんでもつながるということになるので、ライブラリ次第で使い分けでしょう。

例えば、ScratchのTello拡張だと、Node.js使ってソケット接続しています。

TelloEDUモデル(編隊飛行だけでなく)だとクライアントモードでつながるので、クラウドサービス(例えばGoogle TM)を使った画像認識とかもできますね。EDUモード買えばよかった。

 

admin