Jetson 20 基于 OpenCV 的颜色追踪

来自Waveshare Wiki
跳转至: 导航搜索

基于 OpenCV 的颜色追踪

在本章教程中我们会在 OpenCV 的相关功能中加入一些控制外设的函数,例如,在本章教程中,摄像头云台会转动,确保你的手或其它易碎物品远离摄像头云台的转动半径。

准备工作

由于产品开机默认会自动运行主程序,主程序会占用摄像头资源,这种情况下是不能使用本教程的,需要结束主程序或禁止主程序自动运行后再重新启动机器人。

这里需要注意的是,由于机器人主程序中使用了多线程且由 crontab 配置开机自动运行,所以常规的 sudo killall python 的方法通常是不起作用的,所以我们这里介绍禁用主程序自动运行的方法。

如果你已经禁用了机器人主程序的开机自动运行,则不需要执行下面的结束主程序章节。

结束主程序

1. 点击上方本页面选项卡旁边的 “+”号,会打开一个新的名为 Launcher 的选项卡。

2. 点击 Other 内的 Terminal,打开终端窗口。

3. 在终端窗口内输入 bash 后按回车。

4. 现在你可以使用 Bash Shell 来控制机器人了。

5. 输入命令: sudo killall -9 python


例程

以下代码块可以直接运行:

1. 选中下面的代码块

2. 按 Shift + Enter 运行代码块

3. 观看实时视频窗口

4. 按 STOP 关闭实时视频,释放摄像头资源

如果运行时不能看到摄像头实时画面

  • 需要点击上方的 Kernel - Shut down all kernels
  • 关闭本章节选项卡,再次打开
  • 点击 STOP 释放摄像头资源后重新运行代码块
  • 重启设备

运行

在本章教程中,摄像头云台会转动,确保你的手或其它易碎物品远离摄像头云台的转动半径。

我们在例程中默认检测蓝色小球,确保画面背景中没有蓝色物体影响颜色识别功能,你也可以通过二次开发来更改检测颜色(HSV色彩空间)。

import matplotlib.pyplot as plt
import cv2
import math
import numpy as np
from IPython.display import display, Image
import ipywidgets as widgets
import threading

# ================================
# 全局变量(必须定义,否则报错)
# ================================
gimbal_x = 0
gimbal_y = 0
track_spd_rate = 0.3
track_acc_rate = 0.2

# 轨迹显示缓冲区(避免报错)
overlay_buffer = np.zeros((480, 640, 3), dtype=np.uint8)

# 停止按钮
# ================================
stopButton = widgets.ToggleButton(
    value=False,
    description='Stop',
    disabled=False,
    button_style='danger',
    tooltip='Description',
    icon='square'
)


# ================================
# 云台跟踪函数
# ================================
def gimbal_track(fx, fy, gx, gy, iterate):
    global gimbal_x, gimbal_y

    distance = math.sqrt((fx - gx) ** 2 + (gy - fy) ** 2)

    # 更新云台角度
    gimbal_x += (gx - fx) * iterate
    gimbal_y += (fy - gy) * iterate

    # 限制范围
    gimbal_x = max(min(gimbal_x, 180), -180)
    gimbal_y = max(min(gimbal_y, 90), -30)

    # 速度、加速度
    gimbal_spd = max(int(distance * track_spd_rate), 1)
    gimbal_acc = max(int(distance * track_acc_rate), 1)

    # 这里没有 base 对象,防止报错,注释掉
    # base.base_json_ctrl({"T":self.CMD_GIMBAL,"X":gimbal_x,"Y":gimbal_y,"SPD":gimbal_spd,"ACC":gimbal_acc})

    return distance


# ================================
# 摄像头显示与颜色跟踪
# ================================
def view(button):
    camera = cv2.VideoCapture(-1)
    camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    display_handle = display(None, display_id=True)

    color_upper = np.array([120, 255, 220])
    color_lower = np.array([90, 120, 90])
    min_radius = 12
    track_color_iterate = 0.023

    while True:
        ok, img = camera.read()
        if not ok:
            continue

        blurred = cv2.GaussianBlur(img, (11, 11), 0)
        hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)

        mask = cv2.inRange(hsv, color_lower, color_upper)
        mask = cv2.erode(mask, None, iterations=5)
        mask = cv2.dilate(mask, None, iterations=5)

        # ================================
        # 不使用 imutils,兼容版本写法
        # ================================
        cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cnts = cnts[0] if len(cnts) == 2 else cnts[1]

        height, width = img.shape[:2]
        center_x, center_y = width // 2, height // 2

        if len(cnts) > 0:
            c = max(cnts, key=cv2.contourArea)
            ((x, y), radius) = cv2.minEnclosingCircle(c)
            M = cv2.moments(c)

            if M["m00"] > 0:
                cx = int(M["m10"] / M["m00"])
                cy = int(M["m01"] / M["m00"])

                if radius > min_radius:
                    distance = gimbal_track(center_x, center_y, cx, cy, track_color_iterate)

                    cv2.circle(img, (int(x), int(y)), int(radius), (128, 255, 255), 2)

        # ================================
        # 显示图像
        # ================================
        _, frame = cv2.imencode('.jpeg', img)
        display_handle.update(Image(data=frame.tobytes()))

        if stopButton.value:
            camera.release()
            display_handle.update(None)
            return


# ================================
# 启动
# ================================
display(stopButton)
thread = threading.Thread(target=view, args=(stopButton,))
thread.start()