ジャコ Lab

プログラミング関連のメモ帳的ブログです

エラーの原因:Tello からステータスを得ることができない調査

エラー原因の調査中

DJITelloPy を使うとエラーになってしまい Tello の SDK モードにすら入ることができませんでした。

原因を究明したいです

エラーの内容

djitellopy/tello.py", line 547, in connect
    raise TelloException('Did not receive a state packet from the Tello')
djitellopy.tello.TelloException: Did not receive a state packet from the Tello

L547 ですね・・・?

エラーの送出箇所

djitellopy/tello.py#L547

    def connect(self, wait_for_state=True):
        """Enter SDK mode. Call this before any of the control functions.
        """
        self.send_control_command("command")

        if wait_for_state:
            REPS = 20
            for i in range(REPS):
                if self.get_current_state():
                    t = i / REPS  # in seconds
                    Tello.LOGGER.debug("'.connect()' received first state packet after {} seconds".format(t))
                    break
                time.sleep(1 / REPS)

            if not self.get_current_state():
                raise TelloException('Did not receive a state packet from the Tello')

not self.get_current_state() で、Exception ということですね・・・。
self.get_current_state() を追っていくと「ポート番号:8890」のステータス取得にたどり着きます。

つまり、Tello からステータスを得られていないことになります。

wait_for_state を False に設定することで無視することも可能ですが、
default が True ということは、動くのが普通なのでしょう・・・。

もう少しシンプルなコードで確認する

Tello-Python/tello_state.py を使って、単純なステータス取得処理を実行してみます。
コードは少し手を入れます。

    import socket
    from time import sleep
-   import curses

    INTERVAL = 0.2


-   def report(str):
-       stdscr.addstr(0, 0, str)
-       stdscr.refresh()

    if __name__ == "__main__":
-       stdscr = curses.initscr()
-       curses.noecho()
-       curses.cbreak()

        local_ip = ''
        local_port = 8890
        socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # socket for sending cmd
        socket.bind((local_ip, local_port))

        tello_ip = '192.168.10.1'
        tello_port = 8889
        tello_adderss = (tello_ip, tello_port)

        socket.sendto('command'.encode('utf-8'), tello_adderss)

        try:
            index = 0
            while True:
                index += 1
+               print('[START] recvfrom')
                response, ip = socket.recvfrom(1024)
+               print('[END] recvfrom:', response)
+               response = response.decode('utf-8')
                if response == 'ok':
                    continue
                out = response.replace(';', ';\n')
                out = 'Tello State:\n' + out
                report(out)
                sleep(INTERVAL)
        except KeyboardInterrupt:
-           curses.echo()
-           curses.nocbreak()
-           curses.endwin()
+           pass
curses がなんか上手く動かなかったので消しました。あとレスポンスは byte 文字列だったので decode しました。

実行してみます

$ python tello_state.py 
START: recvfrom
END: recvfrom: b'ok'
START: recvfrom
このまま停止してしまうようです

b'ok' しか返ってこないとなると djitellopy/tello.py#L211-L212 に入ることになるので not {} == True により、エラーということですね・・・。十中八九このステータス取得が上手くいかないことが原因ですね・・・。

別の環境でやってみる

現在、以下の環境で試しています。

Mac でやってみる

$ python tello_state.py 
[START] recvfrom
[END] recvfrom: b'ok'
[START] recvfrom
[END] recvfrom: b'pitch:0;roll:0;yaw:0;vgx:0;vgy:0;vgz:0;templ:48;temph:50;tof:10;h:0;bat:55;baro:-14.23;time:0;agx:-2.00;agy:-13.00;agz:-998.00;\r\n'
[START] recvfrom
[END] recvfrom: b'pitch:0;roll:0;yaw:0;vgx:0;vgy:0;vgz:0;templ:48;temph:50;tof:10;h:0;bat:55;baro:-14.28;time:0;agx:-1.00;agy:-12.00;agz:-999.00;\r\n'
[START] recvfrom
[END] recvfrom: b'pitch:0;roll:0;yaw:0;vgx:0;vgy:0;vgz:0;templ:48;temph:50;tof:10;h:0;bat:54;baro:-14.06;time:0;agx:0.00;agy:-11.00;agz:-1001.00;\r\n'
完璧やん・・・

Mac で DJITelloPy を使ってみる

import cv2
from djitellopy import Tello

tello = Tello()
tello.connect()
tello.query_battery()
% python app.py
[INFO] tello.py - 129 - Tello instance was initialized. Host: '192.168.10.1'. Port: '8889'.
[INFO] tello.py - 438 - Send command: 'command'
[INFO] tello.py - 462 - Response command: 'ok'
[INFO] tello.py - 438 - Send command: 'battery?'
[INFO] tello.py - 462 - Response battery?: '50'
完璧やん・・・

まとめ

DJITelloPy でエラーになってしまう問題は実行環境の問題の可能性が高いことがわかりました。ファイアウォールか?ファイアウォールなのか??昨日見た Stack Overflow や Issue にはファイアウォールのことが書かれていたんですよねぇ・・・明日、続きをやってみましょう