1. 项目概述
这个开源项目提供了2017-2016届全国大学生智能汽车竞赛的基础四轮摄像头循迹方案完整代码实现。作为当年参赛队伍的核心技术方案,它采用了经典的摄像头图像处理结合八邻域算法来实现赛道识别与路径规划,在当年的比赛中表现稳定可靠。
我在大三时曾带队参加过这项赛事,深知一套稳定可靠的底层代码对比赛成绩的重要性。这个开源项目最大的价值在于它提供了一个经过实战检验的完整解决方案,包含了从图像采集、处理到最终电机控制的完整链路实现。不同于一些demo性质的代码,这套方案在真实比赛环境中跑过完整的赛道,处理过各种光照条件和赛道类型。
2. 核心需求解析
2.1 智能车竞赛的技术要求
全国大学生智能汽车竞赛对参赛车辆有几个核心要求:
- 必须使用组委会指定的单片机平台(通常是K60或STM32系列)
- 必须自主完成传感器数据采集、处理和控制算法实现
- 需要在规定时间内完成包括直道、弯道、十字路口、坡道等复杂赛道元素
- 比赛环境的光照条件可能变化,赛道可能存在反光、阴影等干扰
2.2 四轮摄像头方案的特点
相比光电管方案,摄像头方案具有明显优势:
- 获取信息量大:一幅图像包含的信息远多于几个光电管
- 前瞻距离远:可以通过调整摄像头角度获得更远的赛道信息
- 适应性强:通过图像处理可以应对各种光照变化
但同时也带来挑战:
- 处理算法复杂,对单片机算力要求高
- 图像处理延迟可能影响控制实时性
- 需要处理大量数据,内存占用高
3. 系统架构设计
3.1 硬件组成
这套方案的硬件配置如下:
- 主控:K60单片机(MK60DN512ZVLQ10)
- 摄像头:OV7725数字摄像头(分辨率320×240)
- 电机驱动:BTS7960B H桥驱动
- 编码器:AB相增量式编码器
- 电源管理:LM2596降压模块
3.2 软件架构
软件采用分层设计:
- 底层驱动层:摄像头采集、电机控制、编码器读取
- 图像处理层:图像预处理、赛道识别
- 决策控制层:路径规划、速度控制
- 调试层:图像显示、参数调节
4. 核心算法实现
4.1 图像预处理流程
c复制// 图像采集
void CAMERA_IRQHandler() {
// 从摄像头DMA获取图像数据
...
}
// 二值化处理
void ImageBinarization(uint8_t *img) {
for(int i=0; i<IMG_HEIGHT; i++) {
for(int j=0; j<IMG_WIDTH; j++) {
// 动态阈值二值化
img[i*IMG_WIDTH+j] = (img[i*IMG_WIDTH+j] > threshold) ? 255 : 0;
}
}
}
4.2 八邻域算法详解
八邻域算法是这套方案的核心,其基本思路是:
- 从图像底部中央开始向上搜索赛道边界
- 对每个像素点,检查其周围8个相邻像素的状态
- 根据预设规则判断边界走向
c复制#define LEFT_BOUND 0
#define RIGHT_BOUND 1
void FindBoundary(uint8_t *img, int *leftBound, int *rightBound) {
// 初始化搜索起点
int startX = IMG_WIDTH / 2;
// 向上逐行搜索
for(int y=IMG_HEIGHT-1; y>=0; y--) {
// 向左搜索左边界
for(int x=startX; x>=0; x--) {
if(IsBoundaryPixel(img, x, y, LEFT_BOUND)) {
leftBound[y] = x;
break;
}
}
// 向右搜索右边界
for(int x=startX; x<IMG_WIDTH; x++) {
if(IsBoundaryPixel(img, x, y, RIGHT_BOUND)) {
rightBound[y] = x;
break;
}
}
// 更新下一行的搜索起点
startX = (leftBound[y] + rightBound[y]) / 2;
}
}
4.3 路径规划与偏差计算
获取赛道边界后,需要计算车辆当前位置与理想路径的偏差:
c复制float CalculateDeviation(int *leftBound, int *rightBound) {
float centerSum = 0;
int validRow = 0;
// 取图像下半部分计算平均偏差
for(int y=IMG_HEIGHT/2; y<IMG_HEIGHT; y++) {
if(leftBound[y] > 0 && rightBound[y] < IMG_WIDTH-1) {
centerSum += (leftBound[y] + rightBound[y]) / 2.0 - IMG_WIDTH/2;
validRow++;
}
}
return validRow > 0 ? centerSum / validRow : 0;
}
5. 控制策略实现
5.1 方向控制
采用PD控制器实现方向控制:
c复制float lastDeviation = 0;
void SteeringControl(float deviation) {
float Kp = 0.8; // 比例系数
float Kd = 0.3; // 微分系数
float output = Kp * deviation + Kd * (deviation - lastDeviation);
lastDeviation = deviation;
// 限制输出范围
output = output > MAX_STEER ? MAX_STEER : output;
output = output < -MAX_STEER ? -MAX_STEER : output;
// 转换为舵机PWM值
SetSteeringPWM(STEER_CENTER + output);
}
5.2 速度控制
速度控制采用分段策略:
c复制void SpeedControl(float deviation) {
float absDev = fabs(deviation);
float targetSpeed;
if(absDev < 10) {
targetSpeed = HIGH_SPEED; // 直道高速
} else if(absDev < 30) {
targetSpeed = MID_SPEED; // 缓弯中速
} else {
targetSpeed = LOW_SPEED; // 急弯低速
}
SetMotorSpeed(targetSpeed);
}
6. 关键参数调试
6.1 图像处理参数
-
二值化阈值:需要根据实际光照条件调整
- 可通过上位机实时调整并观察效果
- 建议实现自适应阈值算法
-
感兴趣区域(ROI):
- 可裁剪掉图像上部噪声较多的区域
- 典型设置为y从50开始(320×240分辨率)
6.2 控制参数
-
方向控制PD参数:
- Kp过大容易振荡,过小响应慢
- Kd能抑制振荡但过大会导致响应迟钝
-
速度分段阈值:
- 需要根据赛道实际情况调整
- 可通过实际测试观察过弯稳定性
7. 实战经验分享
7.1 常见问题及解决
-
图像出现横纹干扰:
- 检查摄像头供电是否稳定
- 增加电源滤波电容
- 调整摄像头时钟频率
-
过弯时冲出赛道:
- 检查机械结构是否对称
- 调整速度控制策略
- 增加弯道识别提前量
-
直道行驶不稳定:
- 可能是PD参数不合适
- 检查图像处理延迟
- 确保编码器读数准确
7.2 性能优化技巧
-
图像处理优化:
- 使用查找表加速二值化
- 降低处理分辨率
- 采用隔行处理
-
控制周期优化:
- 确保控制周期稳定
- 图像处理和控制尽量并行
- 关键代码使用汇编优化
-
内存优化:
- 合理使用内存池
- 避免频繁动态分配
- 使用位域压缩数据
8. 进阶改进方向
-
图像算法升级:
- 引入边缘检测算法
- 尝试HSV色彩空间处理
- 加入图像透视变换
-
控制策略改进:
- 实现模糊控制
- 加入预测控制
- 自适应参数调整
-
系统架构优化:
- 引入RTOS管理任务
- 实现模块化设计
- 增加无线调试功能
这套代码作为基础方案已经能够很好地完成循迹任务,但要追求更高性能,还需要在上述方面进行改进。我在实际比赛中发现,稳定的图像处理和合理的参数调试往往比复杂的算法更能带来好的比赛成绩。