04 PID 小车 ST 工程
项目定位
这是一个基于 STM32F1 HAL / CubeMX 的 PID 小车工程,原始资料位于 raw/projects/pid-car-st-virson-zdt。项目包含电机控制、PID 算法、灰度传感器、JY901S 姿态传感器、OLED 显示、UART DMA 接收、简单周期调度器等模块。
当前 Wiki 将它整理为“STM32 小车控制工程”的主入口,后续可以继续拆成电机协议、PID 控制、灰度循迹、串口通信、姿态传感器、OLED 显示等专题页。
原始资料位置
| 类别 | 路径 | 用途 |
|---|---|---|
| CubeMX 配置 | car.ioc、.mxproject | 芯片、外设、引脚与工程配置 |
| 启动主线 | Core/Src/main.c | HAL 初始化、应用初始化、主循环调度 |
| 外设初始化 | Core/Src/usart.c、gpio.c、dma.c、adc.c、i2c.c | CubeMX 生成的底层初始化 |
| 应用层 | my_app/ | 用户业务模块集合 |
| PID 算法 | my_app/PID/pid.c、pid.h | 位置式/增量式 PID、限幅、积分限幅 |
| PID 应用 | my_app/PID/pid_app.c | X/Y 角度环控制输出 |
| 电机模块 | my_app/Emm_V5.c、Emm_V5_APP.c | Emm_V5 电机/驱动器通信与动作封装 |
| 灰度模块 | gray_Nomcu_app.*、No_Mcu_Ganv_Grayscale_Sensor.* | 无 MCU 灰度传感器读取与标定 |
| 姿态模块 | jy901s.*、jy901s_app.* | JY901S 姿态数据解析与应用 |
| 显示模块 | oled.*、oled_app.*、OLED_Font.h | OLED 驱动与显示应用 |
| 串口应用 | usart_app.*、Module/ringbuffer.* | UART DMA 接收、环形缓冲区、协议解析入口 |
| 调度器 | schedule.c、schedule.h | 周期任务调度 |
工程结构
pid-car-st-virson-zdt/
├─ Core/ CubeMX 生成代码,含 main.c 与外设初始化
├─ Drivers/ STM32 HAL / CMSIS 驱动库
├─ MDK-ARM/ Keil 工程文件
├─ my_app/ 用户应用层
│ ├─ PID/ PID 算法与 PID 应用封装
│ ├─ Module/ringbuffer.* 环形缓冲区
│ ├─ Emm_V5* 电机驱动/运动控制相关模块
│ ├─ gray_Nomcu* 灰度传感器应用层
│ ├─ jy901s* 姿态传感器解析与应用
│ ├─ oled* OLED 显示模块
│ ├─ schedule.* 简单周期调度器
│ └─ usart_app.* 串口应用层
└─ car.ioc CubeMX 配置启动与运行主线
Core/Src/main.c 的主线可以概括为:
HAL_Init
-> SystemClock_Config
-> MX_GPIO_Init
-> MX_DMA_Init
-> MX_RTC_Init
-> MX_USART1_UART_Init
-> MX_USART2_UART_Init
-> MX_I2C2_Init
-> MX_ADC1_Init
-> 初始化 UART 环形缓冲区
-> HAL_UARTEx_ReceiveToIdle_DMA 启动 UART DMA 接收
-> PID_Init
-> OLED_Init
-> schedule_init
-> while(1): schedule_run关键点:USART1 和 USART2 都按 DMA + IDLE 接收方式组织,适合接收上位机、传感器或电机反馈数据。主循环不直接堆业务逻辑,而是交给 schedule_run() 轮询周期任务。
周期任务
schedule.c 当前注册了 3 个周期任务:
| 任务 | 周期 | 作用 |
|---|---|---|
Emm_V5_Task | 10 ms | 电机/驱动器相关周期处理 |
uart_task | 10 ms | 串口缓冲区与协议处理 |
Virson_PID_control | 1 ms | X/Y 角度环 PID 控制 |
注意:schedule_run() 依赖 HAL_GetTick(),系统 tick 通常为 1 ms。Virson_PID_control 标为 1 ms 周期,后续需要确认该函数实际耗时、串口任务耗时和主循环负载是否满足 1 ms 调度要求。
PID 控制结构
my_app/PID/pid.h 定义 PID_T,包含:
kp / ki / kd:PID 参数。target / current / out:目标值、当前值、输出值。limit:输出限幅。error / last_error / last2_error:误差历史。integral:积分累积。p_out / i_out / d_out:各分量输出。
公开接口包括:
| 接口 | 作用 |
|---|---|
pid_init | 初始化 PID 参数、目标和限幅 |
pid_set_target | 设置目标值 |
pid_set_params | 设置 kp/ki/kd |
pid_set_limit | 设置输出限幅 |
pid_reset | 重置控制器状态 |
pid_calculate_positional | 位置式 PID 计算 |
pid_calculate_incremental | 增量式 PID 计算 |
pid_constrain | 数值限幅 |
pid_app_limit_integral | 积分限幅 |
pid_app.c 当前有两个角度/视觉轴控制器:
| 控制器 | 参数 | 输出 |
|---|---|---|
pid_virson_x | kp=1.80, ki=0.0034, kd=2.50, out=±999 | virson_x_pid_output |
pid_virson_y | kp=1.83, ki=0.0034, kd=2.60, out=±999 | virson_y_pid_output |
当前输入变量为 X_axis_input、Y_axis_input,计算前乘以 0.3。后续要补充这个比例系数的来源:是像素偏差、角度、视觉坐标,还是人为标定比例。
电机与传感器模块
| 模块 | 当前判断 | 后续需要补全 |
|---|---|---|
Emm_V5 | 电机/驱动器通信与控制封装 | 命令帧格式、地址、速度/位置单位、反馈格式 |
gray_Nomcu | 灰度传感器读取与归一化 | 黑白标定流程、各通道物理位置、循迹误差计算 |
jy901s | 姿态传感器解析 | 数据帧格式、Yaw/Pitch/Roll 更新周期、异常处理 |
oled | OLED 驱动与显示应用 | 显示页面布局、刷新周期、调试字段 |
usart_app | 串口应用层 | UART1/UART2 分工、协议格式、DMA 回调链路 |
ringbuffer | 环形缓冲区 | 溢出策略、读写并发边界、最大帧长度 |
当前风险点
Virson_PID_control的输入来源还没有在本页完全追踪到,需要继续确认X_axis_input/Y_axis_input由视觉、灰度还是串口写入。- 1 ms PID 周期对阻塞式串口发送、OLED 刷新、传感器解析都比较敏感,需要测量主循环耗时。
- 电机控制输出
±999的物理含义需要明确,是 PWM、速度、位置还是驱动器协议内部单位。 - UART1/UART2 的外设分工需要补充接线表,否则后续调试容易混淆。
- 部分源码注释存在历史编码问题,Wiki 应以代码结构和变量名为准,中文注释需要逐步修复。
后续补全计划
- 拆出
STM32 小车启动流程:根据main.c和 CubeMX 外设配置画初始化流程图。 - 拆出
Emm_V5 电机驱动协议:整理所有Emm_V5_*接口、命令帧和参数单位。 - 拆出
STM32 PID 控制链路:追踪X_axis_input/Y_axis_input -> PID -> 电机输出。 - 拆出
灰度循迹模块:整理传感器标定、通道权重、误差计算。 - 拆出
UART DMA 接收框架:说明 ReceiveToIdle DMA、ringbuffer、uart_task的数据流。