保姆级教程:用OpenMV和STM32做个能测距的‘电子眼’(附完整源码)

保姆级教程:用OpenMV和STM32做个能测距的‘电子眼’(附完整源码) 从零打造智能测距电子眼OpenMV与STM32实战指南项目概述与核心价值在创客圈里能看见世界的电子项目总是格外引人注目。今天我们要实现的是一个融合计算机视觉与嵌入式控制的智能硬件——它不仅能识别特定颜色的物体还能实时测量物体与摄像头之间的距离并通过OLED屏幕直观显示数据。这个项目完美结合了OpenMV的图像处理能力和STM32的实时控制特性是学习嵌入式视觉系统的绝佳入门案例。整套系统的工作流程非常清晰OpenMV摄像头负责捕捉图像、识别色块并计算距离STM32单片机接收数据并驱动OLED显示。两个核心模块通过串口通信交换信息形成完整的视觉-控制闭环。相比单纯的理论学习这种实战项目能让你快速掌握多设备协同开发的关键技能。1. 硬件准备与环境搭建1.1 所需材料清单视觉模块OpenMV Cam H7推荐或M7版本主控板STM32F103C8T6最小系统板蓝色药丸板显示模块0.96寸OLED屏幕I2C接口连接线材杜邦线若干建议使用不同颜色区分功能电源供应5V/2A USB电源适配器 Micro USB线其他工具万用表、面包板可选提示OpenMV与STM32的供电需特别注意。建议先用USB分别调试联机时确保共地。1.2 开发环境配置OpenMV端开发环境下载OpenMV IDE官网提供跨平台版本安装对应串口驱动CH340或CP210x连接摄像头后在IDE中升级固件至最新版本STM32开发环境# 基于PlatformIO的CLI配置示例 platformio init --board bluepill_f103c8 platformio lib install Adafruit SSD1306 platformio lib install Wire或者使用Keil MDK进行开发需要安装STM32标准外设库ST-Link驱动SSD1306 OLED驱动库2. OpenMV视觉处理实现2.1 基础图像采集与色块识别OpenMV的图像处理流程始于感光元件的初始化配置。以下代码展示了如何设置摄像头参数并捕捉图像import sensor, image, time # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) # 设置色彩格式 sensor.set_framesize(sensor.QVGA) # 分辨率320x240 sensor.skip_frames(time2000) # 等待设置生效 # 定义红色阈值(根据实际环境调整) red_threshold (30, 100, 15, 127, 15, 127) while(True): img sensor.snapshot() # 捕获一帧图像 # 寻找色块 blobs img.find_blobs([red_threshold], pixels_threshold100, area_threshold100, mergeTrue) if blobs: max_blob max(blobs, keylambda b: b.pixels()) # 在图像上标记色块 img.draw_rectangle(max_blob.rect(), color(255,0,0)) img.draw_cross(max_blob.cx(), max_blob.cy(), color(0,255,0))关键参数说明参数说明典型值pixels_threshold最小像素点数100-200area_threshold最小区域面积100merge是否合并相邻色块True/False2.2 精确距离测量算法基于单目视觉的距离测量有多种方法我们采用已知尺寸标定法其核心公式为实际距离 (焦距 × 物体实际尺寸) / 像素尺寸具体实现步骤标定阶段将已知尺寸的物体如边长为4cm的立方体放置在距离摄像头25cm处测量物体在图像中的像素尺寸宽、高计算比例系数K测量阶段检测目标物体的像素尺寸应用预存的K值计算实际距离# 标定参数需根据实际测量调整 K_WIDTH 0.16 # cm/pixel K_HEIGHT 0.16 # cm/pixel K_DISTANCE 500 # 距离系数 def calculate_distance(blob): # 计算平均尺寸像素 size_pixels (blob.w() blob.h()) / 2 # 计算实际距离cm distance K_DISTANCE / size_pixels # 计算实际尺寸 width_cm blob.w() * K_WIDTH height_cm blob.h() * K_HEIGHT return distance, width_cm, height_cm3. STM32数据处理与显示3.1 串口通信配置确保OpenMV与STM32的串口参数完全一致// USART2初始化配置STM32端 void USART2_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // TX(PA2)配置为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // RX(PA3)配置为浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate bound; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, USART_InitStructure); USART_Cmd(USART2, ENABLE); }3.2 数据协议解析定义简单的通信协议字节位置内容说明0-10xB3帧头标识2X坐标物体中心X坐标3Y坐标物体中心Y坐标4距离物体距离(cm)5-60x0D0A帧尾(CRLF)对应的STM32中断处理逻辑// 在stm32f10x_it.c中实现 void USART2_IRQHandler(void) { static u8 state 0; static u8 buffer[7]; u8 data USART_ReceiveData(USART2); switch(state) { case 0: if(data 0xB3) { buffer[0]data; state1; } break; case 1: if(data 0xB3) { buffer[1]data; state2; } break; case 2: buffer[2]data; state3; break; // X坐标 case 3: buffer[3]data; state4; break; // Y坐标 case 4: buffer[4]data; state5; break; // 距离 case 5: if(data 0x0D) state6; else state0; // 协议错误 break; case 6: if(data 0x0A) { // 完整帧接收 process_data(buffer); } state 0; break; } }3.3 OLED显示实现使用SSD1306驱动库显示关键信息void display_data(int x, int y, float distance) { OLED_Clear(); OLED_ShowString(0, 0, Object Position:); OLED_ShowString(0, 2, X:); OLED_ShowNum(24, 2, x, 3, 16); OLED_ShowString(64, 2, Y:); OLED_ShowNum(88, 2, y, 3, 16); OLED_ShowString(0, 4, Distance:); OLED_ShowNum(72, 4, (int)distance, 3, 16); OLED_ShowString(108, 4, cm); OLED_Refresh(); }4. 系统集成与调试技巧4.1 硬件连接指南正确连接是项目成功的关键OpenMV引脚STM32引脚功能说明P4(TXD)PA3(RX)串口发送P5(RXD)PA2(TX)串口接收GNDGND共地连接3.3V3.3V可选供电注意串口连接遵循交叉原则——TX接RXRX接TX。供电方面建议分别供电而非从OpenMV给STM32供电以避免功率不足。4.2 常见问题排查问题1串口通信失败检查波特率是否一致建议9600或115200验证TX/RX线是否交叉连接确保共地接可靠用逻辑分析仪或USB-TTL工具监测数据流问题2距离测量不准重新进行标定流程确保测量环境光照充足且稳定调整色块阈值参数尝试使用更大尺寸的识别物体问题3OLED无显示检查I2C地址通常0x3C或0x3D确认复位电路正常工作测试OLED模块是否完好单独测试4.3 性能优化建议帧率提升降低图像分辨率如从QVGA改为QQVGA缩小感兴趣区域(ROI)优化色块识别阈值距离测量改进采用多帧平均滤波实现动态K值调整增加超声波传感器辅助校准系统稳定性增强添加通信校验如CRC实现超时重传机制增加系统状态指示灯5. 项目扩展与进阶方向5.1 功能扩展实现多目标跟踪 修改色块识别代码同时追踪多个颜色不同的物体# 定义多个颜色阈值 thresholds [ (30, 100, 15, 127, 15, 127), # 红色 (0, 30, -128, -20, -128, -20), # 蓝色 (0, 30, -128, -20, 20, 127) # 绿色 ] # 在循环中处理所有色块 for blob in img.find_blobs(thresholds, mergeTrue): color_code blob.code() # 获取颜色编码 # 根据颜色编码进行不同处理无线数据传输 通过ESP8266模块将数据发送到手机或云平台// STM32端通过串口发送到ESP8266 void send_to_wifi(float x, float y, float dist) { char buffer[64]; sprintf(buffer, OBJ_DATA:%.1f,%.1f,%.1f\n, x, y, dist); USART_SendString(USART1, buffer); // 发送到ESP8266 }5.2 机械结构设计为项目设计3D打印外壳摄像头固定支架主板安装底座OLED屏幕保护框整体便携式外壳使用FreeCAD或Fusion 360设计考虑散热孔位线缆管理模块化组装5.3 实际应用场景智能小车避障作为前视传感器工业分拣系统识别特定颜色物体互动艺术装置视觉反馈系统教育演示工具计算机视觉教学开发心得与实用建议在实际调试过程中有几点经验值得分享电源管理至关重要当OpenMV和STM32同时工作时电流需求可能超过1A。使用质量差的USB线会导致电压下降引发各种奇怪问题。建议使用带电流表的USB测试仪监测供电情况。色块识别对环境光敏感在实验室调试好的参数拿到窗边可能就失效了。两种解决方案要么增加自动白平衡功能要么设计遮光罩控制环境光。串口通信的稳定性技巧在数据帧之间增加适当延时如50ms添加软件校验和验证数据完整性为STM32配置DMA接收减轻CPU负担OLED显示优化频繁刷新会导致屏幕闪烁。可以采用局部刷新策略或者每10帧更新一次数据同时保持重要信息的持久显示。调试工具的选择除了常规的串口调试助手推荐使用OpenMV IDE内置的帧缓冲区查看器可以实时观察摄像头捕捉到的图像和处理结果极大提高调试效率。