📑 本文导航
简介
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)
使用说明
- OpenMV 修改颜色阈值,确保画面可以识别小球;
- GND 一定要共地,否则串口乱码接收不到;
- OLED 第一行显示小球 X 像素坐标,第二行显示相对于上一帧移动距离;
- Move 为正数代表向右移动,负数向左移动。
拓展提示:
如果想要真实物理距离(mm),还需要做像素‑毫米标定,当前代码输出的是像素偏移量。
如果你希望改成累计总移动距离,只需要定义int32_t Total_Move = 0;,循环中Total_Move += Move_Dist;再 OLED 显示 Total_Move。
OpenMV‑IDE 中 OpenMV 看 x,y 坐标步骤
脚本直接粘贴到 OpenMV‑IDE,下载到板子运行;有 3 个地方可以看 x、y 数值
一、硬件连接
- USB 线:OpenMV 板子用 USB 接到电脑
- 打开 OpenMV IDE
- 点左下角 连接按钮(插头图标),IDE 连上摄像头板子
- 打开你的测试脚本,粘贴代码,点绿色▶运行(脚本下载到板子执行)
二、3 种查看 x,y 的方式(最常用 1、2)
方式 1:IDE 串口终端(最简单,直接打印数字 x y)✅推荐
脚本里面要有类似:
python
print(“x =”, cx, ” y =”, cy)
- IDE 上方菜单栏:工具 → 串行终端(或者快捷键 Ctrl+T)
- 弹出串口窗口,勾选 启用,波特率默认 115200
- 板子运行脚本后,这里会不停刷新输出 x=220 y=135 这类坐标
⚠注意:不要打开电脑额外串口工具,OpenMV IDE 自带终端就够用。
方式 2:IDE 帧缓冲区(图像预览窗口)
IDE 右侧有预览画面,摄像头实时画面。
- 如果脚本写了 draw_cross((cx, cy), size=10),预览图上会画出十字标记,十字就是识别到物体的 (x,y) 中心点。
- 鼠标直接点预览窗口画面右下角,鼠标悬停,可以看鼠标当前像素坐标(不是识别结果,是鼠标位置)。
方式 3:OLED 屏幕(板子外接 OLED 显示屏,脱离电脑看坐标)
如果接了 I2C OLED 屏幕,可以把 x,y 打印在硬件屏幕,不用电脑;没接硬件就不用管。
常见踩坑
- 运行了脚本,但是串口没有任何输出
- ✔确认 IDE 已经连接板子(插头图标变已连接)
- ✔必须点绿色▶运行脚本;只是粘贴代码不点击运行,板子不会执行
- ✔脚本里必须写 print () 打印 cx、cy;没有 print 就不会输出
- ✔串行终端勾选启用,波特率 115200
- 预览窗口黑屏
- 检查镜头盖子摘掉;检查脚本是否初始化摄像头 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 数值