opencv色块追踪

学习树莓派的路上,今天学习了用opencv进行色块追踪的实验,熟悉了颜色空间转换、掩膜提取、轮廓提取、轮廓绘制相关函数的使用。

基本流程为:

    [读取视频帧]
    [转换到HSV空间]
    [颜色阈值分割]
    [形态学处理]
    [轮廓检测]
    [绘制矩形框]
    [显示结果]

HSV掩膜提取

关于每种颜色对应的HSV阈值,可以参考下表:

设定阈值后,进行掩膜提取,目标区域为白色。

为了方便设置阈值,可以创建滑块,手动调整阈值以获得最佳效果。

形态学去噪

通过腐蚀(核内只要有一个黑色中心像素即为黑)、膨胀、开(先腐蚀后膨胀)、闭运算将掩膜中的噪点或者空洞去除。

        记住口诀​​:
🔹 ​​“开运算去毛刺,闭运算补漏洞”​
🔹 ​​“先开后闭,天下无敌”​​ (针对复杂掩膜)

轮廓检测

使用函数提取轮廓,返回轮廓列表,遍历列表求其外接矩形,并绘制,即可。

跟踪品红色块代码

import numpy as np
import cv2
import time

capture = cv2.VideoCapture(0)

if not capture.isOpened():
    print("open camera failed")
    exit()

while True:
    ret,frame = capture.read()
    if not ret:
        print("no frame")
        break
    #设定阈值,提取掩膜
    frame_hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
    lower_red = np.array([156,50,50])
    upper_red = np.array([180,255,255])
    mask = cv2.inRange(frame_hsv,lower_red,upper_red)
    # 添加形态学操作
    kernel = np.ones((10, 10), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)  # 去噪
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)  # 填充
    #检测轮廓
    contours,hierarchy = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    for cnt in contours:
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
    cv2.imshow("frame",frame)
    if cv2.waitKey(20) & 0xFF == ord('q'):
        break

capture.release()
cv2.destroyAllWindows()

<think>我们正在设计一个基于树莓派OpenCV和STM32的二维云台色块追踪系统。系统工作流程如下: 1. 树莓派运行OpenCV程序,通过摄像头捕获视频流。 2. OpenCV处理每一帧图像,识别特定颜色的色块,并计算出色块在图像中的位置(通常为中心坐标)。 3. 树莓派根据色块位置与图像中心的位置偏差,计算出云台需要转动的角度(或方向)和速度。 4. 树莓派通过串口(或其它通信方式)将控制指令发送给STM32。 5. STM32作为下位机,接收指令并控制二维云台(通常由两个舵机或步进电机组成)转动,使色块保持在图像中心。 6. 实时追踪,形成闭环。 下面分步骤详细说明: ### 1. 树莓派环境搭建与OpenCV配置 首先,在树莓派上安装OpenCV。推荐使用Python环境,安装OpenCV-Python库。 ```bash pip install opencv-python numpy ``` ### 2. 色块识别 使用OpenCV进行颜色识别,一般步骤: - 将图像从BGR转换到HSV颜色空间,便于颜色分割。 - 设定目标颜色的HSV范围,创建掩膜(mask)。 - 对掩膜进行形态学操作(腐蚀、膨胀)以去除噪声。 - 查找掩膜中的轮廓,并筛选出符合条件的色块(如面积足够大)。 示例代码(Python): ```python import cv2 import numpy as np # 设置HSV颜色范围,这里以蓝色为例 lower_blue = np.array([100, 50, 50]) upper_blue = np.array([130, 255, 255]) cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, lower_blue, upper_blue) mask = cv2.erode(mask, None, iterations=2) mask = cv2.dilate(mask, None, iterations=2) contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: # 找到最大轮廓 c = max(contours, key=cv2.contourArea) ((x, y), radius) = cv2.minEnclosingCircle(c) if radius > 10: # 在图像上标出色块 cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2) # 计算偏差:图像中心与色块中心的偏差 center_x, center_y = frame.shape[1]//2, frame.shape[0]//2 error_x = x - center_x error_y = y - center_y # 这里可以将error_x, error_y通过串口发送给STM32 # 注意:需要将偏差转换为云台转动的角度或控制量 cv2.imshow('Frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` ### 3. 树莓派与STM32通信 通常使用串口通信(UART)。树莓派通过USB转TTL模块连接STM32的串口。 在树莓派上,可以使用Python的`pyserial`库。 安装: ```bash pip install pyserial ``` 示例代码(发送部分): ```python import serial import time # 初始化串口,端口号根据实际情况修改(如/dev/ttyUSB0或/dev/ttyAMA0),波特率常用115200 ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) # 在色块识别的循环中,当计算出error_x, error_y后,发送给下位机 # 定义通信协议:例如,发送两个整数(水平偏差和垂直偏差),用逗号分隔,以换行符结束 # 例如: "123,-45\n" if len(contours) > 0: # ... 计算error_x, error_y data = f"{int(error_x)},{int(error_y)}\n" ser.write(data.encode()) ``` ### 4. STM32控制二维云台 STM32接收来自串口的数据,解析出水平方向和垂直方向的偏差,然后控制两个舵机(或步进电机)转动。 - 通常使用PID控制器将偏差转换为舵机的转动角度(或PWM占空比),实现平滑追踪。 - 简单情况下,也可以使用比例控制:舵机角度 = 当前角度 + Kp * 偏差。 示例代码(STM32,使用HAL库,串口接收部分): ```c char buffer[50]; int16_t pan_error, tilt_error; // 水平和垂直偏差 // 在串口中断回调函数中解析数据 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { // 假设使用USART2 // 解析接收到的数据,例如"123,-45" if (sscanf(buffer, "%d,%d", &pan_error, &tilt_error) == 2) { // 更新舵机目标位置 update_servo_position(pan_error, tilt_error); } // 重新启动接收 HAL_UART_Receive_IT(&huart2, (uint8_t*)buffer, sizeof(buffer), HAL_MAX_DELAY); } } // 舵机控制函数 void update_servo_position(int16_t pan_error, int16_t tilt_error) { // 假设当前舵机角度存储在pan_angle, tilt_angle // 比例系数,根据实际调整 float Kp = 0.1; pan_angle += Kp * pan_error; tilt_angle += Kp * tilt_error; // 限制角度范围 pan_angle = (pan_angle < 0) ? 0 : (pan_angle > 180) ? 180 : pan_angle; tilt_angle = (tilt_angle < 0) ? 0 : (tilt_angle > 180) ? 180 : tilt_angle; // 设置舵机PWM set_servo_pwm(SERVO_PAN_CHANNEL, pan_angle); set_servo_pwm(SERVO_TILT_CHANNEL, tilt_angle); } ``` ### 5. 系统调试 - 分别调试树莓派色块识别和串口发送。 - 调试STM32的串口接收和云台控制。 - 联调时,注意调整比例系数Kp,避免云台抖动。 ### 注意事项 - 通信协议:定义简单且明确的协议,确保数据正确解析。 - 实时性:树莓派处理图像的速度可能受限,可调整图像分辨率(如640x480)以提高帧率。 - 稳定性:加入滤波算法(如对色块位置进行移动平均)使追踪更平滑。 - 电源:确保云台电机有独立供电,避免电流过大影响控制板。 通过以上步骤,即可实现二维云台的色块追踪功能。 §§相关问题§§ 1. 如何优化OpenCV色块识别速度以提高系统实时性? 2. 在STM32控制云台时,如何设计PID控制器来改善追踪性能? 3. 树莓派与STM32之间除了串口通信,还有哪些通信方式可选?各有何优缺点?</think>基于树莓派OpenCV和STM32实现二维云台色块追踪的系统设计如下,结合引用[1][2]中的模块化思想: ### 系统架构 ``` 树莓派(上位机) STM32(下位机) 二维云台 ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ OpenCV │<--->│ 串口通信协议 │<---->│ 舵机控制 │ │ 色块识别算法│ │ (UART/USB) │ │ X/Y轴电机 │ └──────┬──────┘ └──────┬──────┘ └─────┬─────┘ │摄像头视频流 │控制指令 │ ▼ ▼ ▼ [摄像头模块] [运动控制计算] [物理执行] ``` ### 实现步骤 1. **树莓派端开发(OpenCV处理)** ```python import cv2 import serial import numpy as np # 初始化串口 ser = serial.Serial('/dev/ttyACM0', 115200) # 连接STM32 # 色域范围(示例:追踪红色) lower_red = np.array([0, 100, 100]) upper_red = np.array([10, 255, 255]) cap = cv2.VideoCapture(0) while True: _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, lower_red, upper_red) # 形态学处理 kernel = np.ones((5,5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 寻找色块轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: max_contour = max(contours, key=cv2.contourArea) (x,y), radius = cv2.minEnclosingCircle(max_contour) if radius > 10: # 过滤噪声 # 计算位置偏差(图像中心为基准) h, w = frame.shape[:2] error_x = int(x - w/2) # 水平偏差 error_y = int(y - h/2) # 垂直偏差 # 发送控制指令给STM32 command = f"X{error_x}Y{error_y}\n" # 示例协议 ser.write(command.encode()) # 可视化标记 cv2.circle(frame, (int(x),int(y)), int(radius), (0,255,0), 2) cv2.imshow('Tracking', frame) if cv2.waitKey(1) == 27: break # ESC退出 cap.release() cv2.destroyAllWindows() ``` 2. **STM32端开发(云台控制)** ```c // 伪代码示例(基于HAL库) void UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart == &huart2) { // 假设使用USART2 // 解析指令 "X100Y-50" sscanf(rx_buffer, "X%dY%d", &target_x, &target_y); // 转换为舵机角度(比例映射) float angle_x = KP_X * target_x; // KP_X为比例系数 float angle_y = KP_Y * target_y; // 驱动云台舵机 set_servo_angle(SERVO_X, angle_x); set_servo_angle(SERVO_Y, angle_y); } } ``` 3. **通信协议设计** | 指令格式 | 说明 | 示例 | |----------|---------------|-------------| | `X[val]Y[val]\n` | 坐标偏移指令 | `X120Y-30\n` | ### 关键优化点 1. **动态参数调整** - 在HSV空间中实时调整色域范围,适应光照变化 - 添加PID控制算法减少云台抖动 ```c // STM32端PID伪代码 float pid_x = KP * error + KI * integral + KD * (error - prev_error); ``` 2. **性能提升** - 树莓派端使用OpenCV DNN模块加速(如NCS棒) - 降低图像分辨率至640x480提升处理速度 - 多线程处理:摄像头采集与算法分离 3. **故障处理** ```python # 树莓派端添加异常检测 if not ser.is_open: ser = serial.Serial('/dev/ttyACM1', 115200) # 尝试备用端口 ``` ### 系统集成示意 ``` [树莓派摄像头] → HSV色彩空间转换 → 形态学处理 → 轮廓定位 ↓ ↑ 原始帧 位置偏差计算 ↓ ↓ [视频显示] [UART指令传输] → [STM32] → [云台舵机] ``` > 该设计结合了引用[1]的机器视觉处理流程和引用[2]的嵌入式控制架构,通过分层设计实现: > - 上位机专注图像处理(树莓派+OpenCV) > - 下位机负责实时控制(STM32+舵机驱动) > - 串口通信实现跨平台协作[^1][^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值