在电子设计竞赛电赛中小车类题目是控制类赛题的常客从基础的循迹、避障到复杂的平衡、滚球控制每年都吸引着大量参赛者。对于大一新生而言面对“电赛飞线小车”这样的项目挑战不仅在于理解单片机、传感器和电机驱动更在于如何将一堆零散的模块通过“飞线”即杜邦线可靠地连接起来并编写出稳定、高效的控制程序。这个过程充满了从硬件连接到软件调试的实战细节任何一个环节的疏忽都可能导致小车“罢工”。本文将以一个典型的电赛小车项目为背景假设目标是完成一个具备循迹与动态避障功能的智能小车。我们将从零开始梳理硬件选型、电路连接飞线技巧、核心代码编写、参数调试到最终系统集成的完整流程。文章不仅会提供可运行的代码框架更会重点解释为什么这样连接、为什么这样编程以及当小车不按预期运行时应该如何一步步排查问题。无论你是初次接触电赛的大一同学还是希望巩固嵌入式开发基础的开发者都能通过本文构建一个清晰、可复现的小车开发与调试思路。1. 理解电赛小车的核心系统构成一个典型的电赛智能小车其本质是一个移动的嵌入式系统。它通过传感器感知环境如地面黑线、前方障碍物由微控制器如STM32、Arduino作为大脑进行决策最后通过执行器电机来驱动车轮行动。理解这个信息流的闭环是设计和调试一切功能的基础。1.1 硬件模块分解与选型依据电赛小车通常由以下几个核心模块构成选型时需要平衡性能、成本、易用性和电赛题目要求。模块类别常见型号/类型核心作用选型考量与电赛常见要求主控MCUSTM32F103C8T6 (蓝桥杯/电赛常用)、Arduino Uno/Nano、ESP32系统大脑运行控制算法处理传感器数据产生PWM驱动电机。STM32性能强外设丰富多定时器、ADC适合复杂控制如PID、平衡。Arduino生态好库丰富上手快适合快速验证。ESP32集成Wi-Fi/蓝牙适合需要无线通信的题目。电赛控制类题目通常对实时性有要求STM32系列是更主流的选择。电机驱动L298N、TB6612FNG、DRV8833接收MCU的小电流控制信号驱动电机所需的大电流。L298N经典、皮实、易购但发热大效率较低。TB6612FNG效率高发热小支持待机模式体积小是当前更优选择。DRV8833集成度更高适合小型小车。选型需计算电机工作电流确保驱动芯片峰值电流留有裕量。直流减速电机TT马达带编码器/不带编码器、N20减速电机提供车轮动力减速齿轮箱提供更大扭矩。需关注电压如3-6V、空载转速、减速比决定最终转速和扭矩、是否带编码器。编码器用于精确测速和闭环控制在需要精确速度控制或里程计算的题目中是必备的。循迹传感器TCRT5000红外反射模块、灰度传感器阵列发射红外光并接收反射光强度用于识别地面黑线与白底。TCRT5000价格低廉需调节电位器改变灵敏度。电赛中常使用多个如3-5个组成阵列以检测小车相对于黑线的位置偏差。数字输出DO简单模拟输出AO可提供更精细的灰度值。避障传感器HC-SR04超声波模块、红外避障模块检测前方障碍物距离。HC-SR04测量距离精确2cm-400cm但测量有最小盲区响应速度相对慢。红外避障检测距离短几cm到几十cm易受环境光干扰但响应快成本低。根据题目障碍物特性选择或融合使用。电源管理18650锂电池组、降压模块如LM2596、升压模块为整个系统提供稳定、合适的电压。电机启动电流大会导致电源电压瞬间跌落可能造成MCU复位。关键实践必须为MCU和逻辑电路传感器、驱动芯片逻辑供电提供独立于电机动力的稳压电源。常用方案锂电池如7.4V-降压模块降至5V供逻辑部分电机直接由锂电池或通过驱动模块供电。1.2 “飞线”连接的艺术与隐患“飞线”指用杜邦线连接各个模块。它灵活但也是项目中最主要的故障来源之一。连接逻辑必须严格遵循“共地”原则。即所有模块的GND引脚必须最终连接到电源的负极形成一个统一的参考零电位。任何地线环路缺失或接触不良都会导致信号紊乱、传感器读数不准、单片机程序跑飞。电源分配如前所述强烈建议将电机动力电源与控制逻辑电源在物理上分离或通过电感、磁珠隔离。最简单的做法是使用两套电池或者使用一个电池但通过两个独立的稳压电路输出。线缆整理杂乱无章的飞线极易被车轮绞住或互相拉扯导致脱落。使用扎带、热熔胶或绝缘胶带将线缆分组固定在小车底盘上是保证长期运行稳定的必要步骤。接触可靠性杜邦线接头反复插拔容易松动。在比赛前对关键连接点如电源、电机线可以用热熔胶轻微加固注意不要影响后续拆卸。2. 从零搭建硬件系统与最小软件测试我们以STM32F103C8T6核心板TB6612FNG电机驱动带编码器的TT马达TCRT5000循迹模块HC-SR04超声波模块为例演示硬件连接和基础驱动软件。2.1 硬件连接清单与原理以下连接示意基于STM32标准库开发环境。请务必先对照原理图或引脚定义表进行操作。电源部分将一组7.4V 18650电池的正负极接入TB6612FNG的VM电机电源和GND。使用一个降压模块如LM2596从电池正负极取电输出稳定的5V连接到核心板的5V或VIN引脚如果核心板有5V稳压并为TB6612FNG的VCC逻辑供电、循迹模块、超声波模块供电。所有模块的GND最终都必须连接到这个5V电源的GND上。电机与驱动连接左电机线接TB6612的A01和A02右电机线接B01和B02。TB6612控制引脚连接STM32STBY使能接STM32任一GPIO如PC13高电平有效。AIN1,AIN2控制电机A左方向接PA1,PA2。BIN1,BIN2控制电机B右方向接PA3,PA4。PWMA,PWMB接收PWM信号控制速度接STM32的定时器通道引脚如TIM2_CH1 (PA0)和TIM2_CH2 (PA1)注意避免与方向引脚冲突。编码器连接如果电机带编码器编码器一般输出A、B两相正交信号。分别接到STM32的定时器编码器模式引脚如TIM3_CH1 (PA6)和TIM3_CH2 (PA7)用于左电机TIM4_CH1 (PB6)和TIM4_CH2 (PB7)用于右电机。循迹传感器连接以3个为例三个TCRT5000的VCC接5VGND接GND。数字输出DO引脚分别接STM32的GPIO如PB12,PB13,PB14。也可以使用模拟输出AO接STM32的ADC引脚实现灰度循迹。超声波模块连接VCC接5VGND接GND。Trig触发接STM32 GPIO如PB8。Echo回响接STM32 GPIO如PB9该引脚应支持输入捕获或外部中断以精确测量高电平时间。2.2 基础驱动代码让小车动起来在开始复杂算法前先确保每个底层模块都能被正确驱动。这里以STM32 HAL库为例展示关键初始化代码。电机驱动初始化PWM和GPIO// motor.c #include “motor.h” TIM_HandleTypeDef htim2; // PWM定时器 GPIO_InitTypeDef GPIO_InitStruct {0}; void Motor_Init(void) { // 1. 初始化控制方向的GPIO引脚 (AIN1, AIN2, BIN1, BIN2, STBY) __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // STBY 引脚 __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_13; HAL_GPIO_Init(GPIOC, GPIO_InitStruct); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 使能驱动芯片 // 2. 初始化PWM定时器 TIM2, Channel1 (PA0) 和 Channel2 (PA1) // 注意这里PA0和PA1与上面的方向引脚冲突实际项目需根据板子原理图选择不冲突的引脚例如使用PA8和PA9。 // 此处仅为示例流程。 htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; // 72MHz / 72 1MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 1000 - 1; // 1MHz / 1000 1kHz PWM频率 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim2); TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比0 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); } // 设置左电机速度和方向 void Set_LeftMotor(int16_t speed) { // speed范围 -1000 ~ 1000 if (speed 0) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // AIN1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // AIN2 __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed); } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, -speed); } } // 右电机函数类似...关键解释PWM频率通常选择1kHz-10kHz。频率太低电机噪音大太高可能超过驱动芯片响应能力。Period寄存器值决定了PWM分辨率这里设为999则速度可设0-999。方向控制是通过给AIN1/AIN2高低电平组合实现的具体需参考TB6612数据手册。超声波测距基础函数// ultrasonic.c #include “ultrasonic.h” uint32_t high_level_time 0; uint8_t measure_done 0; void Ultrasonic_Trigger(void) { HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(12); // 触发信号至少10us高电平 HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); } // 在Echo引脚上升沿/下降沿中断中计算高电平时间 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { static uint32_t start_time 0; if (GPIO_Pin ECHO_Pin) { if (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) GPIO_PIN_SET) { // 上升沿开始计时 start_time HAL_GetTick(); // 使用毫秒计时精度有限。高精度应用需用定时器输入捕获。 } else { // 下降沿结束计时 high_level_time HAL_GetTick() - start_time; measure_done 1; } } } float Get_Distance_CM(void) { Ultrasonic_Trigger(); measure_done 0; uint32_t timeout HAL_GetTick() 100; // 100ms超时 while (!measure_done HAL_GetTick() timeout) { // 等待测量完成或超时 } if (measure_done) { // 距离 (高电平时间 * 声速) / 2 // 注意HAL_GetTick()单位是ms声速340m/s 0.034cm/us // 高电平时间单位ms换算distance high_level_time * 0.034 * 1000 / 2 return (high_level_time * 17.0f); // 简化计算实际误差较大建议使用定时器微秒级计时。 } return -1.0f; // 超时或测量失败 }注意上述代码使用HAL_GetTick()毫秒定时器测量对于超声波测距厘米级精度误差很大仅用于演示流程。实际项目必须使用定时器的输入捕获功能来测量微秒级的高电平时间才能获得准确距离。3. 核心算法实现循迹与动态避障当底层驱动验证无误后便可以开始实现上层逻辑。循迹的核心是PID控制动态避障的核心是状态机决策。3.1 循迹算法PID控制假设我们使用3个数字式TCRT5000左、中、右。定义传感器检测到黑线为1白底为0。// track.c #define SENSOR_LEFT 0 #define SENSOR_MIDDLE 1 #define SENSOR_RIGHT 2 uint8_t sensor_values[3]; // 存储三个传感器状态 int32_t line_position 0; // 虚拟的“线位置”用于PID计算 int32_t last_error 0, integral 0; // 读取传感器并计算位置偏差 void Read_Line_Sensors(void) { sensor_values[SENSOR_LEFT] HAL_GPIO_ReadPin(SENSOR_L_GPIO_Port, SENSOR_L_Pin); sensor_values[SENSOR_MIDDLE] HAL_GPIO_ReadPin(SENSOR_M_GPIO_Port, SENSOR_M_Pin); sensor_values[SENSOR_RIGHT] HAL_GPIO_ReadPin(SENSOR_R_GPIO_Port, SENSOR_R_Pin); // 简单的权重法计算偏差。假设传感器间距相等。 // 定义左传感器为-100中传感器为0右传感器为100 line_position 0; if (sensor_values[SENSOR_LEFT]) line_position - 100; if (sensor_values[SENSOR_MIDDLE]) line_position 0; if (sensor_values[SENSOR_RIGHT]) line_position 100; // 如果都没检测到出界可以根据上一次偏差进行恢复或停车 } // 增量式PID计算输出为速度调整量 float PID_Calculate(int32_t target, int32_t current) { float Kp 0.5f, Ki 0.01f, Kd 0.2f; // PID参数需实际调试 int32_t error target - current; // 目标位置是0中线 float P_out Kp * error; integral error; float I_out Ki * integral; float D_out Kd * (error - last_error); last_error error; float output P_out I_out D_out; // 输出限幅 if (output MAX_OUTPUT) output MAX_OUTPUT; if (output -MAX_OUTPUT) output -MAX_OUTPUT; return output; } void Line_Following_Task(void) { Read_Line_Sensors(); float pid_output PID_Calculate(0, line_position); // 目标偏差为0 int base_speed 500; // 基础速度 int left_motor_speed base_speed - pid_output; int right_motor_speed base_speed pid_output; Set_LeftMotor(left_motor_speed); Set_RightMotor(right_motor_speed); }PID调试心得先P后I再D先将Ki和Kd设为0增大Kp直到小车沿着线摆动但能大致跟上。加I消静差如果小车在直道上始终无法稳定在中心存在静态误差适当加入较小的Ki。加D抑振荡如果小车摆动过于剧烈振荡加入Kd可以抑制这种振荡使曲线更平滑。实时调整最好能通过蓝牙串口等工具在电脑上实时修改参数并观察小车响应。3.2 动态避障决策状态机避障逻辑需要与循迹逻辑结合。一个简单的状态机可以是默认循迹 - 发现障碍 - 停车/绕行 - 恢复循迹。// avoidance.c typedef enum { STATE_LINE_FOLLOWING, STATE_OBSTACLE_DETECTED, STATE_TURNING, STATE_RECOVERING } SystemState_t; SystemState_t current_state STATE_LINE_FOLLOWING; uint32_t obstacle_timer 0; void Main_Decision_Loop(void) { float distance Get_Distance_CM(); switch (current_state) { case STATE_LINE_FOLLOWING: Line_Following_Task(); if (distance 0 distance SAFE_DISTANCE_CM) { // 例如20cm current_state STATE_OBSTACLE_DETECTED; obstacle_timer HAL_GetTick(); Stop_Motors(); // 先停车 } break; case STATE_OBSTACLE_DETECTED: // 停车观察一段时间或立即决策 if (HAL_GetTick() - obstacle_timer 500) { // 停车500ms // 简单策略向右转90度 Set_LeftMotor(600); Set_RightMotor(-600); current_state STATE_TURNING; obstacle_timer HAL_GetTick(); } break; case STATE_TURNING: // 执行转弯例如通过编码器判断转过一定角度或定时转弯 if (HAL_GetTick() - obstacle_timer 800) { // 假设800ms转90度 Stop_Motors(); // 进入恢复状态尝试重新寻线 current_state STATE_RECOVERING; obstacle_timer HAL_GetTick(); } break; case STATE_RECOVERING: // 原地缓慢旋转或前进直到中间传感器检测到黑线 Set_LeftMotor(200); Set_RightMotor(200); Read_Line_Sensors(); if (sensor_values[SENSOR_MIDDLE]) { current_state STATE_LINE_FOLLOWING; } // 超时处理 if (HAL_GetTick() - obstacle_timer 3000) { Stop_Motors(); // 进入错误状态或重新尝试 } break; } }这个状态机非常基础。更复杂的策略可能包括根据障碍物在左/右选择绕行方向绕行时记录路径以便回归或者使用多个传感器左、前、右进行更精细的环境感知。4. 系统集成、调试与故障排查将各个模块的代码整合到一个工程中并合理设计任务调度如使用定时器中断周期性地执行传感器读取、PID计算和决策循环是最后一步。4.1 常见问题与排查清单当小车无法正常工作时请按照以下清单逐项检查可以解决90%以上的“飞线小车”问题。问题现象可能原因排查步骤解决方案与建议上电后单片机无反应LED不亮1. 电源未接通或电压不对。2. 核心板损坏。3. 电源线接触不良。1. 用万用表测量核心板VCC和GND间电压是否为3.3V或5V视核心板而定。2. 检查降压模块输出。3. 轻摇杜邦线接头看是否有接触不良。确保电源正确连接。使用质量好的MicroUSB线或稳压电源供电。焊接关键电源线。电机不转或只抖动1. 电机驱动未使能STBY引脚为低。2. PWM信号未输出或频率不对。3. 电机电源VM电压不足或电流不够。4. 电机线接反或接触不良。1. 用逻辑分析仪或示波器检查PWM引脚是否有波形。2. 检查STBY引脚电平。3. 测量电机驱动VM端电压带载时是否跌落严重。4. 直接将电机接电池看是否正常转动。确认软件中已拉高STBY。检查PWM初始化代码和引脚配置。为电机动力使用独立电池或大容量电源。拧紧电机接线端子。循迹传感器始终触发或不触发1. 传感器供电错误。2. 距离地面高度不合适。3. 环境光干扰特别是红外传感器。4. 阈值电位器未调节。1. 测量传感器VCC电压。2. 观察传感器指示灯遮挡测试。3. 在预期环境下如比赛场地调节传感器上的电位器使其在黑白地面上输出状态翻转。4. 使用单片机ADC读取模拟值观察黑白差异。为传感器提供稳定5V供电。调整传感器安装高度通常距地面1-2cm。在传感器头部加装遮光罩。进行现场校准。超声波测距值不准或不变1. Echo引脚未正确配置中断/输入捕获。2. 测量时序错误Trig触发太频繁。3. 物体超出量程或表面不反射声波。4. 电源噪声干扰。1. 用示波器同时观察Trig和Echo引脚波形。2. 确保两次测距间隔大于60msHC-SR04要求。3. 对固定距离物体如20cm测试校准计算式中的声速系数。使用定时器输入捕获模式精确计时。增加测量间隔。避免测量柔软、倾斜的物体。在VCC和GND间并联10uF和0.1uF电容滤波。小车运行时单片机突然复位1. 电机启动/制动瞬间产生大电流导致电源电压跌落。2. 电机产生的反电动势干扰。3. 杜邦线松动。1. 用示波器监控单片机VCC电压观察电机动作时是否出现跌落毛刺。2. 检查所有GND连接是否牢固。最有效的方案电机动力电源与控制逻辑电源完全分离两套电池。或在电机电源输入端并接大容量电解电容如1000uF缓冲。在电机两端并联续流二极管。PID循迹摆动严重或跑飞1. PID参数不合适。2. 传感器安装间距不合理。3. 小车机械结构不对称左右轮转速不一致。4. 电池电压下降导致电机性能变化。1. 单独测试左右轮在相同PWM下的实际转速进行校准。2. 将PID输出和传感器偏差值通过串口打印到上位机观察曲线。3. 在直线赛道上反复调试P、I、D参数。实现电机速度闭环使用编码器。加入死区控制微小偏差不调整。定期校准电机速度差。使用稳压电源或监测电池电压进行补偿。4.2 进阶优化与扩展方向当基础功能实现后可以考虑以下优化来提升小车的性能和竞赛表现速度闭环控制使用电机编码器测量实际转速并用另一个PID控制器来使得电机转速精确跟随目标值。这能极大提升小车直线行驶的稳定性和抗干扰能力。运动学模型与路径规划建立小车的差分驱动运动学模型。这样你可以更精确地控制小车移动特定距离或旋转特定角度对于需要定点停车的任务至关重要。多传感器融合结合超声波、红外、甚至摄像头如OpenMV的数据构建更全面的环境感知。例如用红外检测近处障碍用超声波测量精确距离。无线调试与参数整定通过蓝牙如HC-05/06或Wi-FiESP32将单片机数据传感器值、PID输出、电机速度实时发送到电脑上位机如Serial Plotter、匿名上位机、自己写的Qt程序可以极大提高调试效率。状态记录与脱机运行对于需要记忆路径的题目可以让小车在第一遍运行时记录下关键动作如直行时间、转弯角度第二遍时脱机复现。对于大一同学完成一个能稳定循迹并避障的“飞线小车”已经是巨大的成功。这个过程中学到的硬件连接、电源管理、单片机编程、传感器使用、PID调试和系统调试的思维远比比赛名次更重要。从最小系统开始逐个模块验证最后集成调试遇到问题按照电源、地线、信号、配置、代码的逻辑层层排查这种工程化解决问题的方法将是你们后续所有软硬件项目中最宝贵的财富。