2024年电赛h题扩展版STM32智能车全代码逻辑逐块解析

)
简介:电赛微缩组小车,TB6612电机驱动,软件I2C读MPU6050,SysTick毫秒时基,蓝牙串口下发任务指令。全文按上电执行顺序逐模块拆解,分层架构,代码可折叠,附带参数调优实战经验。
整体三层架构总览
┌─────────────────────────────────────────────┐
│ 决策层 路线表T1~T8 + RunTask │ "走哪条路、先转几度"
│ 只做编排, 不碰硬件 │
├─────────────────────────────────────────────┤
│ 动作层 DoTurn 陀螺角度闭环(原地转) │ "一种运动怎么做"
│ DoGap 陀螺航向保持(走直+找线) │ 各有各的传感器
│ DoLine 循迹PD闭环(巡弧+找线) │ 和退出条件
├─────────────────────────────────────────────┤
│ 驱动层 Motor(出力) Tracking(看) MPU(感觉) │ "直接摸硬件"
│ Beep(说) Millis(钟) │
└─────────────────────────────────────────────┘
层间通信:全局标志(中断→主循环) / 返回值(动作→调度) / p·yaw(传感→动作)
第1块:main() 上电初始化【开机自检清单】
点击展开代码
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
Motor_Init();
Tracking_Init();
Beep_Init();
UART1_Init();
printf("\r\n=== Smart Car Ready ===\r\n");
MPU_Init();
Motor_Stop();
printf("GZ raw:%d (should jitter within ±80)\r\n", MPU_GZ());
代码功能讲解
一行一件事,上电按顺序执行,属于开机自检清单:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2):配置中断分组,2位抢占优先级+2位响应优先级。全局只配置一次,后续所有中断都遵循这套排队规则。Delay_Init():初始化SysTick,搭建1ms系统时基,必须最先初始化,后续所有延时函数都依赖它。Motor_Init():电机PWM外设初始化,初始占空比置0,上电电机不动作。Tracking_Init():8路红外循迹引脚配置为上拉输入,准备读取黑线信号。Beep_Init():蜂鸣器IO拉高,低电平触发蜂鸣器,上电先静音,防止上电瞬间误响。UART1_Init():蓝牙串口USART1,波特率9600,开启接收中断。printf打印就绪提示。MPU_Init():MPU6050初始化+0.3s零偏校准,校准阶段小车必须保持静止。Motor_Stop():电机停止。- 打印陀螺原始角速度自检值,读数在±80内抖动代表传感器工作正常。
初始化完成后进入
while(1)主循环。
第2块:Delay.c —— 全系统时间地基
点击展开代码
void Delay_Init(void)
{
SysTick->LOAD = 72000 - 1; /* 72MHz/72000 = 1kHz → 每1ms中断一次 */
SysTick->VAL = 0;
SysTick->CTRL = 0x00000007; /* 时钟源HCLK + 开中断 + 使能 */
}
void SysTick_Handler(void) /* 每1ms自动进入中断 */
{
g_ms++; /* 全局毫秒计数器自增 */
}
uint32_t Millis(void) { return g_ms; }
void Delay_ms(uint32_t t)
{
uint32_t s = g_ms;
while (g_ms - s < t); /* 阻塞延时,计数器回绕也安全 */
}
功能讲解
SysTick是STM32内置系统定时器,本项目配置为1ms触发一次中断,中断内部对全局变量g_ms自增,相当于给智能车装上一块毫秒级电子表。
Millis():读取当前系统毫秒时间,看电子表;Delay_ms():阻塞延时,盯着时间差值等待,死等指定毫秒。
全系统依赖场景
if (Millis() - f0 > FOUND_MS) /* DoGap: 见线持续50ms才算到达 */
if (Millis() - lost0 > LOST_MS) /* DoLine: 丢线135ms判定到达弧线终点 */
if ((int32_t)(Millis() - t0) > GAP_TIMEOUT_MS) /* 空白区超时兜底 */
if (Millis() - prt > 150) /* 打印限流,防止串口刷屏拖慢控制 */
第3块:usart.c —— 蓝牙命令入口
点击展开代码
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
void USART1_IRQHandler(void) /* 蓝牙每收到1字节,硬件自动进入中断 */
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t c = (uint8_t)USART_ReceiveData(USART1);
if (c >= '1' && c <= '8') { g_task = c - '0'; g_newtask = 1; }
else if (c == 'G' || c == 'g') g_start = 1;
else if (c == 'S' || c == 's') g_stop = 1;
else if (c == 'T' || c == 't') g_test ^= 1;
else if (c == 'C' || c == 'c') g_calib = 1;
}
}
功能讲解
fputc:重定向printf。C标准printf默认输出到电脑屏幕,这里重定向到USART1串口,所有printf输出会通过蓝牙发送到手机APP,作为小车的“输出嘴”。- 串口接收中断:手机蓝牙下发字符,硬件触发中断。>
核心原则:中断收信,主循环办事。中断内只置全局标志位,不执行复杂动作,保证中断快进快出(几十微秒),实际业务逻辑交给主循环处理。
'1'~'8':选择对应任务,g_newtask=1通知主循环;G/g:g_start=1,发车指令;S/s:g_stop=1,急停标志,任务函数循环内持续检测;T/t:调试打印开关翻转;C/c:触发MPU重新校准。
第4块:主循环 while(1) —— 事件分拣器
点击展开代码
while (1)
{
Yaw_Update(); /* ★每圈必做: 陀螺角度积分 */
if (g_newtask) { /* 门铃1: 选中新任务 */
uint8_t k; g_newtask = 0;
for (k = 0; k < g_task; k++) { Beep(90); Delay_ms(120); }
} /* 任务3就滴3声,确认任务选择 */
if (g_calib) { /* 门铃2: 请求重校准陀螺 */
g_calib = 0;
printf("Recalib:%s\r\n", MPU_Calib(300) ? "OK" : "FAIL");
}
if (g_test) { /* 门铃3: 调试打印开关 */
static uint32_t p = 0;
if (Millis() - p > 100) {
p = Millis();
printf("Y:%d GZ:%d W:%d\r\n", (int)Yaw(), MPU_GZ(), (int)MPU_Rate());
}
}
if (g_start && g_task) { /* 门铃4: 正式发车执行任务 */
g_start = 0;
RunTask(g_task);
g_stop = 0; /* 任务跑完清除急停标志,等待下一次发车 */
}
}
功能讲解
无限循环事件分拣台,不断轮询各类事件标志,有事件就处理。
重点:Yaw_Update()放在循环第一行,每一轮循环都执行陀螺角度积分,即使在任务间隙,航向角度也持续更新,保证陀螺角度数据准确。
第5块:路线表 —— 任务的“菜谱”
点击展开代码
#define SEG_GAP 0 /* 穿空白区段 */
#define SEG_LINE 1 /* 巡黑线弧段 */
typedef struct { uint8_t type; float turn; } Seg;
/* 段类型 进段前先转的度数(+右-左) */
/* 任务3: A→B→右弧→C→D→左弧→A, 车头朝正东摆放 */
static const Seg T3[] = { {SEG_GAP,0},{SEG_LINE,0},{SEG_GAP,0},{SEG_LINE,0} };
/* 任务4: B→斜穿D→左弧→A→直行B */
static const Seg T4[] = { {SEG_GAP,0},{SEG_LINE,+DIAG},{SEG_GAP,0} };
功能讲解
把不同赛道任务,抽象成动作序列表。每一段包含两个信息:区段类型(空白穿区/弧线巡线)、进入本段之前需要原地旋转的角度。
角度规则:正数向右旋转,负数向左旋转。
例:任务4斜线与弧线切线夹角≈38.7°,巡弧前原地右转+DIAG。
优势:修改赛道、新增任务,只修改路线表,底层动作函数无需改动。
第6块:RunTask —— 按菜谱炒菜,任务总指挥
点击展开代码
void RunTask(uint8_t t)
{
const Seg *seg = 0; int n = 0, lap, laps = 1, i;
switch (t) { /* 查表选择任务路线 */
case 3: seg = T3; n = 4; break;
case 5: seg = T5; n = 4; break;
case 6: seg = T6; n = 4; laps = 4; break; /* 多圈任务 */
...
}
Beep(300); /* 出发提示音 */
for (lap = 0; lap < laps; lap++) /* 外层循环:跑几圈 */
for (i = 0; i < n; i++) { /* 内层循环:依次执行每一段路线 */
if (g_stop) goto EXIT; /* 急停标志触发,直接跳出任务 */
DoTurn(seg[i].turn); /* ①原地转向 */
if (seg[i].type == SEG_GAP) {
if (!DoGap()) goto EXIT; /* ②空白区直行,失败直接退出 */
} else {
if (!DoLine()) goto EXIT; /* ②弧线PD巡线,失败直接退出 */
}
Beep(120); /* 到达该段终点,短滴提示 */
}
EXIT:
Motor_Brake(); Beep(300); ... /* 任务收尾:刹车+蜂鸣提示+打印完成信息 */
}
功能讲解
任务调度总函数,双层for循环遍历路线表。
动作原语返回值:1=本段执行成功,0=失败(超时/急停)。任意一段失败,使用goto跳转到EXIT统一收尾,保证小车任何情况都会刹车,不会失控乱跑。
第7块:DoTurn —— 陀螺闭环原地旋转
点击展开代码
static void DoTurn(float deg)
{
float rem; uint8_t spd;
if (deg > -0.5f && deg < 0.5f) return; /* 旋转角度极小,直接跳过 */
if (!MPU_OK()) { /* 陀螺异常,采用固定时间兜底 */
int8_t u = (int8_t)(deg / 45.0f); /* 45°约190ms,按比例估算时间 */
...Delay_ms(ms); Motor_Brake(); return;
}
Yaw_Zero(); /* 当前车头角度置0,作为本次旋转基准 */
if (deg > 0) Motor( TURN_PWM, -TURN_PWM); /* +: 左轮前进、右轮后退 → 原地右旋 */
else Motor(-TURN_PWM, TURN_PWM); /* -: 反向原地左旋 */
while (1) {
Yaw_Update(); /* 实时刷新当前转过角度 */
rem = fabs(deg) - fabs(Yaw()); /* 剩余需要旋转角度 */
if (rem <= 2.0f) break; /* 误差小于2°,判定到位 */
spd = (rem > 15.0f) ? TURN_PWM : 30; /* 大角度全速,接近目标降速,防止过冲 */
if (deg > 0) Motor( spd, -spd);
else Motor(-spd, spd);
if (g_stop) return;
}
Motor_Brake(); Delay_ms(60); /* 刹车,等待60ms稳定车身 */
}
功能讲解
原地角度闭环控制。设定目标旋转角度,电机旋转,陀螺实时反馈当前角度,不断修正电机输出。
- 每次调用
Yaw_Zero()重置角度基准,上一段的角度误差不会传递到下一段; - 接近目标自动降速,抑制超调;
- 误差阈值2°,到位刹车稳定60ms。
第8块:DoGap —— 航向保持,穿越空白区
点击展开代码
static uint8_t DoGap(void)
{
Yaw_Zero(); hdg = 0; /* 将当前车头方向锁定为基准航向 */
/* ---- 阶段1: 盲冲350ms,冲出顶点黑线区域 ---- */
while ((int32_t)(Millis() - t0) < ESCAPE_MS) {
if (g_stop) return 0;
Yaw_Update();
err = Yaw() - hdg + GAP_BIAS; /* 车头偏移角度 */
fix = (int)(2.0f * err); /* 角度误差放大得到修正量 */
if (fix > 20) fix = 20; /* 修正量限幅,防止大幅度猛打方向 */
if (fix < -20) fix = -20;
Motor(GAP_SPEED + GAP_TRIM - fix, GAP_SPEED + fix);
} /* 左轮(带右偏补偿) 右轮 */
/* ---- 阶段2: 继续保持航向 + 寻找黑线 ---- */
while ((int32_t)(Millis() - t0) < GAP_TIMEOUT_MS) {
/* 同上航向保持逻辑 */
if (Tracking_Bits()) { /* 任意探头检测到黑线 */
if (f0 == 0) f0 = Millis(); /* 记录第一次检测到黑线的时刻 */
if (Millis() - f0 > FOUND_MS) { /* 黑线持续存在满50ms */
Motor_Brake(); return 1; /* 判定到达顶点,本段成功 */
}
} else f0 = 0; /* 黑线消失,计时清零,重新检测 */
}
Motor_Brake(); Beep(600); return 0; /* 超时,本段执行失败 */
}
阶段1讲解
无黑线,依靠陀螺锁定航向走直线。GAP_TRIM用来补偿左右电机出厂转速不一致,消除小车自然跑偏。
车头右偏,误差err>0,fix>0,左轮减速、右轮加速,车头修正回基准方向。
阶段2讲解
FOUND_MS连续见线50ms防抖,过滤地面污渍、噪声造成的探头误触发,只有持续检测黑线才判定到达顶点。超时则任务失败。
第9块:DoLine —— PD巡弧线(核心循迹控制)
点击展开代码
p = Tracking_Pos(); /* 获取黑线位置:中心0,左负右正,丢线返回100 */
if (p > 90.0f) { /* ---- 岔路A: 丢失黑线 ---- */
if (lost0 == 0) lost0 = Millis(); /* 记录丢线起始时间 */
if (Millis() - lost0 > LOST_MS) { /* 持续丢线超过135ms */
Motor_Brake(); return 1; /* 判定冲出弧线末端,本段完成 */
}
s = (seen >= 0) ? 1 : -1; /* 记住黑线最后出现的方向 */
Motor(BASE_SPEED + 30*s, BASE_SPEED - 30*s); /* 朝丢失方向画弧继续前进搜线 */
}
else { /* ---- 岔路B: 正常检测到黑线,PD闭环控制 ---- */
lost0 = 0;
seen = p; /* 保存当前黑线位置,丢线时使用 */
st = KP * p + KD * (p - last); /* ★PD核心公式 */
last = p;
if (st > STEER_LIMIT) st = STEER_LIMIT; /* 舵量限幅 ±40 */
if (st < -STEER_LIMIT) st = -STEER_LIMIT;
Motor(BASE_SPEED + (int)st, BASE_SPEED - (int)st);
}
丢线逻辑讲解
丢线有两种可能性:临时脱离黑线,或者已经跑完弧线。利用持续丢线135ms作为弧线终点判定,不需要额外终点传感器。
丢线时,小车沿着黑线消失的方向画弧前进,而不是原地打转,提高找回黑线概率。
PD讲解
- 比例项 KP*p:黑线偏离越远,打舵力度越大,负责把车拉回黑线;
- 微分项 KD*(p-last):黑线位置变化速度,抑制惯性超调,防止来回S形振荡;
- 黑线在右侧 p>0 → st>0,左轮加速、右轮减速,车头右转追线。
第10块:tracking.c —— 巡迹的眼睛,8路红外探头
点击展开代码
static const uint16_t SEN_PIN[8] = { GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_10,
GPIO_Pin_11, GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_4, GPIO_Pin_5 };
static GPIO_TypeDef* const SEN_PORT[8] = { GPIOB,...,GPIOA,GPIOA };
uint8_t Tracking_Bits(void)
{
uint8_t b = 0, i;
for (i = 0; i < 8; i++) {
if (GPIO_ReadInputDataBit(SEN_PORT[i], SEN_PIN[i]) == LINE_LEVEL)
b |= (1 << i); /* 压线探头对应的bit置1 */
}
return b; /* 返回1字节位图,例:0x18=00011000,中间两个探头压线,车居中 */
}
float Tracking_Pos(void)
{
uint8_t b = Tracking_Bits();
float sum = 0;
uint8_t cnt = 0;
for (i = 0; i < 8; i++) {
if (b & (1 << i)) {
sum += i; cnt++;
}
}
if (cnt == 0) return 100.0f; /* 全白,丢线标记 */
if (cnt == 8) return 0.0f; /* 全黑,横线压顶,判定居中 */
return sum / cnt - 3.5f + CENTER_OFFSET;
}
功能讲解
- 两个数组建立探头编号到GPIO端口、引脚的映射。8个探头引脚分散在GPIOA、GPIOB,查表循环读取,不用重复写8次IO读取代码。
Tracking_Bits():读取8路探头状态,压缩为1字节位图,调试串口打印可以直观观察探头压线状态。Tracking_Pos()加权平均算法,把8路探头位图,换算成黑线偏移量,作为PD控制器输入。
- 探头3、4同时压线:
(3+4)/2=3.5,减3.5,偏移=0,黑线居中; - 仅探头6压线:
6-3.5=+2.5,黑线在右侧,需要右转; CENTER_OFFSET:零点偏移,用来调整平衡点,偏移值过大,小车会一直贴赛道外侧行驶。
第11块:mpu6050.c —— 航向感知器官 MPU6050
点击展开代码
static void I2C_Start(void){ SDA_OUT(); SDA_H(); SCL_H(); dly(); SDA_L(); dly(); SCL_L(); }
static uint8_t I2C_WB(uint8_t b){ /* 发送1字节,高位先行 */
uint8_t i;
for(i=0;i<8;i++){
if(b&0x80) SDA_H(); else SDA_L(); /* 准备电平 */
SCL_H(); dly(); SCL_L(); /* 时钟脉冲,从机读取bit */
b<<=1;
}
// 应答读取省略
}
void MPU_Init(void){
//GPIO配置开漏模式
WReg(0x6B,0x01); /* 退出睡眠模式,选择PLL时钟,稳定性优于内部RC */
WReg(0x1A,0x03); /* 低通滤波42Hz,滤除电机振动高频噪声 */
WReg(0x1B,0x08); /* 陀螺仪量程±500°/s,灵敏度65.5 LSB/(°/s) */
MPU_Calib(300); /* 静止采样300次,计算陀螺零偏 */
}
uint8_t MPU_Calib(uint16_t n){
int32_t s = 0; uint16_t i;
for(i=0;i<n;i++){ s += GZ_raw(); Delay_ms(1); }
gz_bias = s/(float)n; /* 静止状态下的固有偏移,保存零偏 */
yaw = 0;
return 1;
}
void Yaw_Update(void){
static uint32_t last_t = 0;
float dt = (Millis() - last_t) / 1000.0f; /* 距离上次积分的时间,单位秒 */
float w = YAW_SIGN * (GZ_raw() - gz_bias) / GYRO_LSB; /* 换算为°/s角速度 */
if (w > DEADBAND || w < -DEADBAND) /* 死区,小于阈值不积分 */
yaw += w * dt; /* ★角速度积分,得到总旋转角度 */
last_t = Millis();
}
功能讲解
软件模拟I2C时序,手动操作GPIO高低电平实现I2C通信。不占用硬件I2C外设,通信速度低,适合低频读取陀螺数据。
- 陀螺静止时读数不为0,存在固定零偏,上电静止采样求平均得到
gz_bias,后续每次读取角速度减去零偏。支持蓝牙C指令随时重校准。 - 陀螺核心积分逻辑:陀螺仪只能输出瞬时角速度,角度=角速度 × 时间积分。
- 死区:过滤静止时微小噪声,避免长时间静止角度漂移;
YAW_SIGN:符号翻转,适配芯片贴装方向,修正角度正负。
第12块:motor.c / beep.c —— 底层执行机构 TB6612+蜂鸣器
点击展开代码
void Motor(int l, int r)
{
if (l >= 0) { AIN1_H(); AIN2_L(); } /* 正转:方向脚10 */
else { AIN1_L(); AIN2_H(); l = -l; } /* 反转:方向脚01 */
// 右轮方向逻辑
if (r >= 0) { BIN1_H(); BIN2_L(); }
else { BIN1_L(); BIN2_H(); r = -r; }
if (l > 80) l = 80;
if (r > 80) r = 80;
TIM3->CCR2 = l; /* CCR寄存器值=PWM占空比,控制速度 */
TIM3->CCR1 = r;
}
void Motor_Brake(void) /* 电磁刹车 vs 断电滑行 */
{
AIN1_H(); AIN2_H(); BIN1_H(); BIN2_H(); /* TB6612两端同电平,电机短接制动 */
TIM3->CCR1 = 0; TIM3->CCR2 = 0;
}
void Beep(uint16_t ms)
{
uint32_t end = Millis() + ms;
LED_ON(); BUZZ_ON(); /* 低电平触发蜂鸣器 */
while (Millis() < end); /* 阻塞延时 */
BUZZ_OFF(); LED_OFF();
}
功能讲解
Motor函数:将左右轮速度指令翻译成TB6612驱动信号。
- 数值符号控制AIN1/AIN2方向引脚,决定电机正反转;
- 绝对值赋值PWM捕获比较寄存器,控制输出电压,调节转速;
- 限幅保护,最大占空比80。
区分:
Motor_Stop():断电滑行,依靠惯性前进;Motor_Brake():电机绕组短接,电磁制动,快速锁死车轮,所有动作终点使用。
蜂鸣器声光提示:任务选择滴对应次数、每段到达短滴、失败长鸣、任务结束双响。
完整运行流程示例:执行任务5
手机发'5' ──→ 串口中断置g_task=5,g_newtask=1
↓ 主循环: 滴5声确认任务选中
手机发'G' ──→ 串口中断置g_start=1
↓ 主循环触发RunTask(5)
查表T5: {GAP,0}{LINE,-DIAG}{GAP,-DIAG+5}{LINE,+DIAG-5}
│
├─ DoTurn(0) → 直接跳过
├─ DoGap() → Yaw_Zero锁航向,盲冲350ms,航向保持直行找线
│ → 连续见线50ms,判定到达C顶点,滴一声
├─ DoTurn(-40) → Yaw_Zero,左旋40°,误差≤2°刹车稳定
├─ DoLine() → Tracking_Pos读取黑线位置,PD闭环巡右弧
│ → 持续丢线满135ms,判定到达B顶点,滴一声
├─ DoTurn(-35) → 原地左旋35°
├─ DoGap() → 陀螺锁航向斜穿D区段
└─ DoTurn(+35), DoLine() → 巡弧线回到A点
EXIT: 电磁刹车+双响蜂鸣+打印任务完成
参数调优实战章节(电赛调试经验)
1. PD巡线参数 DoLine
- KP(比例系数):基础纠偏力度。
- KP太小:反应迟钝,车容易偏离赛道;
- KP太大:轻微偏移就大幅度打舵,车身剧烈抖动、振荡S形。>
推荐起步:KP=6~10,从小到大试。
- KD(微分系数):抑制惯性超调,防甩头。
- KD过小:过弯冲线、来回震荡;
- KD过大:控制僵硬,对弯道变化反应迟钝。>
推荐起步:KD=1~3。
STEER_LIMIT舵量上限:限制最大打舵,防止瞬间猛打翻车,一般±35~±40。
2. 空白区航向保持 DoGap
GAP_TRIM:电机转速差静态补偿,解决小车天然跑偏。>
车一直往左跑 → GAP_TRIM加大;一直往右跑 → 减小。
FOUND_MS=50ms:见线防抖时间。地面污渍多就加大到60~80;赛道干净可以降到30。GAP_TIMEOUT_MS空白区最大超时,防止卡死,一般5000~6000ms。
3. 弧线终点判定 DoLine
LOST_MS=135ms:持续丢线判定弧线结束。
- 数值偏大:出弧后跑太远才判定到达;
- 数值偏小:弯道中间短暂丢线直接误判终点。
4. 原地旋转 DoTurn
TURN_PWM:原地旋转PWM。PWM高转得快,但振动大,陀螺噪声增加;- 到位阈值
2°:要求高精度可以降到1.5°,但容易震荡反复修正;放宽到3°更稳。
5. 探头零点偏移 Tracking_Pos
CENTER_OFFSET:探头阵列平衡点偏移。
现象:车总是贴赛道外侧跑 →
CENTER_OFFSET绝对值太大,减小。
想让车骑在线内侧,增大偏移;居中行驶设置为0。
6. MPU陀螺相关
- 低通滤波:42Hz,电机震动大可以切到20Hz,代价是相位滞后;
- 死区
DEADBAND:过滤静止噪声,0.2~0.3°/s最合适; - 校准:上电必须静置;赛道震动大,比赛前蓝牙发送C指令重新校准。
文末小结
本智能车采用分层架构,决策层只负责路线编排,动作层封装独立运动原语(原地旋转、空白区直行、弧线PD巡线),驱动层负责硬件读写。
- 8路红外加权获取黑线偏移量,PD闭环巡弧线;
- MPU6050陀螺积分获取航向,实现无黑线时航向保持与原地精确转角;
- SysTick提供统一毫秒时基,串口蓝牙接收指令,中断置标志+主循环处理,保证系统稳定;
- 丢线超时、见线防抖、急停全局检测,多重安全兜底,防止小车失控。
适用场景:电赛智能车微缩组,赛道包含黑线弧线、空白穿越段。
更多推荐


所有评论(0)