基于STM32F103和OV7670摄像头实现颜色识别与追踪

基于STM32F103和OV7670摄像头实现颜色识别与追踪

系统组成

  1. 硬件平台:STM32F103系列单片机(如STM32F103C8T6、VET6等)
  2. 图像传感器:OV7670摄像头模块(带FIFO或不带FIFO版本)
  3. 显示模块:可选TFT LCD(用于实时显示和调试)
  4. 执行机构:舵机或直流电机(用于追踪控制)

实现步骤

1. 硬件连接

OV7670与STM32F103的主要连接方式如下:

信号线 OV7670引脚 STM32引脚 说明
数据总线 D0-D7 GPIOA0-GPIOA7 8位并行数据
垂直同步 VSYNC 外部中断引脚(如PB8) 帧同步信号
行同步 HREF GPIO输入(如PB9) 行有效信号
像素时钟 PCLK 定时器捕获引脚 像素时钟(约12MHz@QVGA)
SCCB时钟 SIO_C I2C_SCL(如PB6) 配置接口时钟
SCCB数据 SIO_D I2C_SDA(如PB7) 配置接口数据
主时钟 XCLK PWM输出(如TIM3_CH2) 24MHz时钟输入
电源 3.3V/GND 3.3V/GND 建议独立LDO供电

注意:如果使用带FIFO的模块,数据读取时序更宽松,但需要额外控制FIFO的读写使能信号。

2. OV7670配置

通过SCCB(兼容I2C)协议配置OV7670寄存器:

// 基本配置示例
SCCB_WriteReg(0x12, 0x80); // 复位所有寄存器
delay_ms(50);
SCCB_WriteReg(0x11, 0x01); // CLKRC - 内部时钟分频
SCCB_WriteReg(0x3a, 0x04); // TSLB - UV交换
SCCB_WriteReg(0x40, 0xd0); // COM15 - RGB565输出
SCCB_WriteReg(0x0c, 0x00); // COM7 - 输出格式选择
SCCB_WriteReg(0x3d, 0x03); // COM12 - 无HREF时输出黑色
// 设置QVGA分辨率(320x240)
SCCB_WriteReg(0x17, 0x1F); // HSTART
SCCB_WriteReg(0x18, 0x2B); // HSTOP
SCCB_WriteReg(0x19, 0x01); // VSTART
SCCB_WriteReg(0x1A, 0x7B); // VSTOP

3. 图像采集

无FIFO模式:需要严格按时序读取,在PCLK上升沿采集数据:

// 简化读取流程
while(1) {
    if(VSYNC下降沿) { // 新帧开始
        for(y=0; y<240; y++) {
            while(HREF==0); // 等待行有效
            for(x=0; x<320; x++) {
                while(PCLK==0); // 等待像素时钟
                data_high = GPIOC->IDR & 0xFF; // 读取高字节
                while(PCLK==1);
                while(PCLK==0);
                data_low = GPIOC->IDR & 0xFF;  // 读取低字节
                RGB565_buffer[y][x] = (data_high<<8) | data_low;
            }
        }
    }
}

带FIFO模式:可先让摄像头写入FIFO,再批量读取,降低实时性要求。

4. 颜色识别算法

4.1 RGB转HSV/HSL

HSV色彩空间对光照变化更鲁棒,适合颜色识别:

typedef struct {
    unsigned char Hue;        // 色度 [0,240]
    unsigned char Saturation; // 饱和度 [0,240]
    unsigned char Lightness;  // 亮度 [0,240]
} COLOR_HLS;

void RGB2HSL(uint8_t R, uint8_t G, uint8_t B, COLOR_HLS* hls) {
    uint8_t max_val = max3(R, G, B);
    uint8_t min_val = min3(R, G, B);
    int dif = max_val - min_val;
    
    // 计算亮度
    hls->Lightness = (max_val + min_val) * 240 / 255 / 2;
    
    if(max_val == min_val) {
        hls->Hue = 0;
        hls->Saturation = 0;
    } else {
        // 计算色度
        if(max_val == R) {
            if(G >= B) hls->Hue = 40 * (G - B) / dif;
            else       hls->Hue = 40 * (G - B) / dif + 240;
        } else if(max_val == G) {
            hls->Hue = 40 * (B - R) / dif + 80;
        } else { // max_val == B
            hls->Hue = 40 * (R - G) / dif + 160;
        }
        
        // 计算饱和度
        if(hls->Lightness == 0) hls->Saturation = 0;
        else hls->Saturation = dif * 240 / (511 - (max_val + min_val));
    }
}

4.2 阈值分割与目标定位

设定目标颜色的HSV范围,进行像素级过滤:

// 红色阈值示例
#define H_MIN_RED1 0
#define H_MAX_RED1 10
#define H_MIN_RED2 350
#define H_MAX_RED2 360
#define S_MIN 50
#define S_MAX 100
#define V_MIN 50
#define V_MAX 100

int is_target_color(COLOR_HLS hls) {
    // 红色有两个色相区间
    if(((hls.Hue >= H_MIN_RED1 && hls.Hue <= H_MAX_RED1) ||
        (hls.Hue >= H_MIN_RED2 && hls.Hue <= H_MAX_RED2)) &&
       hls.Saturation >= S_MIN && hls.Saturation <= S_MAX &&
       hls.Lightness >= V_MIN && hls.Lightness <= V_MAX) {
        return 1;
    }
    return 0;
}

4.3 腐蚀中心算法(区域定位)

高效定位色块中心的方法:

  1. 以13×13像素为单元遍历图像
  2. 在每个单元中心点采样,判断是否为目标颜色
  3. 若连续失败超过6次,跳过该单元
  4. 找到合格单元后,向四周扩展确定边界
  5. 计算中心坐标:center_x = (right + left) / 2, center_y = (top + bottom) / 2

5. 追踪控制

获取目标中心坐标后,可控制舵机或电机进行追踪:

// 简单PID控制示例
int target_x = 160; // 屏幕中心X
int target_y = 120; // 屏幕中心Y
int error_x = center_x - target_x;
int error_y = center_y - target_y;

// 水平方向控制
int pwm_x = Kp * error_x + Kd * (error_x - last_error_x);
servo_set_position_x(pwm_x);

// 垂直方向控制
int pwm_y = Kp * error_y + Kd * (error_y - last_error_y);
servo_set_position_y(pwm_y);

参考代码 stm32f103使用OV7670摄像头进行颜色识别及追踪 www.youwenfan.com/contentcnu/56255.html

优化建议

  1. 分辨率选择:从QVGA(320×240)开始,可降采样到160×120以提升处理速度
  2. 采样策略:隔行/隔列采样,或仅处理感兴趣区域(ROI)
  3. 内存优化:使用DMA传输图像数据,减少CPU占用
  4. 算法加速:使用查表法替代浮点运算,预先计算RGB到HSV的映射表
  5. 自适应阈值:根据环境光照动态调整HSV阈值范围

常见问题

  1. 图像撕裂:检查XCLK时钟稳定性,确保在24MHz±5%范围内
  2. 颜色识别不稳定:调整HSV阈值,增加饱和度/亮度过滤
  3. 帧率过低:降低分辨率,优化代码效率,使用带FIFO模块
  4. 通信失败:确认SCCB/I2C上拉电阻,检查时序

专注于matlab/simulink,电子电路,编程