STM32 + OpenMV 摄像头检测物体位置移动的代码

单片机/PLC wes 22 hours ago (2026-07-30) 46 views
📑 本文导航

简介

 OpenMV 检测小球,把小球X 坐标(像素)通过串口发送给 STM32;STM32 接收坐标,计算小球移动距离,OLED 屏幕显示:小球当前 X、移动偏移量。

通信协议:OpenMV 通过串口每次发送 1 个整数,格式 X\r\n,例如120\r\n。

使用 USART1 做串口通信,PA9 (TX)、PA10 (RX),STM32 只接收,PA10 接收 OpenMV 数据。

硬件接线

OpenMV → STM32F103

表格

OpenMV 引脚

STM32 引脚

说明

GND

GND

必须共地!非常重要

TX (P4)

PA10(USART1_RX)

OpenMV 发送 → STM32 接收

⚠️不要直接把 OpenMV 的 3.3V 接 STM32,只接 GND 和信号线。

OLED(0.96 寸 I2C)

  • SCL → PB8
  • SDA → PB9
  • VCC → 3.3V
  • GND → GND

STM32 完整代码(基于标准库)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include 

//串口接收缓冲区
uint8_t USART_RxBuf[16];
uint8_t RxIndex = 0;

int16_t Ball_X = 0;		    //小球当前X像素
int16_t Ball_X_Last = 0;	//上一帧X
int16_t Move_Dist = 0;		//单次帧移动偏移
int32_t Total_Move = 0;		//累计总位移

//USART1初始化 PA9(TX) PA10(RX) 波特率9600
void USART1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	USART_InitStruct.USART_BaudRate = 9600;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Rx;
	USART_Init(USART1, &USART_InitStruct);
	
	USART_Cmd(USART1, ENABLE);
}

//查询接收1字节,有数据返回1,无数据返回0
uint8_t USART1_GetChar(uint8_t *ch)
{
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)
	{
		*ch = USART_ReceiveData(USART1);
		return 1;
	}
	return 0;
}

int main(void)
{
	uint8_t ch;
	OLED_Init();
	USART1_Init();
	
	OLED_Clear();
	OLED_ShowString(1,1,"Ball X:");
	OLED_ShowString(2,1,"Move:");
	OLED_ShowString(3,1,"Total:");
	
	while (1)
	{
		if(USART1_GetChar(&ch))
		{
			if(ch == '\r')
			{
				USART_RxBuf[RxIndex] = '\0';
				Ball_X = atoi((char*)USART_RxBuf);
				
				Move_Dist = Ball_X - Ball_X_Last;
				Total_Move += Move_Dist;	//累加单次偏移得到总位移
				Ball_X_Last = Ball_X;
				
				RxIndex = 0;
			}
			else if( (ch >= '0' && ch <= '9') || ch == '-' )
			{
				if(RxIndex < 15)
				{
					USART_RxBuf[RxIndex++] = ch;
				}
			}
		}

		//刷新OLED
		OLED_ShowNum(1,7, Ball_X,4);
		OLED_ShowSignedNum(2,6, Move_Dist,4);
		OLED_ShowSignedNum(3,7, Total_Move,6);
		
		Delay_ms(50);
	}
}


 

OpenMV 测试脚本(复制到 OpenMV IDE 运行)

作用:寻找小球,输出小球 X 坐标,串口 9600 发送 X\r\n

颜色阈值根据你的小球实际颜色调整!

python

# OpenMV代码,寻找小球,输出X坐标
import sensor, image, time
from pyb import UART

uart = UART(1, 9600)
uart.init(9600, bits=8, parity=None, stop=1)

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)

# 这里修改成你小球颜色阈值!!示例红色
red_threshold = (30, 100, 15, 127, 15, 127)

while(True):
    img = sensor.snapshot()
    blobs = img.find_blobs([red_threshold],pixels_threshold=20,area_threshold=20)
    if blobs:
        b = blobs[0]
        cx = b.cx()
        img.draw_cross(b.cx(), b.cy())
        uart.write("%d\r\n"%cx)

使用说明

  1. OpenMV 修改颜色阈值,确保画面可以识别小球;
  2. GND 一定要共地,否则串口乱码接收不到
  3. OLED 第一行显示小球 X 像素坐标,第二行显示相对于上一帧移动距离
  4. Move 为正数代表向右移动,负数向左移动。

拓展提示:

如果想要真实物理距离(mm),还需要做像素‑毫米标定,当前代码输出的是像素偏移量。

如果你希望改成累计总移动距离,只需要定义int32_t Total_Move = 0;,循环中Total_Move += Move_Dist;再 OLED 显示 Total_Move。

 

OpenMV‑IDE 中 OpenMV 看 x,y 坐标步骤

脚本直接粘贴到 OpenMV‑IDE,下载到板子运行; 3 个地方可以看 xy 数值

一、硬件连接

  1. USB 线:OpenMV 板子用 USB 接到电脑
  2. 打开 OpenMV IDE
  3. 点左下角 连接按钮(插头图标),IDE 连上摄像头板子
  4. 打开你的测试脚本,粘贴代码,点绿色▶运行(脚本下载到板子执行)

二、3 种查看 x,y 的方式(最常用 1、2)

方式 1:IDE 串口终端(最简单,直接打印数字 x y)✅推荐

脚本里面要有类似:

python

print(“x =”, cx, ” y =”, cy)

  1. IDE 上方菜单栏:工具 → 串行终端(或者快捷键 Ctrl+T)
  2. 弹出串口窗口,勾选 启用,波特率默认 115200
  3. 板子运行脚本后,这里会不停刷新输出 x=220 y=135 这类坐标

⚠注意:不要打开电脑额外串口工具,OpenMV IDE 自带终端就够用。

方式 2:IDE 帧缓冲区(图像预览窗口)

IDE 右侧有预览画面,摄像头实时画面。

  • 如果脚本写了 draw_cross((cx, cy), size=10),预览图上会画出十字标记,十字就是识别到物体的 (x,y) 中心点。
  • 鼠标直接点预览窗口画面右下角,鼠标悬停,可以看鼠标当前像素坐标(不是识别结果,是鼠标位置)。

方式 3:OLED 屏幕(板子外接 OLED 显示屏,脱离电脑看坐标)

如果接了 I2C OLED 屏幕,可以把 x,y 打印在硬件屏幕,不用电脑;没接硬件就不用管。

常见踩坑

  1. 运行了脚本,但是串口没有任何输出
  • ✔确认 IDE 已经连接板子(插头图标变已连接)
  • ✔必须点绿色▶运行脚本;只是粘贴代码不点击运行,板子不会执行
  • ✔脚本里必须写 print () 打印 cx、cy;没有 print 就不会输出
  • ✔串行终端勾选启用,波特率 115200
  1. 预览窗口黑屏
  • 检查镜头盖子摘掉;检查脚本是否初始化摄像头 sensor.reset ()

一段最简可直接复制的演示脚本(用来测试看 x,y)

python

import sensor, image, time

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320×240
sensor.skip_frames(time = 2000)

while(True):
    img = sensor.snapshot()
    # 示例:找色块,你换成自己的识别逻辑
    blobs = img.find_blobs([(30, 80,10,60,10,60)], pixels_threshold=20)
    for b in blobs:
        cx = b.cx()
        cy = b.cy()
        img.draw_cross(cx, cy, size=12)
        print("识别中心 x:%d , y:%d" % (cx, cy))

 

运行后:

  • IDE 预览画面出现十字标记
  • Ctrl+T 打开串行终端,持续打印 x y 数值