☰
电赛自动泊车系统实战:传感器融合与路径规划算法详解
2026/10/22 19:41:51 网站建设 项目流程

简介:这份资源是2022年全国大学生电子设计竞赛B题自动泊车系统的完整工程源码与学习说明,面向备战电赛的高校学生、电子信息与自动化相关专业学习者,以及希望研究嵌入式控制与传感器融合的开发者。压缩包共111个文件,约815KB,以C语言源文件与头文件为主体,涵盖STM32外设驱动、MPU6050姿态解算、定时器与串口通信等模块,另含Keil工程文件、JSON配置、日志与烧录hex文件,结构完整可直接编译使用。目前已有177人学习下载,可作为竞赛项目参考与课程设计素材。读者可从中获取自动泊车系统的整体控制思路、传感器数据采集与处理流程、电机与舵机协同控制逻辑,以及工程目录组织方式,便于对照赛题要求理解实现细节,并在此基础上自行调试与扩展功能。

1. 自动泊车系统竞赛题拆解:从倒车入库到路径规划的真实门槛

很多人第一次看到“2022年全国大学生电子设计竞赛-B题自动泊车系统”这个题目,第一反应是“不就是小车倒车入库吗”。但真正上手做过的人都知道,这道题的核心难点根本不在“泊车”这个动作本身,而在于在有限的车位空间内,用超声波或红外测距完成环境感知,再靠路径规划算法把一辆四轮小车精确地停进指定区域。它考察的是传感器融合、运动控制、状态机设计三件事的协同,而不是单一模块的堆砌。

这套源码和学习说明适合两类人:一类是正在准备电赛控制类题目的在校学生,需要一套能跑通的参考实现来理解“感知-决策-执行”的完整链路;另一类是做嵌入式运动控制但没接触过泊车场景的工程师,想看看在低成本传感器条件下怎么把定位精度做到厘米级。如果你只是想要一个“能动的遥控车”,那这道题的价值你大概率用不上。

2. 自动泊车系统的硬件选型与传感器布局:为什么超声波加编码器是竞赛最优解

2.1 传感器方案对比:超声波、红外与视觉的取舍逻辑

在竞赛场景下,传感器选型的第一约束不是精度,而是开发周期和调试确定性。我见过太多队伍一上来就想用摄像头做视觉车位检测,结果光调光照和畸变就耗掉一半时间。常见做法是:车位检测用超声波测距模块,车身位姿用编码器加陀螺仪,两者做互补滤波。

方案测距范围刷新率受环境影响竞赛适配度
超声波模块2cm-400cm20-50Hz温度、反射面角度高
红外测距10cm-80cm100Hz+强光、黑色物体中
视觉方案依赖标定30fps光照、纹理低
激光TOF0.1m-5m50Hz+环境光中高

从表格能看出来,超声波在2cm到4m这个区间覆盖了泊车场景的绝大多数需求,而且它对反射面角度的容忍度比红外好。但超声波有个玄学问题:不同批次模块的波束角差异很大,有的标称15度实际能到30度,导致侧向测距时容易串扰。我一般会在布局时把相邻超声波模块的触发时间错开至少20ms,避免交叉回波。

2.2 四轮小车底盘布局:编码器安装与轮径标定

底盘布局决定了后续运动学模型的准确性。竞赛常用的是两轮差速加万向轮结构,但自动泊车需要更精确的航向控制,所以我会推荐四轮独立驱动加编码器反馈的方案。编码器装在电机输出轴还是车轮轴上有讲究:装在电机轴分辨率高但会引入减速箱回差,装在车轮轴更真实但分辨率低。竞赛场景下装在电机轴加齿轮间隙补偿更实际。

轮径标定是很多人忽略的一步。理论轮径和实际轮径的误差会直接导致里程计漂移。标定方法是让小车直线走3米,记录编码器脉冲数,反推实际轮径:

# 轮径标定脚本:直线行驶3米后计算实际轮径 import math def calibrate_wheel_diameter(pulse_count, encoder_ppr, gear_ratio, distance_m): """ pulse_count: 编码器总脉冲数 encoder_ppr: 编码器每转脉冲数 gear_ratio: 减速比 distance_m: 实际行驶距离(米) 返回:实际轮径(米) """ wheel_circumference = distance_m / (pulse_count / (encoder_ppr * gear_ratio)) diameter = wheel_circumference / math.pi return diameter # 示例:编码器500线,减速比30,走3米记录到45000个脉冲 d = calibrate_wheel_diameter(45000, 500, 30, 3.0) print(f"实际轮径: {d*100:.2f} cm") # 输出约6.37cm

这段代码的逻辑是:总脉冲数除以每转脉冲数得到总转数,再除以减速比得到车轮实际转数,用距离除以转数得到周长,最后反推直径。参数说明:encoder_ppr要查编码器手册,gear_ratio是减速箱标称值但实际可能有偏差,所以标定距离建议取3米以上来降低相对误差。

2.3 电源与电机驱动:竞赛现场最容易被忽略的稳定性问题

电机驱动选型要看堵转电流。泊车过程中频繁启停和方向切换,峰值电流可能是额定值的3到5倍。我一般会选持续电流10A以上的驱动模块,并在电源端并联至少2200uF的电解电容。血泪经验是:电池电压从8.4V降到7.4V的过程中,电机转速会下降约12%,如果里程计算法没有做电压补偿,泊车精度会明显变差。补偿方法是在代码里根据ADC采到的电池电压动态调整PWM占空比。

3. 泊车路径规划算法实现:从圆弧拼接到底层控制

3.1 两段圆弧加直线:最简可行的泊车轨迹生成

自动泊车的路径规划不需要用RRT或A*这类复杂算法,竞赛场景下两段圆弧加一段直线就能覆盖绝大多数车位。核心思路是:先倒车进入一个过渡位姿,再调整方向倒入车位。具体来说,从起始点到泊车点可以分解为:前进圆弧段、直线调整段、倒车圆弧段。

import numpy as np def plan_parking_path(start_pose, target_pose, min_turn_radius): """ start_pose: [x, y, theta] 起始位姿 target_pose: [x, y, theta] 目标位姿 min_turn_radius: 最小转弯半径(由底盘决定) 返回:路径点列表 """ x0, y0, th0 = start_pose x1, y1, th1 = target_pose # 计算两段圆弧的圆心 # 第一段:从起始位姿左转或右转 d = np.hypot(x1 - x0, y1 - y0) if d < 1e-6: return [start_pose] # 判断转向方向:根据目标点在起始点左侧还是右侧 cross = np.cos(th0) * (y1 - y0) - np.sin(th0) * (x1 - x0) direction = 1 if cross > 0 else -1 # 圆弧参数 r = min_turn_radius # 第一段圆弧圆心 cx1 = x0 - direction * r * np.sin(th0) cy1 = y0 + direction * r * np.cos(th0) # 生成路径点 path = [] # 第一段圆弧:从th0转到朝向目标的方向 angle_to_target = np.arctan2(y1 - cy1, x1 - cx1) arc1_end = angle_to_target - direction * np.pi/2 for t in np.linspace(th0, arc1_end, 20): px = cx1 + direction * r * np.cos(t) py = cy1 + direction * r * np.sin(t) path.append([px, py, t]) # 第二段:直线到目标 for t in np.linspace(0, 1, 10): px = path[-1][0] + t * (x1 - path[-1][0]) py = path[-1][1] + t * (y1 - path[-1][1]) path.append([px, py, th1]) return path

这段代码的核心逻辑是:先根据起始位姿和目标位姿的相对位置判断转向方向,然后以最小转弯半径生成第一段圆弧,最后用直线段连接到目标点。参数说明:min_turn_radius需要实测,方法是让小车以最大转向角走一圈,测量圆的半径。注意这里没有考虑倒车,实际泊车时第二段圆弧需要反向生成,把direction取反即可。

3.2 纯追踪控制器:把路径点变成电机PWM

有了路径点之后,需要控制器把路径转化为左右轮速。纯追踪算法的思路是:在小车前方选一个预瞄点,计算小车当前朝向与预瞄点方向的夹角,用这个夹角乘以比例系数得到转向修正量。

def pure_pursuit_control(current_pose, path, lookahead_dist, wheel_base): """ current_pose: [x, y, theta] 当前位姿 path: 路径点列表 lookahead_dist: 预瞄距离 wheel_base: 轴距 返回:左右轮速比 """ x, y, th = current_pose # 找到最近的路径点 min_dist = float('inf') nearest_idx = 0 for i, p in enumerate(path): d = np.hypot(p[0] - x, p[1] - y) if d < min_dist: min_dist = d nearest_idx = i # 从最近点向前找预瞄点 target = path[-1] for i in range(nearest_idx, len(path)): d = np.hypot(path[i][0] - x, path[i][1] - y) if d >= lookahead_dist: target = path[i] break # 计算预瞄点在小车坐标系下的位置 dx = target[0] - x dy = target[1] - y local_x = dx * np.cos(th) + dy * np.sin(th) local_y = -dx * np.sin(th) + dy * np.cos(th) # 纯追踪曲率 if local_x < 0.01: curvature = 0 else: curvature = 2 * local_y / (lookahead_dist ** 2) # 转换为左右轮速差 angular = curvature * 0.3 # 0.3m/s为基准线速度 left_speed = 0.3 - angular * wheel_base / 2 right_speed = 0.3 + angular * wheel_base / 2 return left_speed, right_speed

逻辑说明:先找路径上离小车最近的点,然后从该点向前搜索距离大于预瞄距离的点作为追踪目标。把目标点变换到小车坐标系后,用纯追踪公式计算曲率,最后根据轴距反推左右轮速。参数说明:lookahead_dist太大会导致切内弯,太小会震荡,一般取0.3到0.5米。wheel_base是左右轮中心距,不是轴距,这里命名有点误导但竞赛代码里常这么写。

3.3 状态机设计:泊车流程的六个状态与切换条件

泊车过程用状态机管理最清晰。我一般会定义六个状态:空闲、搜索车位、准备泊车、第一段圆弧、第二段圆弧、完成。每个状态的切换条件要明确,避免状态震荡。

class ParkingStateMachine: def __init__(self): self.state = 'IDLE' self.path = [] self.path_index = 0 def update(self, sensor_data, current_pose): if self.state == 'IDLE': if sensor_data['start_button']: self.state = 'SEARCH' elif self.state == 'SEARCH': # 超声波检测到车位宽度大于车宽加40cm if sensor_data['slot_width'] > 0.4 + 0.2: self.state = 'PREPARE' self.target_pose = self.calc_target_pose(sensor_data) elif self.state == 'PREPARE': # 生成路径 self.path = plan_parking_path(current_pose, self.target_pose, 0.5) self.path_index = 0 self.state = 'ARC1' elif self.state == 'ARC1': # 跟踪路径前半段 if self.path_index >= len(self.path) // 2: self.state = 'ARC2' else: self.path_index += 1 elif self.state == 'ARC2': if self.path_index >= len(self.path) - 1: self.state = 'DONE' else: self.path_index += 1 elif self.state == 'DONE': # 停车,电机抱死 pass

这段状态机的关键点是:SEARCH状态里车位宽度判断要留余量,实际测试中超声波测宽会有±5cm波动,所以阈值设成车宽加40cm比较稳妥。ARC1和ARC2的切换点用路径索引判断,比用时间判断更可靠,因为电机转速会随电压变化。

4. 自动泊车系统调试避坑:从传感器串扰到里程计漂移的五个真实翻车记录

4.1 超声波串扰导致车位宽度误判

现象:小车在搜索车位时,明明车位够宽却报“车位过窄”,或者相邻两个超声波模块同时触发时读数跳变。原因:多个超声波模块同时发射,A模块的回波被B模块接收,导致测距值偏小。解决:分时触发,相邻模块触发间隔至少20ms,并且在代码里加中值滤波,连续采5次去掉最大最小值再取平均。

4.2 编码器计数方向与电机转向不一致

现象:小车直线行驶时里程计显示在走弧线,或者PID控制下小车原地画圈。原因:左右编码器的A/B相接线反了,导致计数方向与电机实际转向相反。解决:在代码里加方向修正系数,或者直接调换编码器A/B相接线。我一般会在初始化时做一次自检:给左轮正转PWM,看左编码器计数是否增加,不是就取反。

4.3 电池电压下降导致泊车精度变差

现象:满电时泊车能进库,电量降到一半后同样的代码停偏10cm以上。原因:电机转速与电压成正比,里程计算法没有做电压补偿,导致实际行驶距离比计算值短。解决:在代码里读取电池电压,用实际PWM = 目标PWM * 标称电压 / 当前电压做补偿。注意补偿系数不要超过1.3,否则低电量时电流过大可能触发保护。

4.4 路径跟踪时预瞄距离设错导致切内弯

现象:小车在圆弧段总是偏向圆心一侧,最后压到车位边线。原因:预瞄距离太小,纯追踪控制器频繁修正,实际走的是弦而不是弧。解决:把预瞄距离从0.2米加大到0.4米,同时降低比例系数。如果还不行,就在路径生成时把圆弧半径加大10%作为补偿。

4.5 状态机切换条件太敏感导致状态震荡

现象:小车在“准备泊车”和“第一段圆弧”之间反复切换,电机一抖一抖的。原因:切换条件用了瞬时传感器值,没有做迟滞。解决:给每个切换条件加一个计数器,连续满足3个控制周期才切换。另外状态切换时要把路径索引和PID积分项清零,避免残留量导致突变。

5. 从能跑到跑准:自动泊车系统的精度验证与参数整定技巧

5.1 用重复定位精度验证泊车效果

跑通之后怎么判断系统好不好?我一般会做十次重复泊车测试,记录每次停车后车头中心点与目标点的偏差。如果十次里有八次偏差在3cm以内,这套系统就算合格。测试时要注意:每次测试前把电池充满,因为电压对精度影响很大。记录表格可以这样设计:

测试次数X方向偏差(cm)Y方向偏差(cm)航向偏差(度)电池电压(V)
11.20.81.58.35
22.11.52.38.28
...............

如果发现偏差随测试次数增大而增大,大概率是机械结构松动,检查电机座和编码器联轴器。如果偏差随机分布,那就是控制参数没调好。

5.2 三个必调参数:预瞄距离、速度上限、PID比例系数

整定顺序很重要。先调预瞄距离,让小车能基本沿路径走不震荡;再调速度上限,在能走准的前提下尽量快;最后调PID比例系数,把稳态误差压下去。我一般会把预瞄距离从0.5米开始试,每次减0.1米,直到出现轻微震荡就回退0.05米。速度上限从0.2m/s开始,每次加0.05m/s,直到泊车时间不再明显缩短为止。

5.3 一个容易被忽略的技巧:用倒车轨迹的对称性做自检

泊车路径的倒车段和前进段在几何上是对称的。我习惯在代码里加一个自检模式:让小车沿路径正走一遍再倒走一遍,比较两次的编码器累计值。如果差值超过5%,说明轮径标定或编码器方向有问题。这个自检能在赛前十分钟快速定位大部分机械和标定问题,比到了赛场上才发现要划算得多。

做这类题目最大的教训是:别在算法上炫技,把传感器标定和机械一致性做扎实,比什么先进算法都管用。我见过用纯追踪加两段圆弧的队伍拿国奖,也见过用模型预测控制但编码器没装好的队伍连基本泊车都跑不稳。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询