简介:一套基于STM32单片机的智能遥控循迹避障灭火小车完整工程,采用标准外设库与Keil开发环境搭建,适合嵌入式初学者、电子设计爱好者及智能车竞赛备赛者。项目以STM32F10x系列为控制核心,覆盖红外循迹、超声波避障、无线遥控、灭火执行四大功能,代码中包含主程序、外设驱动、中断服务、电机调速与辅助工具,可帮助读者理解多传感器数据融合、PWM控制及软硬件联调的整体思路。压缩包内共一百九十七个文件,总大小约六点九兆字节,主要文件类型包括C源文件、H头文件、O编译目标文件、uvprojx工程文件以及axf可执行文件,并附有启动汇编与调试映射文件,便于直接打开工程编译下载或二次修改。目前已有四百四十六人浏览学习,整体体积小且结构清晰,适合作为STM32综合项目参考和智能小车开发的起点。
1. 基于stm32单片机的智能遥控循迹避障灭火小车:一次综合题里的分水岭
基于stm32单片机的智能遥控循迹避障灭火小车,是很多智能车课程设计和竞赛里最常碰到的综合题。四个功能单独拆开都不难:遥控、循迹、避障、灭火,每一样都有现成模块;难点在合体之后——传感器阈值互相干扰、电源被电机拉垮、几套逻辑同时抢控制权。能把这台车稳定跑完一条赛道的,和停在原地打转的,差距往往不在代码量,而在决策顺序和参数标定。
这篇文章不贴某份神秘源码包,而是把一套可复现的落地方法讲清楚:硬件怎么配、引脚怎么分、CubeMX怎么初始化、每个模块的代码骨架怎么写、调参看什么数据,最后是五个实际踩过的坑。新手能照着步骤从零搭;有基础的开发者,重点看第4章的优先级仲裁和第5章的排查思路。整台车做下来,收获最大的不是“车会跑了”,而是学会怎么让多个子系统在一个主循环里不打架。
2. 系统架构与硬件选型:为什么这辆小车建议用STM32而不是51或Arduino
2.1 负载拆解与主控选型:四路传感器加两路电机到底有多重
先数一下这辆车要同时伺候的外设:4路循迹传感器、1个超声波避障模块、1个火焰传感器、1个舵机、2路直流电机、1个灭火风扇、1个遥控串口。如果循迹用模拟量输出,还需要至少4通道ADC;电机和舵机至少需要3路PWM;超声波回波需要微秒级计时;遥控串口要中断接收。这一堆需求放在51单片机上是能跑的,但代价是主循环被占满,中断到处打架,调一个参数要重新烧一次程序。
STM32F103C8T6这类芯片跑72MHz,内置ADC、多个通用定时器、多路串口,外设资源刚好覆盖需求且留有余量。我用它的理由可以总结为三点:ADC采样和PWM输出都由硬件外设完成,CPU只在需要时读结果;定时器资源足够让舵机和电机分开用不同频率的PWM;串口中断配合环形缓冲区,遥控数据不容易丢字节。对比Arduino的话,它会更快能跑,但PWM通道数往往不够三个,而且调底层时序时能做的有限,更适合验证想法而不是做综合题。
这里更推荐“STM32最小系统板 + 模块化传感器”的组合,不推荐自己画板子。焊接和排障的成本远高于板间杜邦线的成本,尤其是舵机和电机驱动的大电流,板子画不好就是冒烟现场。
2.2 硬件搭配与安装方向:循迹、避障、火焰、风扇各自的规矩
循迹部分,我用4路TCRT5000红外对管模块。为什么不用2路?2路在直线和缓弯还好,遇到十字或T字路口时完全不知道车在哪。4路可以判断“正中、微偏、大偏、路口”四类情形,配合PID输出转向修正量。模块上有数字输出和模拟输出两个引脚,我建议用数字输出接GPIO,虽然阈值靠电位器调,但只要调对了,稳定性比代码里做ADC阈值更抗漂移。
超声波避障选HC-SR04,装车头正前方,最好加一个小舵机做云台。固定不转的问题在于:小车循迹时遇到侧面障碍,超声波看不到,车就直接撞上去或卡住。舵机左右各转45度,能扫出一个扇形区域,避障策略可以从“闷头转弯”升级成“看哪边空往哪边走”。
火焰传感器要装在车头偏下位置,感光面斜向下20~30度,这样它看到的是车前地面的火源,而不是远方的窗户和灯泡。灭火执行器用直流吸尘风扇比水泵轻,安装也更简单。风扇不是直接焊在单片机上,而是通过一个MOS管或继电器模块控制,STM32引脚只输出开关信号。
2.3 供电和电机驱动的坑:没共地,调试直接进入玄学阶段
供电是这台车翻车率最高的地方。我采用的方案是2节18650锂电池串联(约7.4V)或一块3S锂电池(11.1V),电池正极先进电机驱动模块,由模块给电机供电。逻辑部分单独从电池接一个降压模块(LM2596S-5.0)输出5V,再用AMS1117-3.3降到3.3V给STM32和传感器。
舵机和风扇的电流不能忽视。舵机堵转时能到1A以上,风扇启动瞬间也有大电流,如果它们和单片机共用同一个3.3V稳压器,大概率会出现“一打舵单片机就复位”的灵异现象。正确的做法是:舵机接5V降压模块的输出端,并在舵机电源脚并两个电容(470μF电解电容加0.1μF陶瓷电容),风扇也不要直接从3.3V取电。
还有一根命脉:共地。电机驱动模块的地、STM32的地、传感器模块的地必须连在一起,否则传感器参考电压会随电机电流波动,读到的数据全是噪声。这块如果不接,后面所有调试都在碰运气,属于典型的“看起来是软件问题,实际是硬件没共地”。
2.4 一张表定完引脚分配
这是我在实际项目里用过的一套引脚分配,可以照抄:
| 功能模块 | 引脚 | 说明 |
|---|---|---|
| 循迹传感器 L1/L2/R2/R1 | PB0/PB1/PB2/PB3 | 数字量输入,GPIO |
| 火焰传感器 模拟量 | PA7 | ADC1_IN7 |
| 火焰传感器 数字量 | PB4 | 备用,GPIO输入 |
| 超声波 Trig | PA4 | GPIO输出 |
| 超声波 Echo | PA5 | GPIO输入,配合定时器计宽 |
| 舵机 PWM | PA6 | TIM3_CH1,50Hz |
| 左电机 PWM | PB6 | TIM4_CH1,10kHz |
| 右电机 PWM | PB7 | TIM4_CH2,10kHz |
| 左电机方向 IN1/IN2 | PB8/PB9 | GPIO输出 |
| 右电机方向 IN3/IN4 | PB12/PB13 | GPIO输出 |
| 灭火风扇控制 | PB14 | GPIO输出,驱动MOS管 |
| 调试串口 TX/RX | PA9/PA10 | USART1,115200 |
| 遥控串口 TX/RX | PA2/PA3 | USART2,9600或115200 |
这里面有个关键设计:舵机和电机用的是不同定时器,这样舵机可以工作在50Hz,电机可以工作在10kHz,互不影响。循迹传感器数字量只用普通GPIO,节省下来的ADC通道留给火焰传感器模拟量,未来想加电池电压检测也有位置。
3. CubeMX初始化、电机PWM与串口调试:先让车“会动、可看”
3.1 CubeMX引脚配置和时钟要点
先用STM32CubeMX建工程,芯片选STM32F103C8T6。时钟配置为外部8MHz晶振,PLL倍频到72MHz。这一步不做,后面ADC和定时器的时序全是错的。
引脚按第2章的表格设置:PB0~PB3、PB4、PB5配成GPIO输入;PB8、PB9、PB12、PB13、PB14、PA4配成GPIO输出;PA6配TIM3_CH1;PB6、PB7配TIM4_CH1和TIM4_CH2;PA9/PA10配USART1;PA2/PA3配USART2;PA7配ADC1_IN7。需要注意的是,PB5(Echo)要开启外部中断,并且选择上升沿和下降沿双边沿触发,这样才能在中断里记录超声波回波的起止时间。
定时器配置有三个:TIM2做微秒计数器,PSC设为71,ARR设为最大值65535,向上计数,溢出中断不用管,只读CNT;TIM3做舵机PWM,PSC设为71,ARR设为19999,这样计数频率1MHz、周期20ms,正好是50Hz;TIM4做电机PWM,PSC设为71,ARR设为99,计数频率1MHz、周期100us,PWM输出频率10kHz。
ADC配置成单通道扫描,采样时间选最长档(239.5周期),因为火焰传感器输出阻抗较高,采样时间太短电压建立不起来,读数会偏低。
3.2 双路电机驱动:PWM频率怎么选,正反转逻辑怎么写
电机驱动模块用TB6612,比L298N的压降小很多,发热也小。用HAL库输出PWM的代码骨架如下:
/* 初始化后启动四路PWM通道 */ HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 舵机 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); // 左电机 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); // 右电机 /* 舵机打角:占空比=脉宽/20000us=1500/20000=0.075 */ __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 1500); /* 左电机占空比50%,正转 */ __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 500); HAL_GPIO_WritePin(MOTOR_LEFT_IN1_GPIO_Port, MOTOR_LEFT_IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(MOTOR_LEFT_IN2_GPIO_Port, MOTOR_LEFT_IN2_Pin, GPIO_PIN_RESET);逻辑说明:PWM启动后比较寄存器决定占空比,方向由IN1/IN2引脚组合决定。左电机IN1=1、IN2=0是正转,反过来是反转,两个引脚都是0会刹停,两个都是1会处于刹车状态。占空比500对应50%,具体值要根据电池电压和路面情况调整,但不要直接满占空比跑直线,转向时会很难控制。
参数说明:电机PWM频率选10kHz而不是1kHz,因为频率低于1kHz时电机会明显啸叫,且速度波动大。舵机脉宽1500us是中位,约900us是左极限,2100us是右极限。每次修改占空比后,舵机需要一个从当前值渐变到目标值的过程,直接跳变会让舵机猛烈甩头,容易把机械结构打坏。
3.3 串口调试与遥控解析:没有日志就别谈断言
调试阶段最痛苦的场景是:车不动,但不知道代码卡在哪个循环,还是传感器根本没触发。我在工程里会把USART1的printf重定向到调试串口,跑主循环时周期性打印关键变量。重定向代码每个STM32工程几乎一样:
int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100); return ch; }然后在主循环里打印当前状态、循迹传感器电平、超声波距离、火焰ADC值、电机占空比,基本就能定位大部分问题。遥控模块统一走USART2,接收中断里把数据压进环形缓冲区,主循环再解析协议帧。不要在主循环里用阻塞式HAL_UART_Receive,这样遥控响应会卡顿,尤其电机PWM占空比高时,中断被串口阻塞的概率非常大。
协议帧我习惯用最简单的帧头+数据+校验格式:0xAA开头,第二个字节是方向键值,第三个字节是帧尾0x55。主循环里每20ms读一次缓冲区,解析到有效键值就更新目标速度和方向。需要特别留意的是遥控模块上电后会有一串乱码,代码里要对无效帧做容错,别让乱码冲掉正常控制。
4. 循迹、避障、火焰检测三个子系统的实现与参数设定
4.1 循迹:先标定阈值,再谈逻辑
循迹模块的数字输出在黑白线上分别输出0和1,但“哪边是0哪边是1”取决于模块上的电位器和地面颜色。安装好后先别写控制算法,而是打开调试串口,把车分别放在白底和黑线上,观察四个GPIO读到的电平,然后用小螺丝刀调电位器,确保白色地面四路都是0、黑色线路四路都是1。
读取状态和转向决策的代码骨架如下:
uint8_t track_state_read(void) { uint8_t s = 0; if (HAL_GPIO_ReadPin(TRACK_L1_GPIO_Port, TRACK_L1_Pin)) s |= 0x08; // 最左 if (HAL_GPIO_ReadPin(TRACK_L2_GPIO_Port, TRACK_L2_Pin)) s |= 0x04; // 左中 if (HAL_GPIO_ReadPin(TRACK_R2_GPIO_Port, TRACK_R2_Pin)) s |= 0x02; // 右中 if (HAL_GPIO_ReadPin(TRACK_R1_GPIO_Port, TRACK_R1_Pin)) s |= 0x01; // 最右 return s; } void track_control(void) { uint8_t s = track_state_read(); switch (s) { case 0x06: /* 中间两路压线,直行 */ set_left_pwm(300); set_right_pwm(300); break; case 0x04: /* 左中压线,车偏右 */ set_left_pwm(400); set_right_pwm(200); break; case 0x02: /* 右中压线,车偏左 */ set_left_pwm(200); set_right_pwm(400); break; case 0x0E: /* 大偏,快速修正 */ set_left_pwm(500); set_right_pwm(100); break; case 0x03: /* 大偏,快速修正 */ set_left_pwm(100); set_right_pwm(500); break; case 0x00: /* 全丢线,保持上一方向 */ set_left_pwm(300); set_right_pwm(300); break; default: break; } }代码逻辑是典型的“四路查表法”:中间两路命中表示在线,哪边命中减速哪边;两边同时命中表示十字路口。参数上的重点是左右PWM差值不要一开始就拉太大,白底黑线场地、轮胎不打滑的条件下,差值200足够转出明显弧线;差值太大小车会在黑线上左右狂摆,时间全花在修正上。
循迹如果要做速度闭环,还需要给电机加编码器,这里先不做,原因是题目核心是功能完整度,PID速度环属于进阶优化,放在第6章提。
4.2 避障:超声波时序、超时保护与左右策略
HC-SR04的测距时序很简单:Trig拉高至少10us,模块内部发出8个40kHz脉冲,Echo输出高电平,高电平时间就是声波往返时间。距离厘米数 = 高电平时间us ÷ 58。这里最大的坑是“没有回波时Echo一直高”,如果代码死等,整个主循环就卡死了。
我做了一个带超时保护的测距函数:
uint16_t sonar_get_distance(void) { uint16_t timeout = 0; __HAL_TIM_SET_COUNTER(&htim2, 0); HAL_GPIO_WritePin(SONAR_TRIG_GPIO_Port, SONAR_TRIG_Pin, GPIO_PIN_SET); delay_us(15); HAL_GPIO_WritePin(SONAR_TRIG_GPIO_Port, SONAR_TRIG_Pin, GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(SONAR_ECHO_GPIO_Port, SONAR_ECHO_Pin) == GPIO_PIN_RESET) { if (++timeout > 500) return 999; /* 模块没响应 */ } timeout = 0; uint32_t t0 = __HAL_TIM_GET_COUNTER(&htim2); while (HAL_GPIO_ReadPin(SONAR_ECHO_GPIO_Port, SONAR_ECHO_Pin) == GPIO_PIN_SET) { if (++timeout > 30000) return 998; /* 回波持续太久,视为无效 */ } uint32_t dt = __HAL_TIM_GET_COUNTER(&htim2) - t0; return dt / 58; }逻辑说明:第一次while等待Echo从低变高,如果模块坏了或没接好,这里就是死循环,所以加超时。第二次while等待Echo变低,记录高电平时长;dt单位是微秒,除以58得到厘米。返回999或998后,上层逻辑应当把“障碍距离”当作安全值处理,而不是当作前方无障碍。
避障策略我建议按距离分三档,而不是一个阈值走天下:小于15cm时原地停车,舵机左右扫描,比较左右两边的距离值,选择空旷一侧转向并前进;15到25cm时减速并朝障碍物反方向转;大于25cm时恢复循迹。舵机扫描时注意给它留足转动时间,每次打完角后至少等100ms再测距,否则测到的是舵机还没停稳时的误导数据。
4.3 火焰检测与灭火:防误报阈值和风扇动作
火焰传感器模块同时有模拟量输出和数字量输出。我用模拟量接PA7,通过ADC读取,数字量只做备用。ADC读数不是“0或1”,而是随火焰距离变化的值,因此阈值设定要有“触发阈值”和“退出阈值”两个值,避免火焰在小幅度波动时风扇反复启停。
关键代码如下:
uint16_t fire_adc_read(void) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); return HAL_ADC_GetValue(&hadc1); } void fire_control(void) { static uint8_t confirm_cnt = 0; uint16_t raw = fire_adc_read(); if (raw > FIRE_TRIGGER_THRESHOLD) /* 触发阈值,比如 1800 */ { if (++confirm_cnt >= 3) /* 连续3次确认才启动 */ { HAL_GPIO_WritePin(FAN_CTRL_GPIO_Port, FAN_CTRL_Pin, GPIO_PIN_SET); } } else if (raw < FIRE_EXIT_THRESHOLD) /* 退出阈值,比如 1200 */ { confirm_cnt = 0; HAL_GPIO_WritePin(FAN_CTRL_GPIO_Port, FAN_CTRL_Pin, GPIO_PIN_RESET); } }这里最容易被忽略的不是代码,而是火焰传感器的防误报设计。安装角度按第2章的方案斜向下看,但还不够;实际环境中,白炽灯直射和阳光反射都会让ADC值突破触发阈值。我处理的办法是在传感器前面套一小段黑色热缩管或遮光筒,限制它的视角,只让水平前方约60度范围内的大面积红外光进入。参数上,触发阈值取“车头前方10cm打火机火焰”读数的70%左右,不要取边缘值,否则误报率很高。
风扇启动后,如果火焰已经熄灭,由于传感器还残留余温和邻近物体热辐射,ADC值下降会慢,所以退出阈值要明显低于触发阈值,这个回差是灭火控制里最容易遗漏的参数。
4.4 状态优先级:四个功能同时请求时谁说了算
很多小车翻车不是单个功能坏了,而是功能之间互相冲突:正在循迹,前方有障碍,避障模块让车转弯;转弯时又看到火焰,灭火流程把车停下;遥控器一按,又希望接管一切。没有优先级,逻辑就乱套。
我用的优先级从高到低是:遥控急停 > 火焰灭火 > 避障 > 循迹。主循环骨架如下:
while (1) { uint8_t rmt = remote_get_command(); /* 从遥控串口读取 */ if (rmt == CMD_STOP) /* 遥控急停永远最高 */ { motor_stop(); continue; } if (fire_detect()) /* 发现火焰就进入灭火流程 */ { fire_fighting_routine(); continue; } if (sonar_get_distance() < 15) /* 障碍太近,避障 */ { obstacle_avoid_routine(); continue; } track_control(); /* 否则正常循迹 */ }逻辑说明:每次循环只执行一个功能,不嵌套,不做多任务,这样状态清晰且单个功能运行时间短。遥控急停单独占一个优先级是因为手动控制时,用户想停就立刻停,哪怕正在全速冲向火源,也不能等灭火流程结束。
参数注意点:主循环周期要控制在10ms以内。超声波测距一次最长约24ms,如果每次都阻塞测距,循迹响应会变迟钝。所以超声波放到定时器中断里触发,或者把测距结果缓存,主循环读缓存值。这里我习惯把测距放到定时器中断,主循环只负责状态决策,整个系统响应会舒服很多。
5. 调试避坑记录:五个排查过的问题,现象、原因和处理
5.1 电机单侧不转或转向相反
现象:给同一组PWM,左侧电机正常转,右侧电机不转;或者两轮都转但方向一个前进一个后退,小车原地打转。
原因:最常见是TB6612的方向引脚接反了,IN3/IN4逻辑和代码里定义不一致;其次是PWM输出到了错误的定时器通道,导致某侧一直为低电平。
解决:先把左右电机的PWM占空比分别固定为300,然后用万用表量PB6/PB7引脚的电压,有跳变说明PWM信号正常。再用一根杜邦线直接给TB6612的IN3口接高电平,观察电机是否转动,以此确认接线顺序。如果转向相反,把代码里IN3和IN4的输出逻辑对调即可,不用改物理接线。
5.2 循迹在强光或反光地面上失灵
现象:室内木地板和黑色胶带上调好的阈值,拿到靠近窗户的浅色地面或光滑瓷砖上,四路传感器同时输出1或同时输出0,小车直接冲出去。
原因:TCRT5000输出的是反射红外光强度,环境光里同样包含红外分量,阳光直射时传感器看到的是“一片白”;光滑瓷砖对红外反射太强,黑色胶带的反射值和瓷砖底面差距太小。
解决:先用黑色绝缘胶带把传感器两边的缝隙贴上,做成一个简易遮光罩,只留底部检测窗口。然后把模块电位器重新调一遍:把传感器放在背景地面,让输出为0,放在黑线上,让输出为1,留出0.5V以上的电压余量。如果场地光线实在不可控,就换成模拟量输出,每次上电时先采样一遍背景值,在代码里动态算阈值,这个方案适应性最好。
5.3 超声波偶尔跳“0”或超时
现象:小车静止时测距稳定在35cm,一走起来读数就出现0cm或直接超时,避障逻辑被错误触发,小车莫名其妙地转弯。
原因:电机转动时电刷和碳刷产生电磁干扰,通过共地线窜入超声波Echo引脚;同时电机电流变化导致稳压器输出电压波动,模块供电不稳,回波信号被淹没。
解决:在超声波模块的VCC脚串联一个10欧姆电阻,并在模块旁边并接一个100μF电解电容。Echo引脚上再简单做一个RC滤波,用10k欧姆电阻串联再加1nF电容到地。测距代码里的两次超时返回值要区分开,999表示无回波,998表示回波超长,避免都当作“距离为0”处理。
5.4 火焰传感器没火也触发风扇
现象:小车放在室内,没有点火源,风扇偶尔自动启动;把传感器用手指盖住就停。
原因:阳光通过窗户照射到火焰传感器感光头上,或白炽灯直射时,模拟量ADC值接近触发阈值。传感器默认视角很大,能看到整个前方区域,自然会误报。
解决:给火焰传感器加遮光筒,用黑色热缩管或者用黑色纸卷一个长约2厘米的小圆筒,套在感光二极管外面,视角收窄到60度以内。再把触发阈值调高,确认接上遮光筒后在白天日光灯环境下ADC读数不超过阈值的一半。最后在代码里加连续3次采样确认,三重防护下来误报率基本可以忽略。
5.5 电池电量下降后舵机抖动、单片机复位
现象:新充满电时一切正常,跑十几分钟后舵机开始左右抖动,随后STM32自动复位,重新上电又能跑一小会。
原因:锂电池电压随放电逐渐下降,舵机和电机同时启动瞬间电流很大,5V降压模块输入电压跌破最低工作电压,5V输出跌到4V以下,单片机3.3V也跟着掉电,触发掉电复位。
解决:先给舵机单独加一个470μF电解电容,容量越大越好,放0.1μF陶瓷电容滤高频。降压模块尽量选最低压差小且输入范围宽的,我换过一次模块后这个现象基本消失。最根本的手段是监控电池电压:用两个电阻分压电池电压到ADC引脚,电压低于6.8V时遥控端收到低电量提示,不再执行任何大动作。这样不会在比赛或演示中突然断电翻车。
6. 最后一公里:状态机编排、PID整定和三个验证场景
6.1 状态机比什么都好用的迁移表
功能多了之后,主循环里的if-else会变成一坨乱麻,我建议梳理成一张状态迁移表:空闲、遥控、循迹、避障、灭火五个状态,每个状态只在特定条件下跳转。遥控切手动时从任何状态都回到遥控;遥控模式按下自动按钮进循迹;循迹中遇到障碍进避障;任意模式发现火焰且不在遥控模式下进灭火。这张表贴在调试笔记本上,比对着代码堆逻辑高效得多。
6.2 巡线PID参数速调
如果只用固定PWM差值,小车在直线上会蛇形前进。我最后换成了位置式PID,误差定义是四路传感器加权求出的“偏差值”,左两路为正右两路为负。参数整定用经验法:先将积分和微分设为0,只加比例项,每次给20%增量,直到小车在直线上出现轻微左右摆;然后加微分项压住摆动,最后加很小的积分项消除长时间同向偏差。注意I不能大,否则过弯时会明显滞后。
6.3 三种场景自测,验收前照一遍
我每次做完都会按三个场景验收:直线加缓弯赛道,验证循迹和速度稳定性;赛道中段放一个纸箱挡路,验证避障加回归循迹;赛道终点放一支酒精棉球点燃,验证火焰检测和风扇灭火。三个场景连续跑三遍,任何一遍翻车都要回头查状态迁移条件,而不是只补那个功能模块的代码。
这种综合项目最忌讳“功能在但不可靠”。我现在的习惯是:每次改完代码,先把传感器原始值打印到串口,确认数据合理再跑运动控制;任何一次复位或丢包都要找出根因,不带着“偶尔坏一次”的侥幸上路。希望这些思路和参数能帮你把这台智能遥控循迹避障灭火小车一次调通,少走几圈弯路,希望帮到你。
本文还有配套的精品资源,点击获取