第18届全国大学生智能汽车竞赛四轮车开源讲解【2】--图像处理实战:从二值化到抗干扰

张开发
2026/4/10 13:25:11 15 分钟阅读

分享文章

第18届全国大学生智能汽车竞赛四轮车开源讲解【2】--图像处理实战:从二值化到抗干扰
1. 图像处理在智能车竞赛中的核心地位参加全国大学生智能汽车竞赛的同学们都知道图像处理是整个比赛中最关键的环节之一。想象一下你的智能车就像是一个在赛道上奔跑的小司机而摄像头就是它的眼睛。这双眼睛能不能看清赛道直接决定了车子能不能跑得又快又稳。在实际比赛中我们主要使用灰度摄像头采集赛道图像。与彩色图像相比灰度图像虽然少了颜色信息但处理起来更简单快速。每个像素点只需要一个0-255的数值来表示明暗程度0代表纯黑255代表纯白。这种简化对于需要实时处理的智能车系统来说至关重要。我见过很多队伍在图像处理环节栽跟头。有的因为二值化效果不好导致车子误判赛道边缘有的因为抗干扰能力差在比赛现场光线变化时表现失常。这些问题往往不是硬件性能不足造成的而是图像处理算法不够健壮。下面我就结合自己参赛和指导的经验分享几个关键要点。2. 图像采集与参数设置实战2.1 摄像头数据采集基础智能车常用的MT9V03X摄像头采集到的图像数据通常存储在一个二维数组中。这个数组的大小由图像分辨率决定比如常见的180×70。在代码中我们通常会看到这样的定义volatile uint8 mt9v03x_finish_flag 0; // 图像采集完成标志位 uint8 mt9v03x_image[MT9V03X_H][MT9V03X_W]; // 图像数据数组标志位的作用很关键。当摄像头完成一帧图像的采集后会将标志位置1这时我们的程序就可以读取这帧图像进行处理。处理完成后需要清除标志位让摄像头开始下一帧的采集。这里有个小技巧建议先处理图像再清除标志位这样可以避免在处理过程中图像数据被新帧覆盖。2.2 坐标系与数组访问注意事项图像处理中最容易出错的往往是边界条件。智能车使用的图像坐标系通常以左上角为原点(0,0)x轴向右y轴向下。这与数学中常见的直角坐标系不同需要特别注意。在遍历图像数组时边界处理尤为重要。以下两种写法都是正确的但千万不要混淆// 方式一使用比较 for(int i0; iMT9V03X_H; i) { for(int j0; jMT9V03X_W; j) { // 处理像素 } } // 方式二使用比较 for(int i0; iMT9V03X_H-1; i) { for(int j0; jMT9V03X_W-1; j) { // 处理像素 } }数组越界是调试时最难发现的bug之一。它可能不会立即导致程序崩溃但会造成数据处理异常让你误以为是算法出了问题。建议在开发阶段就加入边界检查代码。3. 二值化算法深度解析3.1 大津法(Otsu)原理与优化大津法是智能车竞赛中最常用的自动阈值算法。它的核心思想是通过分析图像灰度直方图找到一个最佳阈值将图像分为前景(赛道)和背景两部分。算法步骤可以简单理解为统计图像中每个灰度级出现的概率遍历所有可能的阈值(0-255)计算类间方差选择使类间方差最大的阈值作为最佳阈值在实际应用中我发现可以对标准大津法做一些优化// 优化版大津法 - 隔行隔列采样 for (i 0; i height; i2) { for (j 0; j width; j2) { pixelCount[image[i][j]]; gray_sum image[i][j]; } }这种采样方式可以减少75%的计算量而对阈值结果影响很小。在智能车这种实时性要求高的场景下非常实用。3.2 Sobel算子边缘检测Sobel算子是一种基于梯度的边缘检测算法。它通过计算图像中每个像素点周围区域的灰度变化来检测边缘。相比大津法Sobel算子对光照变化的适应性更强但计算量也更大。Sobel算子的核心是两个3×3的卷积核分别用于检测水平和垂直方向的边缘Gx [-1 0 1] Gy [-1 -2 -1] [-2 0 2] [ 0 0 0] [-1 0 1] [ 1 2 1]实际应用中我们可以只使用其中一个方向或者将两个方向的结果结合。下面是一个简化版的实现void sobel_edge_detect(uint8_t input[][MT9V03X_W], uint8_t output[][MT9V03X_W]) { for(int i1; iMT9V03X_H-1; i) { for(int j1; jMT9V03X_W-1; j) { int gx -input[i-1][j-1] input[i-1][j1] -2*input[i][j-1] 2*input[i][j1] -input[i1][j-1] input[i1][j1]; int gy -input[i-1][j-1] - 2*input[i-1][j] - input[i-1][j1] input[i1][j-1] 2*input[i1][j] input[i1][j1]; int magnitude sqrt(gx*gx gy*gy); output[i][j] (magnitude THRESHOLD) ? 255 : 0; } } }4. 进阶图像处理技巧4.1 图像压缩技术为了平衡处理速度和图像细节很多队伍会使用图像压缩技术。基本原理是按比例缩小图像尺寸比如将180×70的图像压缩为90×35。这样处理的数据量减少为原来的1/4可以显著提高处理速度。实现时需要注意压缩比例最好是整数倍避免引入畸变可以采用简单的均值采样或最大值采样保留原始高分辨率图像用于特殊场景处理void image_compress(uint8_t src[][MT9V03X_W], uint8_t dst[][COMPRESS_W], int ratio) { for(int i0; iCOMPRESS_H; i) { for(int j0; jCOMPRESS_W; j) { int sum 0; for(int m0; mratio; m) { for(int n0; nratio; n) { sum src[i*ratiom][j*ration]; } } dst[i][j] sum / (ratio*ratio); } } }4.2 灰度巡线技术相比二值化处理直接使用灰度图像巡线可以保留更多信息对光照变化的适应性更强。基本原理是检测每行像素的灰度跳变点作为赛道边界。灰度巡线的优势包括避免二值化阈值选择不当导致的信息丢失对光照不均的情况更鲁棒可以检测到微弱的赛道边缘实现时可以通过计算灰度梯度来增强边缘特征int find_edge(uint8_t line[], int width) { int max_grad 0; int edge_pos 0; for(int i1; iwidth-1; i) { int grad abs(line[i1] - line[i-1]); if(grad max_grad) { max_grad grad; edge_pos i; } } return (max_grad GRAD_THRESH) ? edge_pos : -1; }5. 实战抗干扰方案5.1 光线干扰处理比赛现场的光线条件往往不理想常见问题包括光线不均匀导致图像部分过亮或过暗反光造成局部区域过曝环境光变化导致阈值失效解决方案可以从硬件和软件两方面入手硬件方案使用遮光罩减少环境光干扰加装偏振片消除特定角度反光调整摄像头曝光参数适应不同光照软件方案动态阈值调整算法图像分区处理对不同区域使用不同阈值增加图像滤波预处理5.2 偏振片的正确使用偏振片是解决反光问题的利器但使用不当效果会大打折扣。正确使用方法包括将偏振片安装在摄像头镜头前旋转偏振片角度观察实时图像找到最佳位置固定偏振片角度必要时标记参考位置实际测试表明合适的偏振片可以消除90%以上的反光干扰。选购时注意尺寸要能完全覆盖摄像头视野透光率不宜过低避免图像过暗最好准备不同角度的备用品5.3 动态阈值算法针对光照变化可以实现在线阈值调整。常见方法有基于图像均值的动态阈值分区域统计局部阈值结合历史帧信息的自适应阈值一个简单的实现示例int dynamic_threshold(uint8_t image[][MT9V03X_W]) { int sum 0; int count 0; // 采样部分像素计算均值 for(int i0; iMT9V03X_H; i5) { for(int j0; jMT9V03X_W; j5) { sum image[i][j]; count; } } int mean sum / count; return mean * 0.8; // 经验系数 }6. 工程实践建议在实际调车过程中我发现这些经验特别有价值参数调节要有方法论先固定其他参数每次只调节一个变量记录每次修改的效果和参数值建立参数配置文件方便快速切换测试要全面在不同光照条件下测试模拟比赛现场的极端情况记录异常情况下的图像数据用于分析代码要模块化图像采集、处理、控制逻辑分离关键算法封装成独立函数保留调试接口和日志输出重视数据记录保存问题场景的原始图像记录处理过程中的中间结果建立典型场景的测试用例库在最后的比赛准备阶段建议做这些检查确认所有参数都有合理的默认值准备多套参数应对不同光照条件测试摄像头在不同温度下的稳定性确保备用硬件与软件兼容图像处理算法的优化是个持续的过程。即使是比赛前一天也可能会发现新的改进点。但要注意避免过度优化局部而影响整体稳定性。保持算法的简洁和可靠往往比追求极致的性能更重要。

更多文章