お題

トイドローン Tello のサンプルプログラム Tello3.py を改良して、ドローンの状態を受信して表示します。

前回の記事の最後に「ドローンが突然動作 (離陸) しなくなった」と書いたのですが、単なるバッテリー切れでした💦
Telloはバッテリー残量が少なくなると離陸コマンドを受け付けなくなるようです。ということで今回はドローンの状態を受信してバッテリー残量を表示してみたいと思います。

Telloの状態

Telloからは以下の状態が取得できます。

pitchピッチ (角度)
rollロール (角度)
yawヨー (角度)
vgxx速度
vgyy速度
vgzz速度
templ最低温度 (℃)
temph最高温度 (℃)
tofTOF距離 (cm)
h高さ(cm)
batバッテリー残量 (%)
baro気圧 (cm)
timeモーター起動時間
agxx加速度
agyy加速度
agzz加速度

Telloの状態取得方法

SDKドキュメント によると、Telloの状態を受信するには以下を行う必要があります。

  • 事前に command コマンドを送信しておく
  • UDP 8890番ポートで待ち受けを行う

これに従って、前回のサンプルプログラムに「起動時にTelloの状態を表示する」という実装を追加してみます (差分)。

(...)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("", 8889))

# command コマンドを送信
sock.sendto("command".encode(), ("192.168.10.1", 8889))
sock.recvfrom(1024)

# 状態を受信して表示
state_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
state_sock.bind(("", 8890))
data, _ = state_sock.recvfrom(1024)
print()
print(data.decode())

print("Tello Python3 Demo.")
(...)

実行すると以下のように出力されます。

pitch:0;roll:0;yaw:2;vgx:0;vgy:0;vgz:0;templ:87;temph:89;tof:10;h:0;bat:57;baro:59.87;time:0;agx:-1.00;agy:2.00;agz:-1000.00;

バッテリー残量は bat:57 の部分が該当するので、現在のバッテリー残量は「57%」ということになります。

表示メッセージの改善

今の出力内容だとちょっと情報が多すぎて分かりにくいので「バッテリーの残量だけ」を取り出して表示するように表示内容を改善します。

具体的には以下の実装を追加します:

  1. state の文字列を分解してディクショナリに格納
  2. batの値だけを取得して表示

state の文字列を分解してディクショナリに格納する処理はこんな感じです:

def __get_drone_state(data):
    s = data.decode(errors="replace")

    # state の文字列を `;` で区切って
    # `pitch:0`, `roll:0`, `yaw:2`... という形に分割する
    values = s.split(";")
    state = {}
    for v in values:
        # 分割された文字列をさらに `:` で区切り
        # キー (`pitch`) と値 (`0`) に分割して、ディクショナリに格納する
        kv = v.split(":")
        if len(kv) > 1:
            state[kv[0]] = float(kv[1])
    return state

表示部分は以下のように変更します:

print(f'battery: {__get_drone_state(data)["bat"]}%')

差分はこちら

実行結果:

% python main.py

battery: 39.0%

Tello Python3 Demo.
(...)

起動時以外も状態取得する

起動直後だけでなく、それ以降も状態取得してバッテリーの減り具合が確認できるともっと便利そうです。

状態取得の部分をスレッドにすればこれも実現できそうなので実装してみます。

もともとのサンプルプログラム Tello3.py を参考にして以下のように実装してみました (差分):

import threading

(...)

def receive_state():
    while True:
        try:
            data, _ = state_sock.recvfrom(1024)
            print(f'battery: {__get_drone_state(data)["bat"]}%')
        except Exception:
            print("\nExit . . .\n")
            break


state_receive_thread = threading.Thread(target=receive_state)
state_receive_thread.start()

実行結果:

% python main.py

battery: 39.0%

Tello Python3 Demo.
Tello: takeoff land flip forward back left right
       up down cw ccw speed speed?
end -- quit demo.
> battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
battery: 39.0%
(...)

想定通りといえば想定通りなのですが、延々とバッテリー表示が出続けてまともにコマンドが打てない状態になってしまいました💦

ということで、次回はターミナルでの入出力を卒業して GUI を導入してみたいと思います。

Tello 関連記事

かわかみしんいち。島根県津和野町在住のフリーランスエンジニア。複合現実(Mixed Reality)と3DUXでおもちゃを作るのが趣味。 https://github.com/ototadana