tiffany1098
@tiffany1098

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

[Python]telloEduで編隊飛行を行った際のカメラ映像の取得方法を教えてください。

質問内容

[Python]telloEduで編隊飛行を行った際のカメラ映像の取得方法を教えてください。

この質問は以下のサイトでマルチポストしています。
分かったこと、補足説明や解決方法はどちらのサイトも更新します。
https://teratail.com/questions/289372

UDPやIPアドレスについてあまり詳しくないため、間違っていましたらご指摘お願いします。

発生している問題

tellopyなどは使わず、TelloSDK2.0のコマンド送信によるプログラムを書いています。
TelloEdu1台での飛行時のカメラ映像は「command」の次に「streamon」とコマンドを送信したのち、OpecCVで
cap = cv2.VideoCapture('udp://0.0.0.0:11111')
ret,frame = cap.read()
cv2.imshow('',frame)
と書きtelloの映像を取得していました。1台の時はこの方法でPC画面に映像を表示させることに成功しました。
しかし、telloEDU3台の編隊飛行の際はこの方法では成功しません。実行画面ではstreamon OK まで進み、その後何も起こりません。3台使うことによりカメラのUDPが変わるなど、何か原因があると思うのですがそれが分かりません。

telloEDUの編隊飛行(前進、後退などの単純な動き)自体はうまくいっています。

補足説明

tello 1台のときは、tello自体がwifiアクセスポイントとなります。
telloのIPアドレス、ポート番号は公式通りに
('192.168.10.1',8889)
を使用しています。
映像受信時のUDPも公式通りに
('udp://0.0.0.0:11111')
を使用しています。

tello3台のときは
wifiルーターにtello3台とパソコンをつなげているため、IPアドレスが変わるため
ドローン1:('192.168.100.112',8889)
ドローン2:('192.168.100.113',8889)
ドローン3:('192.168.100.114',8889)
を使用しています。このIPアドレスはwifiにつなげた状態でパソコンでIPアドレススキャナーのアプリでスキャンして得ました。
映像受信時のUDPは1台の時と変わらず
('udp://0.0.0.0:11111')
を使用していますが、映像を取得できませんでした。「streamon」はきちんと送れているようなので、telloのIPアドレスは正しく、映像受信時のUDPが間違っているのではないかと考えています。

IPアドレスを変更するとUDPも変わるのでしょうか。変わるとしても、変更後の値が分かりません。そもそも編隊飛行ではカメラを使えないのでしょうか。

ご回答宜しくお願い致します。

試したこと


3台全てのTelloがPC側の同じUDPのアドレスに同時に映像を(ブロードキャストで)投げ込んでいるため、サーバであるPC側ではそれらが混在して正しく映像として結合できないことが問題かもしれないため、プロキシサーバでPC側で受けた映像をIPアドレスに応じて別のポートに投げ込むといいとアドバイスを頂きました。しかし、プロキシサーバーを起動した状態でもうまくいかず「stereamon OK」で止まりました。


wi-fiルーターにパソコンとドローン3台ではなく、1台のみ接続し試しました。
ドローン1:('192.168.100.112',8889)
('udp://0.0.0.0:11111')
で試しましたが、「streamon OK」で止まり、映像の取得はできませんでした。
つまり、混信以前のUDPなどが間違っていることになるのでしょうか。

#試したこと②
import socket 
import threading
import time
import cv2

event = threading.Event()

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
sock.bind(('',9000))

tello1_address = ('192.168.100.112',8889)

def send_command1(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello1_address)
    event.wait()
    event.clear()

def send_command2(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello2_address)
    event.wait()
    event.clear()

def send_command3(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello3_address)
    event.wait()
    event.clear()

def send_command(msg):
    print(msg,msg,msg, sep='\n', end='\n')
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello1_address)
    sock.sendto(msg,tello2_address)
    sock.sendto(msg,tello3_address)
    event.wait()
    event.clear()

def send_command12(msg1,msg2):
    print(msg1,msg2, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg2 = msg2.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg2,tello2_address)
    event.wait()
    event.clear()

def send_command23(msg2,msg3):
    print(msg2,msg3, sep='\n', end='\n')
    msg2 = msg2.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg2,tello2_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def send_command13(msg1,msg3):
    print(msg1,msg3, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def send_command123(msg1,msg2,msg3):
    print(msg1,msg2,msg3, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg2 = msg2.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg2,tello2_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def recv():
    while True:
        try:
            data,server = sock.recvfrom(1518)
            print(data.decode(encoding="utf-8"))
            event.set()
        except Exception:
            break
recvThread = threading.Thread(target=recv)
recvThread.start()


def main():
    try:
        send_command1('command') 
        send_command1('streamon')

        cap1 = cv2.VideoCapture('udp://0.0.0.0:11111')

        while True:
            ret,frame1 = cap1.read()
            cv2.imshow('TelloEDU1',frame1)

            key = cv2.waitKey(1)
            if key == 27:
                break


    except Exception as ex:
        print(ex)
    finally:
        cap1.release()
        cv2.destroyAllWindows()
        send_command1('streamoff')
        sock.close()
        print('--- END ---')

if __name__ == '__main__':
    main()


('192.168.10.1',8889)と('udp://0.0.0.0:11111')がセットになっている(対になっている)可能性があるか調べました。wi-fiルーターにパソコンとドローンを1台接続し、ドローンは192.168.10.1に割り振られるようにルーター側で設定しました。
この方法もうまくいかず、「streamon OK」で止まりました。


ドローン自体をwi-fiアクセスポイントとしたとき(パソコンとドローンを1対1で接続)、
('192.168.10.1',8889)('udp://0.0.0.0:11111')
('192.168.10.1',8889)('udp://127.0.0.1:11111')
('192.168.10.1',8889)('udp://192.168.10.2:11111')
のように映像受信時のIPアドレスを変えても、映像を受信できました。


wi-fiルーターではなく、ドローンをwi-fiのアクセスポイントとしたとき、このアクセスポイントに他のドローンも接続できるか試しましたができませんでした。

該当のソースコード

import socket 
import threading
import time
import cv2
'''
from formationflight.multi_1.tello1 import Tello1
from formationflight.multi_1.tello2 import Tello2
from formationflight.multi_1.tello3 import Tello3

tello1 = Tello1()
tello2 = Tello2()
tello3 = Tello3()
'''

event = threading.Event()

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
sock.bind(('',9000))

tello1_address = ('192.168.100.112',8889)
tello2_address = ('192.168.100.113',8889)
tello3_address = ('192.168.100.114',8889)

def send_command1(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello1_address)
    event.wait()
    event.clear()

def send_command2(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello2_address)
    event.wait()
    event.clear()

def send_command3(msg):
    print(msg)
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello3_address)
    event.wait()
    event.clear()

def send_command(msg):
    print(msg,msg,msg, sep='\n', end='\n')
    msg = msg.encode(encoding="utf-8")
    sock.sendto(msg,tello1_address)
    sock.sendto(msg,tello2_address)
    sock.sendto(msg,tello3_address)
    event.wait()
    event.clear()

def send_command12(msg1,msg2):
    print(msg1,msg2, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg2 = msg2.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg2,tello2_address)
    event.wait()
    event.clear()

def send_command23(msg2,msg3):
    print(msg2,msg3, sep='\n', end='\n')
    msg2 = msg2.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg2,tello2_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def send_command13(msg1,msg3):
    print(msg1,msg3, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def send_command123(msg1,msg2,msg3):
    print(msg1,msg2,msg3, sep='\n', end='\n')
    msg1 = msg1.encode(encoding="utf-8")
    msg2 = msg2.encode(encoding="utf-8")
    msg3 = msg3.encode(encoding="utf-8")
    sock.sendto(msg1,tello1_address)
    sock.sendto(msg2,tello2_address)
    sock.sendto(msg3,tello3_address)
    event.wait()
    event.clear()

def recv():
    while True:
        try:
            data,server = sock.recvfrom(1518)
            print(data.decode(encoding="utf-8"))
            event.set()
        except Exception:
            break
recvThread = threading.Thread(target=recv)
recvThread.start()


def main():
    try:
        send_command('command')
        send_command1('streamon')

        cap1 = cv2.VideoCapture('udp://0.0.0.0:11111')
        #cap2 = cv2.VideoCapture('udp://0.0.0.0:11111')
        #cap3 = cv2.VideoCapture('udp://0.0.0.0:11111')

        while True:
            ret,frame1 = cap1.read()
            cv2.imshow('TelloEDU',frame1)

            key = cv2.waitKey(1)
            if key == 27:
                break


    except Exception as ex:
        print(ex)
    finally:
        cap.release()
        cv2.destroyAllWindows()
        send_command('streamoff')
        sock.close()
        print('--- END ---')

if __name__ == '__main__':
    main()

補足情報(FW/ツールのバージョンなど)

python 3.7.6

1

2Answer

お世話になります。
昔、自分も同じようなことをやろうとしたことがあります。
公式に明言されているか分からなかったですが、
その時はルーターのAPにTello eduを接続する形式だと動画を流してくれず
結局、諦めた記憶があります。
※カメラストリームはAPモードでのみ受信でき、ステーションモードでは受信できないみたいです。

参考URL:
Tello Edu SDK
Station mode and sockets

2Like

Comments

  1. @tiffany1098

    Questioner

    回答ありがとうございます。

    やはりそうなのですね……
    丁度今日tello公式SDKに問い合わせてみたところ、「telloEduは編隊飛行の時の映像取得はできない。」と返信がありました。

    とても参考になるURLをありがとうございます。
    URL内に「複数のRaspberry PiコンピュータとUDPパケット転送を使用すると映像取得ができる」?のような記述があるのですが、mototoke様はラズベリーパイは使用しましたか?
  2. 私が使用したのは普通のPCです。
    ラズベリーパイに限らずNICを二枚あるいは複数枚もつPCを
    複数用意し、それぞれ毎にTello eduのAPと通信、動画やコマンドを別のPCに送受信(転送)という風にしてあげれば編隊飛行かつ複数の映像取得はできるかもしれないですね。

    @tiffany1098さんの言われいてるURL内の人は
    ラズベリーパイ二つ(無線LAN・・・tello edu, 有線LAN・・・ホストPC)
    ホストPC(有線LAN・・・ラズベリーパイ二つ)
    スイッチングハブ
    という構成みたいです。
    [udp_forward](https://github.com/clydemcqueen/udp_forward)

    ラズベリーパイは無線wifiでtello eduと通信、有線lanでホストPCと通信しながら
    ラズベリーパイが受け取った動画をホストPC側に転送しているみたいですね。

  3. @tiffany1098

    Questioner

    返信ありがとうございます。
    なるほど、色々な方法があるのですね。
    試してみようと思います。
    有力な情報をありがとうございました。

Comments

  1. @tiffany1098

    Questioner

    ご回答ありがとうございます。
    送ってくださったURLの方法では、パソコンに直接つなぐタイプのカメラにしか使えないみたいです。telloの場合は通信で映像を取得しますので、この方法は使えませんでした。URLを送っていただきありがとうございました。

    マルチポストについて調べました。このような質問サイトでは、同じ内容を複数のサイトで書くことはマナー違反とされているようですね。質問サイトを最近使い始めたのですが、マナーや使い方について確認不足でした。申し訳ありません。
    こちらのサイトと、もう一方のサイトにこの質問はマルチポストであることを明記しようと思います。また、分かったことがあったらどちらのサイトも更新します。
    マルチポストを知らせて頂きありがとうございました。

Your answer might help someone💌