1. 编程语言选择C与Python的实战定位很多刚接触ROBOMASTER视觉组的新同学第一个纠结的问题就是我到底该学C还是Python这个问题我当年也纠结过现在回头看其实答案很明确——两个都要学但定位不同。C在比赛中是绝对的“主力选手”。几乎所有强队的开源代码都是C写的因为它的性能优势太明显了。视觉算法需要实时处理高清视频流每帧图像可能有上百万个像素点要处理还要做复杂的矩阵运算、特征提取、目标跟踪。Python虽然写起来爽但在这个场景下速度就是生命。我实测过同样的装甲板识别算法用C实现能轻松跑到60帧以上用Python可能连30帧都吃力。比赛时对方机器人可不会等你慢慢算帧率低就意味着瞄准延迟大打不中。但Python也不是摆设。它的优势在于“快速验证”和“辅助工具”。比如你想试试某个新的深度学习模型用Python的PyTorch或TensorFlow几行代码就能搭起来训练。等模型效果验证OK了再考虑用C部署到实际机器人上。我们组里经常用Python写一些数据标注工具、参数调试界面或者快速验证某个数学公式是否正确。简单说C是上战场的武器Python是后方的兵工厂。我建议的学习路径是零基础从Python入门有基础直接冲C。如果你完全没编过程Python的语法友好能让你快速建立编程思维看到自己的代码跑出结果这种正反馈很重要。但记住Python只是跳板最终一定要过渡到C。我们组里有个经典笑话“只会Python的视觉队员就像只会用筷子吃西餐——不是不行但总觉得哪里不对劲。”2. C学习从“类”到实战的跨越很多教程一上来就让你把C的面向对象、模板、STL全学一遍我觉得这不太对。对于比赛来说你不需要成为C专家你需要的是“够用就行快速上手”。我当年学C看到“类”这个概念后就直接开始写OpenCV的程序了。是的你没听错我连继承、多态都没搞明白就已经在写图像处理的代码了。为什么因为比赛不考语法考的是解决问题的能力。你需要知道的是怎么用cv::Mat存储一张图片怎么用cv::imshow显示它怎么用for循环遍历像素点找灯条。这里我踩过一个坑过早陷入“语法完美主义”。总想着要把代码写得像教科书一样优雅用了各种设计模式结果一个简单的颜色识别函数写了三天。后来学长一句话点醒我“比赛还有两个月你是要漂亮的代码还是要能打中人的代码”从此我转变思路——先实现功能再考虑优化。我推荐的学习资源是B站上清华大学的C课程讲得很系统。但看视频时一定要动手。我的方法是每看完一集就立刻在Visual Studio里把例子敲一遍然后改改参数看看输出有什么变化。比如学到指针我就自己写个小程序用指针操作一个图像数组看看能不能把图片反色。这种“学一点用一点”的方式记忆最深刻。等你掌握了基本语法就要立刻开始“项目式学习”。不要等学完所有C知识再开始那太晚了。直接去GitHub上找开源代码比如上海交大的CVRM2021-sjtu把代码下载下来尝试在你自己电脑上编译运行。一开始肯定会报一堆错缺这个库那个依赖但这个过程本身就是最好的学习。你会在解决一个个编译错误的过程中真正理解头文件、链接库、CMake这些概念。3. Python学习聚焦OpenCV与快速原型Python在视觉组里的核心用途就两个快速原型验证和深度学习。对于前者OpenCV-Python是必须掌握的利器。安装OpenCV-Python很简单一行命令pip install opencv-python但这里有个版本坑要注意。很多老教程会让你装OpenCV 3.x但现在我强烈推荐OpenCV 4.5以上。新版本不仅修复了很多bug还加入了对深度学习模型更好的支持。Python版本也别用太老的Python 3.6已经快退役了现在用Python 3.8或3.9比较稳妥生态兼容性好。学OpenCV不要从头到尾啃官方文档那有几千页看到猴年马月。我建议直接瞄准几个比赛最常用的功能猛攻图像基本操作读取、显示、保存、颜色空间转换RGB转HSV太常用了图像预处理高斯模糊、二值化、形态学操作开运算、闭运算轮廓查找与筛选cv::findContours找灯条轮廓然后按面积、长宽比、凸度筛选相机标定张正友标定法用cv::calibrateCamera求内参和畸变系数PnP解算用cv::solvePnP把2D图像点转换成3D世界坐标我当年写第一个装甲板识别程序就只用了这五个部分的功能。代码大概200行虽然简陋但真的能识别出装甲板。这种“最小可行产品”的思路很重要——先做出一个能跑的版本哪怕识别率只有50%然后再慢慢优化。除了OpenCVPython在数据预处理和可视化上也很有用。比如你可以用matplotlib画个图看看不同曝光参数下图像的亮度分布用numpy快速计算一些统计特征用jupyter notebook交互式地调试算法参数。这些工具能极大提升你的调试效率。4. Ubuntu环境配置双系统还是虚拟机这是新人最容易崩溃的环节没有之一。我见过太多同学在装系统这一步就放弃了。但说实话只要你按步骤来真没那么难。首选方案绝对是双系统。虚拟机性能损失太大而且显卡直通、USB设备访问都是一堆麻烦。我们做视觉处理很吃CPU和内存虚拟机里跑OpenCV帧率直接砍半。双系统虽然安装时麻烦点但一劳永逸。安装双系统的关键就三步制作启动U盘用RufusWindows或dd命令Linux把Ubuntu镜像写到U盘里。注意U盘至少8GB数据提前备份这个过程会清空U盘。分配磁盘空间在Windows的磁盘管理里压缩出一个至少50GB的空间建议100GB以上。不用格式化留空就行。安装过程开机按F2/F12/Del不同电脑不一样进BIOS设置U盘启动。安装时选“其他选项”手动分区。我的一般分法是/根目录30-50GB放系统和程序/home剩余空间的大部分放你的代码和数据swap交换分区内存大小的1-2倍比如你16GB内存就分16-32GB/boot500MB放启动引导这里有个大坑引导安装位置。一定要把引导器安装到整个硬盘比如/dev/sda而不是某个分区。否则很可能出现装完Ubuntu后Windows进不去的情况。如果真遇到了也别慌用U盘进Ubuntu Live模式装个boot-repair工具一般都能修复。版本选择上虽然很多老教程推荐Ubuntu 16.04但我现在更推荐Ubuntu 20.04 LTS。18.04已经停止支持了20.04有更久的维护期而且对新手更友好。最重要的是ROS NoeticROS1最后一个版本和ROS2 Foxy都官方支持20.04后续想玩ROS也方便。5. 开发环境搭建从编辑器到编译工具链系统装好了接下来要搭开发环境。这里我分享一套自己用了多年的“黄金组合”。代码编辑器VS Code是首选。轻量、插件多、对C和Python支持都好。必装的插件有C/C微软官方出品智能提示、跳转定义Python同样微软出品CMake Tools如果你用CMakeGitLens看Git历史方便编译工具CMake是标配。现在很少有视觉项目用纯Makefile了CMake跨平台语法也更清晰。刚开始学CMake可能会觉得有点绕但其实核心就几个命令cmake_minimum_required(VERSION 3.10) project(YourProjectName) find_package(OpenCV REQUIRED) find_package(Eigen3 REQUIRED) add_executable(main src/main.cpp) target_link_libraries(main ${OpenCV_LIBS} ${Eigen3_LIBS})这就是一个最简单的CMakeLists.txt找OpenCV和Eigen3库然后编译一个可执行文件。更复杂的项目无非就是多几个add_executable和target_link_libraries。版本控制Git必须会。不要觉得“我就一个人写代码用不到”。等你代码改崩了想回退或者想在两个功能之间切换测试时Git能救你的命。最基本的工作流git init # 初始化仓库 git add . # 添加所有文件 git commit -m 第一次提交 # 提交 git branch new_feature # 创建新分支 git checkout new_feature # 切换到新分支平时就把GitHub当网盘用代码写完就push上去。我们组有一次比赛前夜学弟的电脑硬盘坏了代码全丢。幸好他前一天push过从GitHub上拉下来就能继续调不然真得现场重写。6. 核心库安装OpenCV与Eigen3的编译指南环境搭好了现在要装视觉算法的“左膀右臂”——OpenCV和Eigen3。OpenCV安装我强烈建议从源码编译而不是用apt直接装。apt装的版本通常比较老而且可能缺一些contrib模块。编译过程看起来复杂其实就几步# 1. 安装依赖 sudo apt update sudo apt install build-essential cmake git pkg-config libgtk-3-dev \ libavcodec-dev libavformat-dev libswscale-dev libv4l-dev \ libxvidcore-dev libx264-dev libjpeg-dev libpng-dev libtiff-dev \ gfortran openexr libatlas-base-dev python3-dev python3-numpy \ libtbb2 libtbb-dev libdc1394-22-dev # 2. 下载源码建议用国内镜像快很多 git clone https://gitee.com/mirrors/opencv.git git clone https://gitee.com/mirrors/opencv_contrib.git # 3. 编译安装 cd opencv mkdir build cd build cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D OPENCV_EXTRA_MODULES_PATH../../opencv_contrib/modules \ -D WITH_CUDAOFF \ # 如果你有NVIDIA显卡且需要CUDA加速可以ON -D BUILD_EXAMPLESOFF \ -D BUILD_opencv_python3ON \ -D PYTHON3_EXECUTABLE$(which python3) \ -D PYTHON3_INCLUDE_DIR$(python3 -c from distutils.sysconfig import get_python_inc; print(get_python_inc())) \ -D PYTHON3_PACKAGES_PATH$(python3 -c from distutils.sysconfig import get_python_lib; print(get_python_lib())) .. make -j$(nproc) # nproc是你CPU的核心数比如8核就用-j8 sudo make install编译过程可能要半小时到一小时取决于你电脑性能。完成后可以用pkg-config检查是否安装成功pkg-config --modversion opencv4如果显示版本号比如4.5.5那就成功了。Eigen3安装这个简单Eigen是纯头文件库没有.so动态库需要编译sudo apt install libeigen3-dev安装后头文件通常在/usr/include/eigen3。在CMakeLists.txt里用find_package(Eigen3 REQUIRED)就能找到。这里有个实际项目中的CMakeLists.txt例子你可以参考cmake_minimum_required(VERSION 3.10) project(armor_detector) set(CMAKE_CXX_STANDARD 14) find_package(OpenCV REQUIRED) find_package(Eigen3 REQUIRED) include_directories(${EIGEN3_INCLUDE_DIRS}) add_executable(armor_detector src/main.cpp src/armor_finder.cpp) target_link_libraries(armor_detector ${OpenCV_LIBS}) # 如果要用Python调用C可以加pybind11 find_package(pybind11 REQUIRED) pybind11_add_module(armor_pybind src/python_bindings.cpp) target_link_libraries(armor_pybind PRIVATE armor_detector)这个配置编译出的C程序可以直接被Python导入实现混合编程。7. 实战项目从零搭建装甲板识别程序理论说了这么多现在来点实际的。我们用一个最简单的装甲板识别程序把前面学的串起来。这个程序虽然简陋但包含了完整的流程图像采集→预处理→特征提取→目标输出。首先你需要一个相机。比赛常用的是大疆的官方相机或者MindVision的工业相机。这里我用电脑摄像头模拟原理是一样的。#include opencv2/opencv.hpp #include iostream #include vector int main() { // 1. 打开摄像头 cv::VideoCapture cap(0); // 0是默认摄像头 if (!cap.isOpened()) { std::cerr 无法打开摄像头 std::endl; return -1; } // 设置分辨率比赛常用1280x720或640x480 cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); cv::Mat frame; while (true) { cap frame; if (frame.empty()) break; // 2. 颜色空间转换RGB转HSV更好分离颜色 cv::Mat hsv; cv::cvtColor(frame, hsv, cv::COLOR_BGR2HSV); // 3. 颜色阈值分割识别红色或蓝色装甲板 // 红色在HSV中有两个区间因为红色在色环两端 cv::Mat mask_red1, mask_red2, mask_red; cv::inRange(hsv, cv::Scalar(0, 100, 100), cv::Scalar(10, 255, 255), mask_red1); cv::inRange(hsv, cv::Scalar(160, 100, 100), cv::Scalar(180, 255, 255), mask_red2); cv::bitwise_or(mask_red1, mask_red2, mask_red); // 蓝色装甲板 cv::Mat mask_blue; cv::inRange(hsv, cv::Scalar(100, 100, 100), cv::Scalar(130, 255, 255), mask_blue); // 4. 形态学操作去除噪声连接相邻区域 cv::Mat kernel cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); cv::morphologyEx(mask_red, mask_red, cv::MORPH_CLOSE, kernel); cv::morphologyEx(mask_blue, mask_blue, cv::MORPH_CLOSE, kernel); // 5. 查找轮廓 std::vectorstd::vectorcv::Point contours_red, contours_blue; cv::findContours(mask_red, contours_red, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); cv::findContours(mask_blue, contours_blue, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // 6. 轮廓筛选按面积、长宽比等 std::vectorcv::RotatedRect light_bars; // 存储灯条 for (const auto contour : contours_red) { double area cv::contourArea(contour); if (area 100) continue; // 面积太小可能是噪声 cv::RotatedRect rect cv::minAreaRect(contour); float width rect.size.width; float height rect.size.height; float ratio (width height) ? width / height : height / width; // 灯条通常长宽比2.5 if (ratio 2.5 ratio 8.0) { light_bars.push_back(rect); cv::Point2f vertices[4]; rect.points(vertices); for (int i 0; i 4; i) { cv::line(frame, vertices[i], vertices[(i1)%4], cv::Scalar(0, 255, 0), 2); } } } // 同样的处理对蓝色轮廓... // 7. 灯条配对形成装甲板 // 这里简化处理实际比赛需要更复杂的配对逻辑 for (size_t i 0; i light_bars.size(); i) { for (size_t j i1; j light_bars.size(); j) { // 计算两个灯条的角度、距离等判断是否配对 // ... // 如果配对成功画矩形框 // cv::rectangle(frame, ...); } } // 显示结果 cv::imshow(Armor Detection, frame); if (cv::waitKey(30) q) break; } cap.release(); cv::destroyAllWindows(); return 0; }这个代码只有100多行但已经包含了装甲板识别的核心逻辑。你可以把它保存为armor_detector.cpp然后用下面的CMakeLists.txt编译cmake_minimum_required(VERSION 3.10) project(armor_detector) find_package(OpenCV REQUIRED) add_executable(armor_detector armor_detector.cpp) target_link_libraries(armor_detector ${OpenCV_LIBS})编译运行mkdir build cd build cmake .. make ./armor_detector如果一切正常你应该能看到摄像头画面并且绿色的框会框出识别到的灯条。这就是你第一个视觉程序的起点。8. 调试与优化让识别更稳更准程序能跑了但你会发现问题一大堆光线稍暗就识别不到反光会误识别快速移动时框框乱跳。别急这才是视觉算法的常态。接下来我们要进入最磨人但也最有成就感的阶段——调试优化。第一步参数可视化调试。不要每次改参数都重新编译运行太慢了。我用的是最土但最有效的方法用trackbar滑块实时调整。在OpenCV里加几行代码cv::namedWindow(Control); cv::createTrackbar(H min, Control, h_min, 180); cv::createTrackbar(H max, Control, h_max, 180); cv::createTrackbar(S min, Control, s_min, 255); // ... 更多参数这样你就能在程序运行时用鼠标拖动滑块实时看识别效果变化。我经常一调就是几个小时记录下不同光照条件下的最佳参数然后写个自动调节的逻辑。第二步日志输出。光看图像不够还要看数据。我在关键位置加了很多std::cout输出比如“找到X个轮廓”、“灯条长宽比X.Y”、“配对成功装甲板中心在(123,456)”这样的信息。后来发现控制台输出太乱就改成了写日志文件用fstream把每帧的数据都记下来事后分析。第三步性能分析。识别算法不仅要准还要快。用chrono库给每个函数计时#include chrono auto start std::chrono::high_resolution_clock::now(); // ... 你的代码 ... auto end std::chrono::high_resolution_clock::now(); auto duration std::chrono::duration_caststd::chrono::milliseconds(end - start); std::cout 函数耗时: duration.count() ms std::endl;你会发现80%的时间可能花在了某几个函数上。比如cv::findContours就很耗时如果一帧图像里有太多噪声点轮廓查找就会变慢。这时候就要在之前加更严格的滤波或者用多线程——把图像分成几块并行处理。第四步应对极端情况。比赛现场什么光都可能遇到场馆顶灯、窗户阳光、对方机器人的LED干扰。我的经验是准备多套参数根据图像平均亮度自动切换。比如float mean_brightness cv::mean(frame)[0]; if (mean_brightness 50) { // 暗光环境用高灵敏度参数 s_min 50; v_min 30; } else if (mean_brightness 200) { // 强光环境防止过曝 s_min 100; v_max 200; } else { // 正常光照 s_min 80; v_min 60; }还有运动模糊的问题。机器人快速移动时图像会糊灯条就拉长了。这时候要动态调整长宽比的阈值范围或者用卡尔曼滤波预测下一帧的位置减少搜索范围。9. 进阶方向传统视觉与深度学习的结合传统视觉方法像上面那个程序在规则明确、场景固定的比赛里很有效但它有个硬伤调参太痛苦。换个场地、换个灯光参数可能就不好使了。所以现在越来越多的队伍开始用深度学习或者两者结合。纯深度学习方案比如用YOLO直接检测装甲板。好处是鲁棒性强只要训练数据够多各种光照条件都能适应。但缺点也很明显需要大量标注数据模型计算量大虽然可以用TensorRT加速而且如果比赛规则突然改了比如装甲板样式变化模型就得重新训练。传统深度学习结合这是我比较看好的方向。比如上海交大开源的“四点模型”用YOLO检测装甲板的四个角点然后用传统的PnP解算3D位置。这样既利用了深度学习对光照的鲁棒性又保留了传统几何方法的精确性。他们的代码在GitHub上开源了你可以搜CVRM2021-sjtu看看。如果你想尝试深度学习我的建议是先从简单的开始别一上来就搞YOLOv5、v8先用个轻量级的模型比如MobileNet SSD在PC上跑通了再移植到Jetson这类嵌入式平台。自己制作数据集网上有一些公开的RM数据集但最好自己拍。用手机对着装甲板各个角度拍几百张然后用LabelImg标注。注意要涵盖不同光照、不同距离、不同角度。训练技巧数据增强很重要。随机旋转、缩放、调整亮度对比度能让模型更健壮。学习率不要太大用余弦退火或者OneCycle策略。部署优化PC上训练好的模型直接放到NX上跑可能很慢。要用TensorRT或OpenVINO做推理优化把FP32转成FP16甚至INT8速度能提升好几倍。我们组去年尝试过用YOLOv5-tiny在NX上能跑到50帧识别率比传统方法高不少尤其是对方机器人部分被遮挡时。但最后还是没完全替换传统方法而是两者融合先用传统方法快速初筛再用神经网络对候选区域做二次判断。这样既保证了速度又提高了准确率。10. 硬件平台选择从树莓派到NUC的实战体验视觉算法最终要跑在真实的硬件上。新人常问“我用树莓派行不行”我的回答是学习可以比赛不行。我最早就是用树莓派4B入门的。装个Ubuntu Mate跑OpenCV写个简单的颜色识别确实能跑起来。但一旦上真实比赛场景问题就来了树莓派的CPU性能不够处理640x480的图像都吃力帧率上不去USB摄像头有延迟图像传过来可能已经过了几十毫秒更重要的是树莓派是ARM架构很多库要自己交叉编译调试起来很麻烦。比赛用的硬件目前主流就两个选择英特尔的NUC和英伟达的Jetson系列。NUC就是个小电脑x86架构和你用的笔记本没区别。好处是兼容性好什么软件都能装调试方便。我用的NUC11i7处理器32GB内存跑视觉算法绰绰有余。但NUC有个问题没有GPU深度学习推理只能靠CPU速度慢。如果你要用深度学习就得考虑Jetson。Jetson是英伟达专门为边缘AI设计的平台自带GPU。NX、AGX Xavier这些性能很强能跑复杂的神经网络。但ARM架构的坑也不少软件生态不如x86有些库要自己编译散热要做好不然容易降频价格也贵。我的建议是新手先用NUC。等传统视觉玩熟了再考虑上Jetson做深度学习。我们队里现在是NUC和NX混用NUC跑传统算法稳定可靠NX跑深度学习模型处理复杂场景。如果你决定用NUC有几点要注意散热NUC很小散热是问题。最好加个散热底座或者自己改一下散热。我们有一次比赛NUC过热降频帧率从60掉到20差点翻车。供电比赛机器人上是电池供电电压可能不稳。给NUC配个好点的DC-ATX电源模块12V转19V那种。接口NUC的USB口可能不够用。相机一个串口一个可能还要接键盘鼠标。买个带独立供电的USB Hub不然可能供电不足导致相机掉线。硬件这东西一分钱一分货。但也不是越贵越好关键看需求。如果你只是做自瞄NUC足够如果想做更复杂的识别、多传感器融合那Jetson更适合。