CAR CONTROLSTM32F103C8T6 / KEIL PROJECT
BUILD OK · 0 ERR
Maze runner / line follower / RC car

参赛小车迷宫循迹控制系统

一台基于 STM32F103C8T6 的双轮差速小车:五路模拟循迹传感器经 ADC + DMA 连续采样,由加权偏差与 PD 控制完成自主循迹;状态机识别十字、虚线区、死胡同与终点,并支持蓝牙遥控接管。

5 ms control loop5× TCRT5000 analog1 kHz PWMUSART3 9600 bpsV52 firmware mark
11.7KBCode size
1.7KBRAM footprint
11State IDs
0ERRLast build
ADC1 · DMA circular
TIM2 · 1 kHz PWM
IWDG · ~1 s reset
STM32 F103C8T6 CORTEX-M3 · 72 MHz 5-CH SENSORADC1 + DMAPB0 · PA4–PA7 DUAL MOTORTIM2 CH3/CH4PA2 · PA3 OLED HUDsoft I²CPB8 · PB9 HC-05USART3PB10 · PB11 PID MIXERKp 20 / Kd 25 IWDGrescue watchdog
01 Hardware topology

板级连接 与执行器

所有映射均来自当前 Keil 工程的实际初始化代码。五路探头顺序、双电机方向脚、OLED 软 I²C 与蓝牙串口共用同一块 STM32F103C8T6。

STM32 引脚映射

active build
PB0ADC1_IN8第 1 路循迹 · 最左SENSOR
PA4ADC1_IN4第 2 路循迹SENSOR
PA5ADC1_IN5第 3 路循迹 · 中间SENSOR
PA6ADC1_IN6第 4 路循迹SENSOR
PA7ADC1_IN7第 5 路循迹 · 最右SENSOR
PA2TIM2_CH3左轮 PWM · 1 kHzMOTOR
PA3TIM2_CH4右轮 PWM · 1 kHzMOTOR
PA11GPIO OUT左轮方向 IN1DIR
PA12GPIO OUT左轮方向 IN2DIR
PB12GPIO OUT右轮方向 IN3DIR
PB13GPIO OUT右轮方向 IN4DIR
PB8GPIO ODOLED SCL · 软件 I²CDISPLAY
PB9GPIO ODOLED SDA · 地址 0x78DISPLAY
PB10USART3_TXHC-05 蓝牙发送UART
PB11USART3_RXHC-05 蓝牙接收 · 中断UART
注意:Car_Move() 内部启用了 MOTOR_LR_SWAP=1,因为两路驱动接线在硬件上对调;上层仍然用 L/R 表示实车左右轮。PB11 当前归 USART3_RX,旧 Key.c 也占用 PB11,因此不应加入当前构建。

整车功能布局

top view schematic
12345 STM32 BOARD F103C8T6 · 72 MHz OLED 128×64 L298N / DRIVER BATTERY PACK 7.4 V / 2S Li-ion BT MOTOR LMOTOR RFORWARD ↑
循迹传感器动力/驱动主控/显示电源

ADC1 + DMA

5 路 12 位模拟量循环扫描,DMA 后台写入 AD_Value[5],主循环直接取整帧。

TIM2 双路 PWM

72 MHz ÷ 720 ÷ 100,得到 1 kHz PWM;占空比范围 0–100,最小有效占空比 28。

OLED 128×64

PB8/PB9 软件 I²C,4 行 × 16 列 HUD 显示方向、偏差、左右输出与状态位图。

USART3 / HC-05

9600 8N1,RXNE 中断接收;字符 K 切换模式,其余指令进入遥控状态。

02 Keil build map

工程构建 框架

Keil uVision 工程按 Start / Library / System / Hardware / User 五个分组组织;最后一次 Rebuild 为 0 Error、0 Warning,并生成可直接烧录的 HEX。

Target 1 · 编译配置

Project.uvprojx
DeviceSTM32F103C8Cortex-M3 · 64 KB Flash / 20 KB SRAM
ToolchainARMCC 5.06 u7build 960 · D:\Keil5\ARM\ARMCC
Device packSTM32F1xx DFP2.2.0 · STMicroelectronics
PreprocessorUSE_STDPERIPH_DRIVERSTM32 Standard Peripheral Library
Include path5 个目录Start · Library · User · System · Hardware
OutputProject.axf + .hexObjects/ · Listings/

固件占用

from build.log
FLASH · Code + RO13,520 / 65,536 B · 20.6%
SRAM · RW + ZI1,712 / 20,480 B · 8.4%
Code11,710 B
RO data / RW / ZI1,810 / 64 / 1,648 B
01 / sources5 个分组.c · .h · .s
02 / compileARMCC 编译armcc -c
03 / assemble启动文件startup_stm32f10x_md.s
04 / link链接器Project.axf
05 / outputfromelfProject.hex

源码分组 · Group View

5 groups
Start
startup_stm32f10x_md.score_cm3.cstm32f10x.hsystem_stm32f10x.c
Library
misc.cstm32f10x_adc.cgpio.ctim.cusart.cdma.ciwdg.crcc.c+ SPL drivers
System
Delay.cDelay.h
Hardware
OLED.cOLED_Font.hMotor.cADC.cPID.cblue.c
User
main.cstm32f10x_it.cstm32f10x_conf.h

模块依赖图

call / link
main.cinit · loop · state machine OLEDHUD MotorPWM + DIR ADCDMA PIDerror → adjust blueUSART3 IRQ DelaySysTick SPL 3.5Library/*.c IWDGwatchdog Startstartup PID reads AD_Value
最后一次构建结果

Code=11710 · RO=1810 · RW=64 · ZI=1648;链接输出 .\Objects\Project.axf,随后由 fromelf 生成 HEX。编译日志显示 0 Error、0 Warning,耗时约 1 秒。

仅在目录中、未加入构建的文件

Key.c、LED.c、PWM.c 存在于 Hardware 文件夹,但当前 Project.uvprojx 未收录。Key 使用 PB11(与蓝牙 RX 冲突),LED 使用 PA2(与左轮 PWM 冲突),因此不能直接加入现有工程。

03 Control pipeline

循迹算法 与控制链路

从模拟电压到轮端 PWM 的每一次转换都可追溯到源码。控制周期固定 5 ms,误差以 5 路黑度加权求取,再经过 PD、速度调度与差速混合。

01 / SENSEADC + DMA5 路 12 bit 连续扫描
02 / NORMALIZE黑度归一化白 3370 → 黑 60
03 / LOCATE加权偏差 E权重 −2 … +2
04 / SCHEDULE速度分档直道 45 / 弯道 30
05 / CORRECTPD 补偿Kp 20 · Kd 25
06 / ACTUATE差速输出±45 → 限幅 ±100
SENSING

五路黑度与偏差

dark[i] = clamp((3370 − ad[i]) / (3370 − 60), 0, 1)
E = Σ(weight[i] × dark[i]) / Σdark[i]

权重 [-2,-1,0,1,2];总黑度 < 0.12 判为丢线,返回 999.0。线落在两探头之间时仍能得到 0.5 级连续误差。

FEEDBACK

位置式 PD

I = clamp(I + E, −90, 90)
D = E − E_prev
U = Kp·E + Ki·I + Kd·D
U = clamp(U, −45, 45)

当前参数 Kp=20.0, Ki=0.0, Kd=25.0,实际表现为 PD;丢线时清空积分与上一拍误差,避免重新压线时猛打方向。

MIXING

差速混合与死区

base = |E| < 0.5 ? 45 : 30
if |E| < 0.3 → U = 0
L = base + U
R = base − U

刚过弯后 corner_hold=50 拍维持 40 低速;中心 0.3 死区抑制 S 弯交叉点附近的“画龙”摆动。

LIMITER

极限差速与轮端

if |E| > 1.5 → (70, −30)
nonzero |v| < 30 → sign(v)·30
Car_Move(L, R) → PWM 0…100

最外侧探头压线时跳过 PID 直接打死;MOTOR_LR_SWAP=1 在 Car_Move 入口交换两路,以补偿实车接线反接。

04 Interactive model

五路循迹 实时仿真

拖动黑线位置,观察 AD 黑度、加权误差、PD 补偿与轮端输出如何变化。模型复现源码中的阈值、死区、极限差速与最低速度限制。

Sensor / control visualizer

TRACK · 5 ms
E ≈ +0.71

Telemetry

computed from source formulas
Error E+0.71
PD adjust+41.8
Base speed30
Sensor blackness / AD1 L ← → R 5
3350
S1
3300
S2
2300
S3
600
S4
3370
S5
LEFT WHEEL72
RIGHT WHEEL-12
E / U historyE · U

说明:传感器响应采用随距离线性衰减的可视化近似;黑度归一化、加权误差、PD、45 补偿限幅、30 最低速度、70/−30 极限差速等控制量按源码规则计算。

05 Behaviour engine

迷宫状态机 与脱困策略

主循环每 5 ms 进入一次状态机。正常循迹之外,还包含死胡同掉头、终点锁停、丢线搜索、倒车脱困与卡死自救。

Active state graph

click a node
dead-endline foundafter dashedlost > 600 msretryline found6 tries2 flipsline found3 back tries3 s timeoutstuck 3 s ST_TRACKPID line follow ST_UTURNdead-end U-turn ST_STOPfinish latch ST_PAUSEpause then acquire ST_ACQcreep + micro-turn ST_SEARCHalternating scan ST_BACKreverse escape ST_SPINstuck 360° rescue
+

十字 / 三路压黑

≥3 路黑视为路口,强制直行 60×5ms=300ms 冲过去;随后冷却 1s,防止同一路口重复触发。

↩

死胡同

位图 mb & 0x1B == 0x1B(两侧各两路黑)时停车 200 ms,再原地右转,最长 3 s;中间探头压线即恢复循迹。

⋯

虚线区

“黑 → 短白 → 黑”连续 3 次,且白间隙持续 10–400 ms,判定进入虚线区;长白会清零计数。

■

T 字终点

进入虚线区后,五路全黑持续 100ms 才锁停;ST_STOP 永久停车,等待重新上电或模式切换。

◎

丢线 / 卡死

丢线依次尝试直行、按方向打死、原地慢转、停车找线;传感器 3 s 无变化则原地转 360°,左右各一次。

当前编译配置下休眠:JUNC_LEFT_RULE=0 与 AUTO_TURN90=0,所以 ST_JWAIT → ST_JADV → ST_TURN 这套“路口一律左转 / 定时 90°”路径仍然编译保留,但不会被当前主流程触发。页面以实际可达状态为主。
06 Remote & HUD

蓝牙遥控 与 OLED 仪表

比赛时迷宫区禁止遥控;蓝牙模式作为调试与资源区接管通道。USART3 中断接收单字符指令,500 ms 无新指令自动停车。

Command pad

click to preview
MODE MAZELAST —AUTO STOP 500 ms
REMOTE HOLD TIMER50 / 50 ticks

每次收到有效指令重置 REMOTE_TIMEOUT_CNT=50;倒计时归零后 motor_cmd='S',防止断连后车辆继续行驶。

按键为页面内交互预览,不会向真实小车下发蓝牙数据。

OLED HUD preview

128 × 64 / 4 × 16
Forward E:+07 A:+41 Maze L: 72 R:-12 B:00100 N:1 D:0 |
SSD1306 · 0x78PB8 SCL / PB9 SDA
四行信息布局

第 1 行运动方向;第 2 行误差 / PD 补偿 / 当前模式;第 3 行左右轮输出;第 4 行五路压黑位图、黑线数、虚线区标志与心跳字符。刷新周期 50ms。

07 Tuning reference

关键参数 速查表

以下数值直接取自当前源码与 Keil 工程,便于现场调车时快速定位。BASE_SPEED 等旧宏仍存在,但实际循迹由动态 base 覆盖。

循迹控制

PID.c / main.c
比例系数 kp20.0
积分系数 ki0.0
微分系数 kd25.0
白底 ADC 参考3370
黑线 ADC 参考60
丢线总黑度门限0.12
PID 补偿限幅±45
中心死区0.3
直道 / 弯道基础速度45 / 30
极限差速+70 / −30
误差范围 −2 … +2;丢线返回 999.0。非零输出统一抬到最低 30。

迷宫事件

main.c
主循环周期5 ms
冲十字直行300 ms
路口冷却1 s
死胡同停车200 ms
掉头最长3 s
掉头速度±50
虚线短白窗口10–400 ms
虚线交替次数3 次
终点全黑保持100 ms
卡死检测3 s
终点判据为 is_after_dashed && mb == 0x1F;FINISH_BLACK_MIN 宏存在但未参与最终判断。

外设与通信

ADC / Motor / blue
MCU 主频72 MHz
ADC 时钟12 MHz
ADC 分辨率 / 通道12 bit / 5
PWM 频率1 kHz
PWM 最小占空比28
蓝牙波特率9600 8N1
遥控超时50 ticks
遥控速度 F/B±45
遥控转向 L/R±60
冲刺 y/t±100
IWDG:64 分频、重装值 625,约 1 s 无喂狗则复位。

Source map

User/main.c1209 行 · 主循环 / 状态机 / 模式切换
Hardware/PID.c黑度加权 / 偏差 / PD 补偿
Hardware/Motor.c双轮 PWM / 方向 / 最小占空比
Hardware/ADC.cADC1 + DMA 五通道采样
Hardware/blue.cUSART3 中断 / 指令解析
Hardware/OLED.c软件 I²C / 128×64 HUD
Project.uvprojxTarget 1 · 分组 / 工具链 / 输出
build.log0 Error · 0 Warning · 内存占用