Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法
Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法

Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法

いよいよ面白くなりTelloを飛ばすことはできましたが、本来の機能であるカメラ画像の送信が生かされていない( ;∀;)

まだお持ちでない方、2台目希望の方 プログラミングドローン ”Tello” の購入はこちら

ドローンのプログラミングでどうやって画像をPCに送信したらいいだろう? YouTubeをたまたまみていたTechの部屋に出会いました。しかし私はプログラミング初心者でしかもTechの部屋のPCはMAC かなり勉強になりましたが、私はPCはWindowsでしたので、参考にして我流でしてみました。オープンソースGitHub Tech の部屋「Pythonで小型ドローン Tello EDU を飛ばす!」サンプルプログラムからダウンロードできます。

まずOpenCvのダウンロードからです。


OpenCV(正式名称: Open Source Computer Vision Libraryとはオープンソースコンピューター・ビジョン・ライブラリです。コンピューターで画像や動画を処理するのに必要な、さまざま機能が実装されており、BSDライセンスで配布されていることから学術用途だけでなく商用目的でも利用できます。加えて、マルチプラットフォーム対応されているため、幅広い場面で利用されていることが特徴です。

画像処理 (Image Processing)構造解析 (Structural Analysis)モーション解析と物体追跡 (Motion Analysis and Object Tracking)物体検出 (Object Detection)などが可能です。

OpenCvのダウンロード

リリースのウインドウズをクリックしダウンロードします。ダウンロードしたら展開してopencvのフォルダーをCドライブの直下に置きます。OPENCVのファイルの中のbild→x64→vc15→binを開きこのアドレスをコピーし、コントロールパネルから

システム&セキュリティのシステム環境変数に追加するため、Pathに先ほどコピーしたアドレスを追加します。

pythonでopencvを使いたいので opencv-pythonpipを使用してインストールできます。

インストールする場合は以下のコマンドを使用します。

pip install opencv-python

コマンドプロンプトからですがアプリの右クリックで管理者として実行で起動させます。

オープンソースGitHub Tech の部屋「Pythonで小型ドローン Tello EDU を飛ばす!」サンプルプログラムからダウンロードしたファイル cli.py(先のTello3.pyと同じプログラム) & vc.py(画像送信プログラム)

OpenCVが使われている箇所を強調してみました。

#vc.pyの内容は以下の通りです。

Tello Python3 Control Demo

http://www.ryzerobotics.com/

1/1/2018

Modified by MPS

#

import threading
import socket
import time
import cv2                  ⇒ ここでOpenCVがインポートされている

host = ”
port = 9000
locaddr = (host, port)

Create a UDP socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

tello_address = (‘192.168.10.1’, 8889)

sock.bind(locaddr)

def recv():
while True:
try:
data, server = sock.recvfrom(1518)
print(data.decode(encoding=”utf-8″))
except Exception:
print(‘\nExit . . . RECV\n’)
break

print(‘\r\n\r\nTello Python3 Demo.\r\n’)

print(‘Tello: command takeoff land flip forward back left right \r\n up down cw ccw speed speed?\r\n’)

print(‘end — quit demo.\r\n’)

recvThread create

recvThread = threading.Thread(target=recv)
recvThread.start()

sock.sendto(b’command’, tello_address)
print(‘command ok’)
time.sleep(0.5)
sock.sendto(b’streamon’, tello_address)
print(‘stream on’)
time.sleep(1)
sock.close()

cap = cv2.VideoCapture(“udp://%s:%s?overrun_nonfatal=1&fifo_size=50000000” % (‘192.168.10.1’, ‘11111’))       #ここでビデをキャプチャーでTelloのカメラから画像受信

print(‘start cap’)
while True:
try:
ret, frame = cap.read()
if ret:
cv2.imshow(‘tello’, cv2.resize(frame, (360, 240)))     #受信画像を画面表示
cv2.waitKey(1)
except KeyboardInterrupt:
cv2.destroyAllWindows()
cap.release()
print(‘\nExit . . .\n’)
break


をデスクトップでも貼り付けます。

あとは 各ファイルを実行できるようにコマンドプロンプトを2つ起動させます。

それでは、準備が整いましたので 2つのプログラムを同時に実行させてみます。動画をとってますので御覧ください。

次にOpenCvによる顔認識の仕方こちらへどうぞ!!

PCでOpenCVでと顔認識して遊んででみる。

トレンドアイテム60万点以上 | ファッションブランド【SHEIN】

マッキーの趣味のドローン YouTube channel

  • ArUcoマーカーの検出を使った、Telloのプログラミングゲーム

    ArUcoマーカーの検出を使った、Telloのプログラミングゲーム

    OpenCVのArUcoマーカーの検出で構築。AR(拡張現実)(Augmented Reality)とは、現実…

    ⬛⬛⬛⬛⬛⬛⬛⬛
    ⬛⬜⬜⬜⬜⬜⬜⬛
    ⬛⬜⬛⬜⬛⬛⬜⬛
    ⬛⬛⬜⬜⬛⬛⬜⬛
    ⬛⬜⬜⬜⬜⬜⬛⬛
    ⬛⬛⬜⬛⬛⬜⬜⬛
    ⬛⬜⬛⬜⬜⬜⬜⬛
    ⬛⬛⬛⬛⬛⬛⬛⬛

    IDは「白黒のパターン」で表現されている!

    ⬜ ⬛ ⬜ ⬛ ⬛
    ⬜ ⬜ ⬛ ⬛ ⬜
    ⬜ ⬜ ⬜ ⬜ ⬛
    ⬜ ⬛ ⬛ ⬜ ⬜
    ⬛ ⬜ ⬜ ⬜ ⬜

    import cv2
    import cv2.aruco as aruco
    
    # マーカー辞書(4x4の50種類のうちの1つ
    aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
    
    # ID=1 のマーカーを 200x200ピクセルで生成
    marker_image = aruco.drawMarker(aruco_dict, id=1, sidePixels=200)
    
    # 画像として保存
    cv2.imwrite("aruco_id1.png", marker_image)
    
    # 表示任意
    cv2.imshow("AR Marker - ID 1", marker_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    import cv2
    import cv2.aruco as aruco
    import numpy as np
    import socket, threading, time
    import pygame
    
    aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
    parameters = aruco.DetectorParameters()
    
    host = '0.0.0.0'
    port = 8889
    tello_addr = ('192.168.10.1', 8889)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((host, port))
    
    def recv():
        while True:
            try:
                data, _ = sock.recvfrom(1518)
                print(data.decode('utf-8'))
            except:
                break
    
    threading.Thread(target=recv, daemon=True).start()
    
    def send(cmd, delay=1):
        sock.sendto(cmd.encode(), tello_addr)
        print(f">>> {cmd}")
        time.sleep(delay)
    
    send('command')
    send('streamon')
    cap = cv2.VideoCapture("udp://192.168.10.1:11111")
    
    pygame.mixer.init()
    point_sound = pygame.mixer.Sound("point.wav")
    
    def play_point_sound():
        point_sound.stop()
        point_sound.play()
    
    def draw_star(img, center, size, color, thickness):
    score = 0
    passed = set()
    MARKERS = {
        0: 'star_floor', ...
    }
    DESIRED_HEIGHT_CM = 90
    
    while True:
        ret, frame = cap.read()
        if not ret:
            continue
    
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        corners, ids, _ = aruco.detectMarkers(gray, aruco_dict, parameters=parameters)
    
        frame_center = (frame.shape[1] // 2, frame.shape[0] // 2)
    

    if ids is not None:
            for idx, i in enumerate(ids.flatten()):
                c = corners[idx][0]
                cx, cy = int(c[:, 0].mean()), int(c[:, 1].mean())
                size_px = np.linalg.norm(c[0] - c[2])
                cm_per_px = size_px / 21.0
                shape = MARKERS.get(i, 'unknown')
                size = int(25 * cm_per_px)
    
                offset_y = int(-DESIRED_HEIGHT_CM * cm_per_px)
                cy_offset = cy + offset_y
                cy_offset = max(0, min(frame.shape[0] - 1, cy_offset))
                center_pos = (cx, cy_offset)
    
    if ids is not None:
    
        for idx, i in enumerate(ids.flatten()):
    
            c = corners[idx][0]
    
            cx, cy = int(c[:, 0].mean()), int(c[:, 1].mean())
    
            size_px = np.linalg.norm(c[0] - c[2])
    
            cm_per_px = size_px / 21.0
    
            shape = MARKERS.get(i, 'unknown')
    
            size = int(25 * cm_per_px)
    
            offset_y = int(-DESIRED_HEIGHT_CM * cm_per_px)
    
            cy_offset = cy + offset_y
    
            cy_offset = max(0, min(frame.shape[0] - 1, cy_offset))
    
            center_pos = (cx, cy_offset)
    

    最終的に図形を描画する中心位置を (x, y) のタプルで保存します。

                cx, cy = int(c[:, 0].mean()), int(c[:, 1].mean())
    
                size_px = np.linalg.norm(c[0] - c[2])
                cm_per_px = size_px / 21.0
    
                offset_y = int(-DESIRED_HEIGHT_CM * cm_per_px)
    
      if shape == 'star_floor' or shape == 'star_floor2':
                    draw_star(frame, center_pos, size, (0, 255, 255), thickness=10)
                elif shape == 'pentagon' or shape == 'pentagon2':
                    draw_polygon(frame, center_pos, size, 5, (255, 0, 0), thickness=10)
                elif shape == 'circle':
                    draw_circle(frame, center_pos, size, (0, 0, 255), thickness=10)
                elif shape == 'triangle':
                    draw_polygon(frame, center_pos, size, 3, (0, 255, 0), thickness=10)
                elif shape == 'star_vertical':
                    draw_star(frame, center_pos, size, (0, 165, 255), thickness=10)
                elif shape == 'pyramid':
                    draw_pyramid(frame, center_pos, size)
                elif shape == 'cube_transparent':
                    draw_transparent_cube(frame, center_pos, size)
                elif shape == 'shadow_square':
                    draw_shadowed_square(frame, center_pos, size)
    
                distance = np.hypot(cx - frame_center[0], cy - frame_center[1])
    
                if distance < 100 and marker_key not in passed:
                    score += 10
                    passed.add(marker_key)
                    play_point_sound()
    
                if marker_key in passed:
                    cv2.putText(frame, "X", ...)
    
        cv2.putText(frame, f"Score: {score}", ...)
        cv2.imshow("Tello AR 7Shapes", frame)
        if cv2.waitKey(1) == 27:
            break
    
    def draw_star(img, center, size, color, thickness):
        pts = []
        cx, cy = center
        for i in range(5):
            angle = np.pi / 2 + i * 2 * np.pi / 5
            pts.append((int(cx + size * np.cos(angle)), int(cy - size * np.sin(angle))))
        cv2.polylines(img, [np.array(pts)], True, color, thickness)
    
    def draw_polygon(img, center, size, sides, color, thickness):
        pts = []
        cx, cy = center
        for i in range(sides):
            angle = np.pi / 2 + i * 2 * np.pi / sides
            pts.append((int(cx + size * np.cos(angle)), int(cy - size * np.sin(angle))))
        cv2.polylines(img, [np.array(pts)], True, color, thickness)
    
    def draw_circle(img, center, size, color, thickness):
        cv2.circle(img, center, int(size), color, thickness)
    
    def draw_shadowed_square(img, center, size, square_color=(0, 128, 255), shadow_color=(50, 50, 50), thickness=4):
        cx, cy = center
        d = size // 2
        offset = size // 5
    
        # 
        shadow_pts = np.array([
            (cx - d + offset, cy - d + offset),
            (cx + d + offset, cy - d + offset),
            (cx + d + offset, cy + d + offset),
            (cx - d + offset, cy + d + offset)
        ])
        cv2.fillPoly(img, [shadow_pts], shadow_color)
    
        # 本体
        square_pts = np.array([
            (cx - d, cy - d),
            (cx + d, cy - d),
            (cx + d, cy + d),
            (cx - d, cy + d)
        ])
        cv2.fillPoly(img, [square_pts], square_color)
    
    import cv2
    import cv2.aruco as aruco
    import numpy as np
    import socket, threading, time
    import pygame
    
    # --- ArUco 設定 ---
    aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
    parameters = aruco.DetectorParameters()
    
    # --- 通信設定 ---
    host = '0.0.0.0'
    port = 8889
    tello_addr = ('192.168.10.1', 8889)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((host, port))
    
    def recv():
        while True:
            try:
                data, _ = sock.recvfrom(1518)
                print(data.decode('utf-8'))
            except:
                break
    
    threading.Thread(target=recv, daemon=True).start()
    
    def send(cmd, delay=1):
        sock.sendto(cmd.encode(), tello_addr)
        print(f">>> {cmd}")
        time.sleep(delay)
    
    # --- Tello 飛行映像 ---
    send('command')
    send('streamon')
    cap = cv2.VideoCapture("udp://192.168.10.1:11111")
    
    # --- 効果音の初期化 ---
    pygame.mixer.init()
    point_sound = pygame.mixer.Sound("point.wav")
    
    def play_point_sound():
        point_sound.stop()
        point_sound.play()
    
    # --- 図形描画関数 ---
    def draw_star(img, center, size, color, thickness):
        pts = []
        cx, cy = center
        for i in range(5):
            angle = np.pi / 2 + i * 2 * np.pi / 5
            pts.append((int(cx + size * np.cos(angle)), int(cy - size * np.sin(angle))))
        cv2.polylines(img, [np.array(pts)], True, color, thickness)
    
    def draw_polygon(img, center, size, sides, color, thickness):
        pts = []
        cx, cy = center
        for i in range(sides):
            angle = np.pi / 2 + i * 2 * np.pi / sides
            pts.append((int(cx + size * np.cos(angle)), int(cy - size * np.sin(angle))))
        cv2.polylines(img, [np.array(pts)], True, color, thickness)
    
    def draw_circle(img, center, size, color, thickness):
        cv2.circle(img, center, int(size), color, thickness)
        
    def draw_pyramid(img, center, size, color=(0, 200, 255), thickness=2):
        cx, cy = center
        d = size // 2
    
        # 底面三角
        base = np.array([
            (cx - d, cy + d),
            (cx + d, cy + d),
            (cx, cy - d)
        ])
    
        # 頂点
        apex = (cx, cy - int(size * 1.5))
    
        # 三角面を線で描く
        for pt in base:
            cv2.line(img, apex, pt, color, thickness)
        cv2.polylines(img, [base], isClosed=True, color=color, thickness=thickness)
        
        
    def draw_transparent_cube(img, center, size, color=(200, 255, 200), thickness=2):
        cx, cy = center
        d = size // 2
        offset = size // 3
    
        front = np.array([
            (cx - d, cy - d),
            (cx + d, cy - d),
            (cx + d, cy + d),
            (cx - d, cy + d)
        ])
        back = np.array([
            (cx - d + offset, cy - d - offset),
            (cx + d + offset, cy - d - offset),
            (cx + d + offset, cy + d - offset),
            (cx - d + offset, cy + d - offset)
        ])
    
        for i in range(4):
            cv2.line(img, front[i], front[(i+1)%4], color, thickness)
            cv2.line(img, back[i], back[(i+1)%4], color, thickness)
            cv2.line(img, front[i], back[i], color, thickness)
    
    def draw_shadowed_square(img, center, size, square_color=(0, 128, 255), shadow_color=(50, 50, 50), thickness=4):
        cx, cy = center
        d = size // 2
        offset = size // 5
    
        # 影を描画
        shadow_pts = np.array([
            (cx - d + offset, cy - d + offset),
            (cx + d + offset, cy - d + offset),
            (cx + d + offset, cy + d + offset),
            (cx - d + offset, cy + d + offset)
        ])
        cv2.fillPoly(img, [shadow_pts], shadow_color)
    
        # 正方形本体
        square_pts = np.array([
            (cx - d, cy - d),
            (cx + d, cy - d),
            (cx + d, cy + d),
            (cx - d, cy + d)
        ])
        cv2.fillPoly(img, [square_pts], square_color)
    
       
        
        
        
    
    # --- メインループ変数 ---
    score = 0
    passed = set()
    MARKERS = {
        0: 'star_floor',
        1: 'pentagon',
        2: 'circle',
        3: 'triangle',
        4: 'star_vertical',
        5: 'star_floor2',
        6: 'pentagon2',
        7: 'pyramid',
        8: 'cube_transparent',
        9: 'shadow_square'
    }
    
    DESIRED_HEIGHT_CM = 90
    
    # --- メインループ ---
    while True:
        ret, frame = cap.read()
        if not ret:
            continue
    
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        corners, ids, _ = aruco.detectMarkers(gray, aruco_dict, parameters=parameters)
    
        frame_center = (frame.shape[1] // 2, frame.shape[0] // 2)
        cv2.circle(frame, frame_center, 10, (0, 255, 0), 2)
    
        if ids is not None:
            for idx, i in enumerate(ids.flatten()):
                c = corners[idx][0]
                cx, cy = int(c[:, 0].mean()), int(c[:, 1].mean())
                size_px = np.linalg.norm(c[0] - c[2])
                cm_per_px = size_px / 21.0
                shape = MARKERS.get(i, 'unknown')
                size = int(25 * cm_per_px)
    
                offset_y = int(-DESIRED_HEIGHT_CM * cm_per_px)
                cy_offset = cy + offset_y
                cy_offset = max(0, min(frame.shape[0] - 1, cy_offset))
                center_pos = (cx, cy_offset)
    
                # 図形描画
                if shape == 'star_floor' or shape == 'star_floor2':
                    draw_star(frame, center_pos, size, (0, 255, 255), thickness=10)
                elif shape == 'pentagon' or shape == 'pentagon2':
                    draw_polygon(frame, center_pos, size, 5, (255, 0, 0), thickness=10)
                elif shape == 'circle':
                    draw_circle(frame, center_pos, size, (0, 0, 255), thickness=10)
                elif shape == 'triangle':
                    draw_polygon(frame, center_pos, size, 3, (0, 255, 0), thickness=10)
                elif shape == 'star_vertical':
                    draw_star(frame, center_pos, size, (0, 165, 255), thickness=10)
                elif shape == 'pyramid':
                    draw_pyramid(frame, center_pos, size)
                elif shape == 'cube_transparent':
                    draw_transparent_cube(frame, center_pos, size)
                elif shape == 'shadow_square':
                    draw_shadowed_square(frame, center_pos, size)
       
                    
                    
                    
    
                aruco.drawDetectedMarkers(frame, corners)
                cv2.putText(frame, shape, (cx + 10, cy + 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
    
                # --- 接触判定とX表示 ---
                distance = np.hypot(cx - frame_center[0], cy - frame_center[1])
                marker_key = (i, cx, cy)
    
                if distance < 100 and marker_key not in passed:
                    score += 10
                    passed.add(marker_key)
                    play_point_sound()
                    print(f"Hit marker {i}! +10 points. Score: {score}")
    
                # 通過済みマーカーには常にXを表示
                if marker_key in passed:
                    cv2.putText(frame, "X", (center_pos[0] - 20, center_pos[1] + 20),
                                cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 0, 255), 5)
    
        # スコア表示
        cv2.putText(frame, f"Score: {score}", (30, 50),
                    cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)
    
        cv2.imshow("Tello AR 7Shapes", frame)
        if cv2.waitKey(1) == 27:
            break
    
    # --- 終了処理 ---
    send('land')
    cap.release()
    cv2.destroyAllWindows()
    ホーム » プログラミング » Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法

  • ソケット通信について・Telloとパソコンの交信を見てみよう!

    ソケット通信について・Telloとパソコンの交信を見てみよう!

    1.パソコン側の動き      sock = socket.socket(socket.AF_INET, so…

    PCからドローンを操作するsocket通信プログラムを少しだけ解説してみました。

    import threading 
    import socket
    import sys
    import time
    
    
    host = ''
    port = 9000
    locaddr = (host,port) 
    
    
    # Create a UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    tello_address = ('192.168.10.1', 8889)
    
    sock.bind(locaddr)
    
    def recv():
        count = 0
        while True: 
            try:
                data, server = sock.recvfrom(1518)
                print(data.decode(encoding="utf-8"))
            except Exception:
                print ('\nExit . . .\n')
                break

    「socket通信」って何?
    コンピュータ同士(ここではPC&Tello)がネットワークを通じてデータをやり取りするための方法。(コンピュータ間でデータを送受信するための仕組み)
    「手紙を出す」みたいなイメージで、
    送信先の住所(IP)と郵便受け(ポート)を指定して、
    データ(コマンド)を送る感じです。

    それでは、学習をしていこう!!
           まずは、大まかな流れ!!⇒それから深堀学習していきたいと思います。

    31
    # Create a UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    IPv4とは:IPインターネットプロトコルでインターネットに接続されたコンピュータ同士がデーターをやり取りするためにデーター通信の方法を定めた規約のことで、インターネットに接続されたコンピューターを識別するためのIPアドレスが割り当てられIPv4はこのIPアドレスを32ビットのデーターとして表現するものです。

    while True: 
            try:
                data, server = sock.recvfrom(1518)
                print(data.decode(encoding="utf-8"))

    もう少し詳しくすると。。。。

    IP アドレス & ポート番号の確認は以下の Tello SDK マニュアルで確認します

    tello_address = (‘192.168.10.1’, 8889)

    ocerviwe

    Tello SDKのマニュアルをダウンロードします。

    https://www.ryzerobotics.com/jp/tello/downloads

    tello dwn

    深堀学習!!

    ソケット通信一連のパソコンの内部の動きを見てよう

    階層

    少し、トランスポート層(UDP)UDPヘッダが付加 と
    インターネット層(IP)IPヘッダが追加を深堀してみます。

    UDPヘッダ/IPヘッダの追加、Wi-Fiデーターはどうなってるの?

    ここまでいろいろと調べてまとめてみて、プログラミングで指示を出し、Tello(ドローン)が飛ぶ仕組みを自分自身も深堀学習できたと思います。

    ホーム » プログラミング » Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法

  • Tello ピンクのボールを追尾する(ピンク色のボール検出(OpenCV HSV)を使い追尾)

    Tello ピンクのボールを追尾する(ピンク色のボール検出(OpenCV HSV)を使い追尾)

    ピンク色のボール検出(OpenCV HSV)を使い追尾をプログラミングしてみました。Telloのカメラは解像度…

    顔検出→ OpenCVでピンク色の検出に変更
    音声出力pyttsx3 を使用して**「ピンクのボール発見!」**をしゃべらせる
    追尾制御→ 既存のPID制御をそのまま流用(中心点と面積)

    pyttsx3 2.98プロジェクトの説明

    import pyttsx3  # 音声合成ライブラリを読み込み
    
    engine = pyttsx3.init()  # 音声エンジンを初期化
    
    engine.say("こんにちは、Telloが追尾します")  # 読み上げたいテキスト
    engine.runAndWait()  # 実際に再生
    
    import pyttsx3
    音声エンジン初期化
    engine = pyttsx3.init()
    ball_detected = False  # ボールが見つかったかどうかのフラグ
    def findBall(img):
        global ball_detected
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
        # ピンク色のHSV範囲明るさ環境によって調整
        lower_pink = np.array([140, 100, 100])
        upper_pink = np.array([170, 255, 255])
        mask = cv2.inRange(hsv, lower_pink, upper_pink)
    
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
        BallList_center = []
        BallList_area = []
        ball_center_y = 0
    
        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > 500:  # ノイズ除去
                x, y, w, h = cv2.boundingRect(cnt)
                cx = x + w // 2
                cy = y + h // 2
                BallList_center.append([cx, cy])
                BallList_area.append(area)
                cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 255), 2)
                cv2.circle(img, (cx, cy), 10, (0, 0, 255), -1)
                cv2.putText(img, f"ballcenter: ({cx}, {cy})", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (204, 0, 255), 1)
    
        if len(BallList_area) != 0:
            i = BallList_area.index(max(BallList_area))
            if not ball_detected:
                engine.say("ピンクのボール発見!追尾します")
                engine.runAndWait()
                ball_detected = True
            return img, [BallList_center[i], BallList_area[i]]
        else:
            ball_detected = False
            return img, [[0, 0], 0]
    

    import threading
    import socket
    import time
    import cv2
    import numpy as np
    import pyttsx3
    
    # 音声再生非同期
    def speak_async(text):
        def _speak():
            engine = pyttsx3.init()
            engine.say(text)
            engine.runAndWait()
        threading.Thread(target=_speak, daemon=True).start()
    
    # Telloネットワーク設定
    host = '0.0.0.0'
    port = 8889
    locaddr = (host, port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    tello_address = ('192.168.10.1', 8889)
    sock.bind(locaddr)
    
    # Telloからの応答を受信
    def recv():
        while True:
            try:
                data, _ = sock.recvfrom(1518)
                print(data.decode(encoding="utf-8"))
            except Exception:
                print('\nExit . . . RECV\n')
                break
    
    recvThread = threading.Thread(target=recv)
    recvThread.daemon = True
    recvThread.start()
    
    # 初期化コマンド
    sock.sendto(b'command', tello_address)
    time.sleep(0.5)
    sock.sendto(b'streamon', tello_address)
    time.sleep(1)
    sock.sendto(b'takeoff', tello_address)
    time.sleep(2)
    
    # カメラ映像取得
    addr = 'udp://192.168.10.1:11111'
    cap = cv2.VideoCapture(addr)
    print('start cap')
    
    # PID制御パラメータ
    pid = [0.4, 0.4, 0]
    pError = 0
    w, h = 480, 360
    
    # RCコマンド初期値
    a = b = c = d = 0
    
    ball_detected = False  # 音声を一度だけ再生するためのフラグ
    
    # ピンクボール検出関数
    def findBall(img):
        global ball_detected
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
        lower_pink = np.array([140, 100, 100])
        upper_pink = np.array([170, 255, 255])
        mask = cv2.inRange(hsv, lower_pink, upper_pink)
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
        BallList_center = []
        BallList_area = []
    
        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > 500:
                x, y, w_box, h_box = cv2.boundingRect(cnt)
                cx = x + w_box // 2
                cy = y + h_box // 2
                BallList_center.append([cx, cy])
                BallList_area.append(area)
                cv2.rectangle(img, (x, y), (x + w_box, y + h_box), (255, 0, 255), 2)
                cv2.circle(img, (cx, cy), 10, (0, 0, 255), -1)
    
        if len(BallList_area) != 0:
            i = BallList_area.index(max(BallList_area))
            if not ball_detected:
                speak_async("ピンクのボール発見!追尾します")
                ball_detected = True
            return img, [BallList_center[i], BallList_area[i]]
        else:
            ball_detected = False
            return img, [[0, 0], 0]
    
    # 左右前後制御PID
    def TrackBall(info, w, pid, pError):
        global b, d
        area = info[1]
        x, y = info[0]
        b = 0
    
        error = x - w / 2
        d = pid[0] * error + pid[1] * (error - pError)
        d = int(np.clip(d, -100, 100))
    
        if 6200 < area < 6800:
            b = 0
        elif area > 6800:
            b = -20
        elif area < 6200 and area != 0:
            b = 20
        if x == 0:
            d = 0
            error = 0
    
        return error
    
    # 上下制御オプション
    def throttle(y):
        global c
        c = 0
        if 50 < y < 110:
            c = 20
        elif 280 > y > 190:
            c = -20
    
    # メインループ
    while True:
        for i in range(5):  # フレームスキップで安定化
            ret, frame = cap.read()
        if frame is None or frame.size == 0:
            continue
    
        frame = cv2.resize(frame, (w, h))
        img, info = findBall(frame)
        pError = TrackBall(info, w, pid, pError)
        throttle(info[0][1])
    
        rc_command = f"rc {a} {b} {c} {d}"
        sock.sendto(rc_command.encode('utf-8'), tello_address)
        print("Sending RC command with values:", a, b, c, d)
    
        # 画面に情報表示
        cv2.putText(img, f'Pitch: {b}  Throttle: {c}  Yaw: {d}', (20, 20),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
        cv2.imshow('Tracking', img)
    
        # ESCキーで終了
        if cv2.waitKey(1) & 0xFF == 27:
            sock.sendto(b'land', tello_address)
            cap.release()
            cv2.destroyAllWindows()
            sock.close()
            break
    
    lower_pink = np.array([140, 100, 100])
    upper_pink = np.array([170, 255, 255])
    
    ホーム » プログラミング » Tello の画像をOpenCVでPCに写してみる!!OpenCVの導入方法

    投稿







コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です