STM32+FPGA电赛实战:视觉跟踪系统软硬件协同设计与PID控制

发布时间:2026/8/2 3:04:34
STM32+FPGA电赛实战:视觉跟踪系统软硬件协同设计与PID控制 最近在准备2026年电赛特别是吉林省赛区的发挥部分发现很多同学在系统设计、软硬件协同和算法实现上容易卡壳。网上资料虽然多但往往不成体系尤其是针对具体赛题的系统性实战拆解较少。本文将围绕一个典型的电赛发挥部分题目以第一问为例结合STM32和FPGA两大热门平台从需求分析、方案选型、硬件设计、软件编程到调试优化提供一个完整的闭环实战方案。无论你是初次参赛的新手还是想提升工程化能力的老手都能从中获得可直接复用的代码、配置和排错思路。1. 赛题分析与核心需求拆解电赛的“发挥部分”通常要求参赛者在完成基本功能的基础上实现更高性能、更智能或更创新的设计。我们假设一个典型的“第一问”场景基于视觉或传感器信号实现一个高精度、实时性的运动控制系统。这类题目在控制类、仪器仪表类赛题中非常常见。1.1 典型赛题场景描述假设赛题要求如下设计一个系统能够通过摄像头或线性CCD识别一个小球的实时位置坐标X, Y并控制一个二维舵机云台或步进电机平台快速、平稳地跟踪小球运动使云台中心始终对准小球。考核指标包括跟踪精度、响应速度、超调量和稳态误差。1.2 核心需求与技术难点信号采集与处理需要实时获取小球位置。如果用摄像头涉及图像采集、处理二值化、滤波、轮廓查找、中心计算数据量大对处理速度要求高。控制算法实现需要将位置偏差ΔX, ΔY转换为舵机或电机的控制量如PWM占空比。常用的PID算法需要仔细调参。实时性与确定性整个“感知-计算-控制”环路必须在极短的时间内完成例如20ms以内任何环节的延迟都会导致系统不稳定。多核心协同图像处理计算密集适合用FPGA进行硬件加速控制逻辑和系统调度适合用STM32等单片机。二者如何高效、可靠地通信是关键。抗干扰与鲁棒性环境光线变化、小球运动突变、机械抖动等都需要系统有良好的鲁棒性。1.3 主流方案选型STM32FPGA架构针对上述难点STM32 FPGA的组合成为许多优秀作品的选择STM32 (ARM Cortex-M内核)擅长逻辑控制、任务调度、浮点运算如果带FPU、外设通信UART, SPI, I2C, PWM。负责运行控制算法PID、管理FPGA、与上位机通信、生成最终执行机构的控制信号。FPGA (现场可编程门阵列)擅长并行处理、流水线操作、对确定时序要求极高的任务。负责实现图像传感器的驱动、图像预处理算法如灰度化、高斯滤波、Sobel边缘检测、二值化、连通域分析等并将计算出的目标坐标通过高速接口如SPI、并行总线发送给STM32。这种架构将计算密集型任务卸载到FPGA保证了图像处理的实时性STM32则专注于控制决策使系统层次清晰性能最大化。2. 开发环境与工具链准备工欲善其事必先利其器。一个稳定、高效的开发环境能事半功倍。2.1 硬件准备清单组件推荐型号/说明作用主控MCUSTM32F407ZGT6 / STM32H750VBT6系统主控运行控制算法。F4系列性价比较高H7系列性能更强。FPGAAltera Cyclone IV EP4CE10 / Xilinx Artix-7 XC7A35T实现图像处理流水线。根据逻辑资源和IO需求选择。图像传感器OV7725 (DVP接口) / MT9V034 (全局快门)采集图像。OV7725成本低MT9V034抗拖影更好。执行机构数字舵机 (如DS3225) / 步进电机驱动器控制云台运动。舵机控制简单步进电机精度高。调试工具ST-Link V2 / J-Link, USB-TTL, 逻辑分析仪程序下载、调试、串口通信、信号观测。电源模块12V/5V/3.3V多路输出稳压模块为各部件提供稳定、干净的电源极其重要2.2 软件与IDE准备STM32开发IDE: Keil MDK (uVision5) 或 STM32CubeIDE。CubeIDE基于Eclipse免费且集成CubeMX推荐新手使用。配置工具: STM32CubeMX。用于图形化配置引脚、时钟、外设生成初始化代码。串口工具: Tera Term, Putty, 或SecureCRT。用于查看调试信息。FPGA开发IDE: Intel Quartus Prime (针对Altera/Intel FPGA) 或 Xilinx Vivado (针对Xilinx FPGA)。设计语言: Verilog HDL 或 VHDL。本文示例使用Verilog。仿真工具: ModelSim (通常集成在Quartus/Vivado中)用于代码功能仿真。辅助工具版本控制: Git。管理代码避免版本混乱。文档编辑: Markdown编辑器。撰写设计报告、笔记。2.3 版本说明与项目初始化STM32: 使用HAL库或LL库。HAL库抽象程度高易于移植LL库更接近寄存器效率高。本文基于STM32CubeMX生成HAL库代码进行演示。FPGA: 以Quartus Prime 18.1 Standard Edition和Cyclone IV EP4CE10为例。第一步创建STM32工程打开STM32CubeMX选择对应型号芯片。配置系统时钟通常使用外部晶振最大化主频。配置用于调试的SWD接口SWDIO, SWCLK并禁用被复用的JTAG引脚如PA15, PB3, PB4作为普通GPIO使用这在资源紧张时很常见。// 在CubeMX中Pinout视图下找到这些引脚选择功能为GPIO_Output或GPIO_Input即可。配置与FPGA通信的SPI或FSMC并行总线。SPI简单FSMC速度快。这里以SPI全双工模式为例配置为主机模式。配置生成PWM控制舵机的定时器如TIM1, TIM8的高级定时器。配置一个UART用于打印调试信息。生成代码选择IDE为MDK-ARM或CubeIDE。第二步创建FPGA工程打开Quartus Prime创建新工程。选择器件家族Cyclone IV和具体型号EP4CE10E22C8。创建顶层Verilog文件。3. 系统硬件设计与核心模块3.1 系统硬件框图一个清晰的硬件框图是设计的起点。------------------- ------------------- | 图像传感器 | | | | (如OV7725) |---------| FPGA | | | DVP | (图像处理流水线) | ------------------- | | ------------------ | SPI/并行总线 ------------------ | | | STM32 | | (PID控制调度) | | | ------------------ | PWM/脉冲 ------------------ | | | 舵机云台 | | (执行机构) | | | -------------------3.2 STM32与FPGA通信接口选择SPI (串行外设接口)优点引脚少4线制协议简单STM32硬件SPI速率可达几十Mbps。缺点数据需要串行打包/解包有一定软件开销。适用数据量不大如只传输目标坐标、实时性要求不是极端高的场景。接线示例STM32 SPI1_MOSI (PA7) - FPGA SDISTM32 SPI1_MISO (PA6) - FPGA SDOSTM32 SPI1_SCK (PA5) - FPGA SCLKSTM32 SPI1_NSS (PA4) - FPGA CSn (片选)FSMC (灵活的静态存储器控制器) / 并行总线优点并行传输速度快吞吐量大STM32可像访问内存一样访问FPGA。缺点占用引脚多数据线D0-D15地址线控制线布线复杂。适用需要高速、大批量传输图像中间数据或参数的场景。接线示例将FPGA内部定义一个寄存器映射到STM32的某个地址STM32通过写该地址来发送数据通过读该地址来获取数据。本文选择SPI作为示例因其平衡了速度与复杂度。3.3 图像处理流水线设计FPGA侧FPGA内的图像处理通常采用流水线(Pipeline)结构每一时钟周期处理一个像素。核心模块划分传感器驱动模块 (sensor_controller.v)产生OV7725所需的SCCB类似I2C配置时序和像素时钟(PCLK)、行同步(VSYNC)、场同步(HREF)捕获逻辑。图像缓存模块 (line_buffer.v)使用FPGA内部的RAM如M9K构建行缓冲区为需要邻域像素的操作如滤波提供数据。预处理模块 (image_preprocess.v)灰度化如果传感器输出RGB。高斯滤波消除噪声。二值化固定阈值或自适应阈值用于分离小球。目标识别模块 (target_detection.v)连通域分析Labeling或质心计算。对于单个目标可以计算二值图像中白色像素的坐标平均值作为质心。输出目标中心的X、Y坐标通常用10-12位表示足够。通信封装模块 (spi_slave.v)将坐标数据打包按照与STM32约定的协议通过SPI从机接口发送出去。示例SPI从机通信协议设计约定一帧数据为16位2字节。例如用高字节传输X坐标高8位低字节传输X坐标低8位下一帧传输Y坐标。同时可以加入帧头、校验和等提高可靠性。// spi_slave.v 部分代码片段 module spi_slave ( input wire clk, // 系统时钟 input wire rst_n, // 复位 // SPI 接口 input wire sclk, // SPI时钟 input wire cs_n, // 片选 input wire mosi, // 主机输出从机输入 output reg miso, // 主机输入从机输出 // 数据接口 input wire [15:0] tx_data_x, // 待发送的X坐标 input wire [15:0] tx_data_y, // 待发送的Y坐标 input wire data_ready, // 数据就绪信号 output reg data_sent // 数据已发送完成 ); // 状态机定义IDLE, SEND_X_HIGH, SEND_X_LOW, SEND_Y_HIGH, SEND_Y_LOW reg [2:0] state; reg [15:0] shift_reg; reg [3:0] bit_cnt; // ... 状态机逻辑在cs_n为低且sclk上升沿时将shift_reg的数据通过miso移出 // 同时在sclk下降沿采样mosi如果STM32也需要向FPGA发送数据 endmodule4. STM32软件设计与控制算法实现4.1 STM32 CubeMX配置与代码生成SPI配置在CubeMX中将SPI1配置为全双工主模式。设置数据大小为8位或16位与FPGA协议匹配设置波特率预分频器如PCLK/16CPOLLow CPHA1Edge模式0。定时器配置配置一个定时器如TIM1的某个通道如CH1为PWM Generation模式用于控制舵机。舵机PWM周期通常为20ms脉冲宽度0.5ms-2.5ms对应0-180度。UART配置配置一个UART如USART1用于调试波特率115200。生成代码生成初始化代码后在工程中重点编写以下部分。4.2 SPI通信驱动与数据解析在STM32中使用HAL库的SPI收发函数。我们需要实现一个协议解析层。// spi_com.h #ifndef __SPI_COM_H #define __SPI_COM_H #include main.h #define SPI_TIMEOUT 100 // 超时时间(ms) typedef struct { int16_t ball_x; // 小球X坐标 int16_t ball_y; // 小球Y坐标 uint8_t data_valid; // 数据有效标志 } Vision_Data_t; extern Vision_Data_t g_vision_data; HAL_StatusTypeDef SPI_GetVisionData(SPI_HandleTypeDef *hspi, Vision_Data_t *pData); #endif// spi_com.c #include spi_com.h Vision_Data_t g_vision_data {0}; HAL_StatusTypeDef SPI_GetVisionData(SPI_HandleTypeDef *hspi, Vision_Data_t *pData) { uint8_t rx_buf[4] {0}; // 假设接收4字节X高、X低、Y高、Y低 uint8_t tx_buf[4] {0xFF, 0xFF, 0xFF, 0xFF}; // 发送哑元数据因为SPI全双工需要 HAL_GPIO_WritePin(FPGA_CS_GPIO_Port, FPGA_CS_Pin, GPIO_PIN_RESET); // 拉低片选 HAL_StatusTypeDef status HAL_SPI_TransmitReceive(hspi, tx_buf, rx_buf, 4, SPI_TIMEOUT); HAL_GPIO_WritePin(FPGA_CS_GPIO_Port, FPGA_CS_Pin, GPIO_PIN_SET); // 拉高片选 if(status HAL_OK) { // 解析数据假设大端模式先高字节后低字节 pData-ball_x (rx_buf[0] 8) | rx_buf[1]; pData-ball_y (rx_buf[2] 8) | rx_buf[3]; pData-data_valid 1; } else { pData-data_valid 0; } return status; }4.3 PID控制算法实现PID是控制系统的核心。我们将实现一个位置式PID控制器。// pid_controller.h #ifndef __PID_CONTROLLER_H #define __PID_CONTROLLER_H typedef struct { float Kp, Ki, Kd; // PID系数 float integral; // 积分项 float prev_error; // 上一次误差 float integral_limit; // 积分限幅 float output_limit; // 输出限幅 } PID_Controller_t; void PID_Init(PID_Controller_t *pid, float kp, float ki, float kd, float i_limit, float out_limit); float PID_Calculate(PID_Controller_t *pid, float setpoint, float measurement); #endif// pid_controller.c #include pid_controller.h void PID_Init(PID_Controller_t *pid, float kp, float ki, float kd, float i_limit, float out_limit) { pid-Kp kp; pid-Ki ki; pid-Kd kd; pid-integral 0.0f; pid-prev_error 0.0f; pid-integral_limit i_limit; pid-output_limit out_limit; } float PID_Calculate(PID_Controller_t *pid, float setpoint, float measurement) { float error setpoint - measurement; // 比例项 float proportional pid-Kp * error; // 积分项抗饱和 pid-integral error; // 积分限幅 if(pid-integral pid-integral_limit) pid-integral pid-integral_limit; else if(pid-integral -pid-integral_limit) pid-integral -pid-integral_limit; float integral pid-Ki * pid-integral; // 微分项用误差的差分近似微分 float derivative pid-Kd * (error - pid-prev_error); pid-prev_error error; // 计算总输出 float output proportional integral derivative; // 输出限幅 if(output pid-output_limit) output pid-output_limit; else if(output -pid-output_limit) output -pid-output_limit; return output; }4.4 主控制循环与任务调度在main.c或单独的任务文件中组织整个控制流程。建议使用定时器中断来保证控制周期的稳定性。// main.c 部分代码 #include main.h #include spi_com.h #include pid_controller.h #include servo.h SPI_HandleTypeDef hspi1; UART_HandleTypeDef huart1; TIM_HandleTypeDef htim1; // PWM定时器 PID_Controller_t pid_x, pid_y; Vision_Data_t vision_data; // 定时器中断回调函数例如1kHz void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim2) { // 假设TIM2用于1ms定时 static uint32_t control_tick 0; control_tick; // 每20ms执行一次控制循环50Hz if(control_tick % 20 0) { // 1. 从FPGA获取视觉数据 if(SPI_GetVisionData(hspi1, vision_data) HAL_OK vision_data.data_valid) { // 2. 计算目标位置与图像中心位置的偏差 // 假设图像中心为(320, 240)目标坐标为(ball_x, ball_y) float error_x 320.0f - vision_data.ball_x; float error_y 240.0f - vision_data.ball_y; // 3. PID计算 float output_x PID_Calculate(pid_x, 0, error_x); // 目标偏差为0 float output_y PID_Calculate(pid_y, 0, error_y); // 4. 将PID输出转换为舵机PWM脉宽 Servo_SetPulse(htim1, TIM_CHANNEL_1, output_x); // X轴舵机 Servo_SetPulse(htim1, TIM_CHANNEL_2, output_y); // Y轴舵机 // 5. 调试通过串口打印数据 printf(X:%d, Y:%d, eX:%.1f, eY:%.1f, oX:%.1f, oY:%.1f\r\n, vision_data.ball_x, vision_data.ball_y, error_x, error_y, output_x, output_y); } } } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init(); MX_USART1_UART_Init(); MX_TIM1_Init(); // PWM定时器 MX_TIM2_Init(); // 系统定时器 // 初始化PID控制器 PID_Init(pid_x, 0.5f, 0.01f, 0.05f, 100.0f, 500.0f); // 参数需要实际调试 PID_Init(pid_y, 0.5f, 0.01f, 0.05f, 100.0f, 500.0f); // 启动定时器 HAL_TIM_Base_Start_IT(htim2); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); printf(System Start\r\n); while (1) { // 主循环可以处理其他任务如按键扫描、状态显示等 // 核心控制已在定时器中断中完成 } }5. 系统联调与常见问题排查软硬件分别调试通过后进行系统联调是问题高发阶段。5.1 调试方法与工具分段调试FPGA单独调试使用Quartus的SignalTap II逻辑分析仪抓取图像处理流水线中各阶段的信号验证坐标计算是否正确。STM32单独调试编写测试代码模拟FPGA发送固定坐标数据验证SPI接收和PID计算、PWM输出是否正确。用逻辑分析仪或示波器测量SPI波形和PWM波形。传感器单独调试确保FPGA能正确驱动传感器并读出图像数据。联合调试先让小球静止观察系统能否稳定在中心。然后让小球缓慢移动观察跟踪是否平滑。最后测试小球快速移动或突然变向观察系统响应和超调。5.2 常见问题与解决方案问题现象可能原因排查思路与解决方案FPGA无法识别传感器数据1. 传感器电源/时钟不正常。2. SCCB配置失败。3. 时序不满足传感器要求。1. 用示波器检查传感器供电、主时钟(MCK)。2. 用逻辑分析仪抓取SCCBI2C波形确认寄存器配置是否成功。3. 检查FPGA代码中的VSYNC、HREF、PCLK同步逻辑。STM32收不到FPGA数据或数据错乱1. SPI模式(CPOL/CPHA)不匹配。2. 片选(CS)时序问题。3. 波特率过高导致时序裕量不足。4. 协议解析错误大小端、帧结构。1.务必确认STM32和FPGA的SPI模式、数据位宽、时钟极性相位完全一致。2. 用逻辑分析仪同时抓取SCLK、MOSI、MISO、CS四根线对照协议分析。3. 降低SPI波特率再试。4. 检查STM32解析代码与FPGA发送代码逐字节对照。系统跟踪响应慢滞后严重1. 图像处理算法耗时过长。2. 控制周期太长。3. PID参数不合适积分项太弱或微分项有负作用。1. 优化FPGA算法减少流水线级数或降低图像分辨率。2. 缩短定时器中断周期提高控制频率。3. 重新整定PID参数先调P再调D最后调I。系统振荡云台来回抖动1. 微分系数D过大。2. 机械结构松动或舵机力矩不足。3. 控制周期不稳定存在较大抖动。1. 减小D参数或尝试只使用PI控制器。2. 加固机械结构选用扭矩更大的舵机。3. 确保控制中断由硬件定时器触发避免在软件循环中延时。上电后程序不运行1. 电源问题。2. 启动模式配置错误。3. 晶振未起振。4. 程序下载失败或未固化。1. 测量各点电压3.3V, 1.2V等。2. 检查STM32的BOOT0/BOOT1引脚电平。3. 检查晶振电路测量波形。4. 确认编程器连接正常对于FPGA检查固化配置是否成功如使用EPCS芯片。使用STM32CubeMX禁用JTAG后无法下载程序禁用了SWD所用的引脚。1. 进入ISP模式拉高BOOT0通过串口下载一个不禁用JTAG/SWD引脚的程序。2. 或者使用NRST复位引脚配合编程器进行连接前复位有时可以强制进入调试模式。6. 工程优化与备赛建议6.1 性能优化技巧FPGA侧流水线优化确保图像处理每个阶段都能在一个时钟周期内完成避免组合逻辑过长导致时序违例。资源复用对于多个相同操作如两个方向的Sobel尽量复用计算模块。使用IP核Quartus/Vivado提供的乘法器、RAM、FIFO等IP核经过优化性能和资源利用率更高。STM32侧启用硬件FPU如果使用F4或H7系列在IDE中开启硬件浮点单元能极大加速PID等浮点运算。使用DMA如果数据量大例如通过FSMC传输使用DMA可以解放CPU。优化中断只将最紧急的任务放在中断中非关键任务放到主循环。6.2 稳定性与鲁棒性提升通信可靠性在SPI协议中加入帧头如0xAA、0x55和校验和如CRC8。STM32侧增加超时重传和错误计数机制。控制算法增强积分抗饱和上文PID代码已实现。设定值平滑对目标设定值进行滤波避免阶跃变化导致系统冲击。变参数PID根据误差大小动态调整PID参数大误差时快速响应小误差时精细调节。电源管理模拟部分传感器和数字部分FPGASTM32使用磁珠或电感隔离供电。关键电源节点增加去耦电容如100nF 10uF。6.3 电赛备赛实战建议模块化开发将摄像头驱动、图像算法、电机控制、通信协议等封装成独立的模块便于调试和复用。版本控制务必使用Git管理代码每次重大更改前提交便于回溯。文档同步代码注释、硬件原理图、调试笔记、参数记录同步更新。备用方案准备一套纯STM32带DCMI接口和简单图像处理的备选方案以防FPGA调试不顺。提前演练按照正式比赛的时间流程进行几次完整的系统搭建、编程、调试练习掌握节奏。报告撰写设计报告是评分重点。图文并茂突出系统框架、创新点、算法思路、测试数据。将调试过程中解决的典型问题及思路写入报告能体现工程能力。从系统分析到模块实现再到联调优化完成一个电赛发挥部分题目是一个典型的软硬件协同工程项目。STM32与FPGA的搭配能充分发挥各自优势是解决复杂实时控制问题的利器。关键在于理解整个信号链明确各环节的输入输出并熟练使用工具进行调试。遇到问题时学会用逻辑分析仪、示波器等工具观察波形用“分而治之”的思想隔离问题。希望这篇长文能为你2026年的电赛征程提供一个扎实的参考框架祝你取得优异成绩