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.cHAL 初始化、应用初始化、主循环调度
外设初始化Core/Src/usart.cgpio.cdma.cadc.ci2c.cCubeMX 生成的底层初始化
应用层my_app/用户业务模块集合
PID 算法my_app/PID/pid.cpid.h位置式/增量式 PID、限幅、积分限幅
PID 应用my_app/PID/pid_app.cX/Y 角度环控制输出
电机模块my_app/Emm_V5.cEmm_V5_APP.cEmm_V5 电机/驱动器通信与动作封装
灰度模块gray_Nomcu_app.*No_Mcu_Ganv_Grayscale_Sensor.*无 MCU 灰度传感器读取与标定
姿态模块jy901s.*jy901s_app.*JY901S 姿态数据解析与应用
显示模块oled.*oled_app.*OLED_Font.hOLED 驱动与显示应用
串口应用usart_app.*Module/ringbuffer.*UART DMA 接收、环形缓冲区、协议解析入口
调度器schedule.cschedule.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_Task10 ms电机/驱动器相关周期处理
uart_task10 ms串口缓冲区与协议处理
Virson_PID_control1 msX/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_xkp=1.80, ki=0.0034, kd=2.50, out=±999virson_x_pid_output
pid_virson_ykp=1.83, ki=0.0034, kd=2.60, out=±999virson_y_pid_output

当前输入变量为 X_axis_inputY_axis_input,计算前乘以 0.3。后续要补充这个比例系数的来源:是像素偏差、角度、视觉坐标,还是人为标定比例。

电机与传感器模块

模块当前判断后续需要补全
Emm_V5电机/驱动器通信与控制封装命令帧格式、地址、速度/位置单位、反馈格式
gray_Nomcu灰度传感器读取与归一化黑白标定流程、各通道物理位置、循迹误差计算
jy901s姿态传感器解析数据帧格式、Yaw/Pitch/Roll 更新周期、异常处理
oledOLED 驱动与显示应用显示页面布局、刷新周期、调试字段
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 的数据流。