练习:运行第一个传统视觉识别器
使用 Daedalus 模拟器和 C++ SDK 取图,实时观察灯条候选与配对角点。
这一节提供一个可以直接运行的传统视觉参考程序。它从 Daedalus 模拟器读取图像,完成颜色差与亮度阈值、形态学处理、轮廓筛选、灯条拟合和灯条配对,并将每一步画到窗口中。程序只识别灯条及其几何关系,不识别数字。
运行它的目的不是直接得到比赛代码,而是让你能立刻看见:一个阈值改变后,二值图、候选灯条和最终四个角点分别发生了什么。
1. 实验使用的 Release 与接口
从 Daedalus Simulator Releases 仓库获取最新稳定的完整 Release,并使用同一个包随附的 C++ SDK、标定和文档。也可以从全部 Releases选择需要复现的历史版本:
- Windows:
windows-x86_64.zip; - Ubuntu 24.04:优先使用
linux-x86_64.tar.gz,也可以使用 ZIP。
本练习只依赖 Daedalus SDK v1 的稳定接口;实际模拟器和 SDK 版本以下载包的 release.json 为准,不要跨版本混用。
程序只通过公开 SDK 使用模拟器:
| 接口 | 地址 | 本实验中的作用 |
|---|---|---|
SceneControlClient | 127.0.0.1:5603/UDP | 创建实验会话、切换 Shooting Range、控制靶车运动 |
TcpImageClient | 127.0.0.1:5602/TCP | 读取带帧号和曝光时间戳的最新完整图像 |
camera-calibration.json | Release 根目录 | 提供固定只读的相机内参、畸变与相机外参 |
图像流采用 latest-only 语义:识别处理较慢时,中间帧会被跳过,程序得到的是最新完整帧。source_sequence 是帧身份,capture_timestamp_ns 是曝光时间;两者都应跟随识别结果继续向下游传递。
2. 启动模拟器
完整解压 Release,不要只拿出 bin/。第一次练习使用可视模式,便于同时观察场景和算法窗口。
Windows
cd <Daedalus发布目录>
Set-ExecutionPolicy -Scope Process Bypass
./start-simulator.ps1 -VisibleLinux
cd <Daedalus发布目录>
chmod +x ./start-simulator.sh ./bin/daedalus
./start-simulator.sh --visible模拟器启动后保持运行。练习程序会通过 SDK 自动切换到 ShootingRange。
3. 固定相机参数
本实验使用 Release 根目录的 camera-calibration.json,标定 ID 为 daedalus-camera-1440x1080-v2,修订号为 2。图像尺寸和像素格式:
width = 1440
height = 1080
TCP 默认格式 = RGBA32相机内参矩阵:
K = [1303.67532368147, 0, 720.0;
0, 1303.67532368147, 540.0;
0, 0, 1.0]畸变系数采用 plumb_bob 顺序:
D = [k1, k2, p1, p2, k3] = [0, 0, 0, 0, 0]这个练习输出灯条端点构成的二维角点,暂时不调用 PnP;后续模块直接沿用同一份标定进入位姿解算。
4. 获取参考程序
源码可从教程站直接打开:examples/first-traditional-detector。该页静态发布完整的 README.md、CMakeLists.txt 与 src/main.cpp,不依赖 GitHub 仓库权限。它已经完成:
- 建立场景控制会话并切换
ShootingRange; - 使用
TcpImageClient::waitForLatest()读取图像; - 根据帧头把 RGB24 / RGBA32 转换为 OpenCV BGR;
- 构造颜色差与亮度二值图,查找和筛选灯条轮廓;
- 将候选灯条两两配对,输出固定顺序的四个灯条端点;
- 显示原图、二值图和参数滑条;
- 按
1使用静止目标,按2使用直线运动,按3使用直线并自转。
核心入口是:
// 颜色掩码 → 轮廓 → 灯条 → 配对角点。
DetectionResult runTraditionalDetector(const cv::Mat& bgr,
const DetectorParameters& parameters);5. 构建练习程序
需要 C++17、CMake 3.16、OpenCV 4,以及 Release 自带的预编译 SDK。
Linux
sudo apt update
sudo apt install -y build-essential cmake ninja-build libopencv-dev
cmake -S examples/first-traditional-detector \
-B build/first-traditional-detector \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=<Daedalus发布目录>/sdk
cmake --build build/first-traditional-detector --parallel
./build/first-traditional-detector/first_traditional_detectorWindows
先通过 vcpkg 安装 OpenCV:
C:/dev/vcpkg/vcpkg install opencv4:x64-windows在 Visual Studio Developer PowerShell 中配置和构建:
cmake -S examples/first-traditional-detector `
-B build/first-traditional-detector `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_PREFIX_PATH=<Daedalus发布目录>/sdk `
-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build/first-traditional-detector --config Release --parallel
./build/first-traditional-detector/Release/first_traditional_detector.exe6. 先观察可视化结果
程序启动后检查:
- 模拟器自动进入 Shooting Range;
detector窗口持续显示 1440×1080 图像;mask窗口显示由颜色和亮度规则产生的二值图;- 橙色矩形是灯条候选,黄色线是灯条长轴;
- 绿色四边形和圆点是两灯条配对得到的四个角点;
- 左上角的
seq与timestamp_ns随新帧更新; - 按
1、2、3时目标运动状态发生变化。
这一步已经跑通“场景控制 → 图像接口 → OpenCV → 可视化”的最小链路。
7. 修改像素与灯条规则
程序默认通过下列规则提取红色灯条;enemy: red / blue 滑条可切换颜色差的方向:
color_score = R - B
bright_score = gray
mask = (color_score > color_difference_threshold)
AND
(bright_score > brightness_threshold)拖动 brightness、color difference、morphology radius 和 minimum area 滑条。每次只改一个参数,观察:
- 灯条是否保持完整,是否因为阈值或形态学操作断裂、粘连;
- 背景是否产生新的白色区域和候选灯条;
- 哪些候选灯条没有通过几何配对;
- 绿色四点是否连续,还是发生跳变与错误配对。
接着阅读 runTraditionalDetector():它使用 cv::findContours() 和 cv::minAreaRect() 得到候选灯条,并比较两灯条的长度比、长轴角度差、中心高度差和间距。改变其中任意一个阈值后,重新运行相同场景,记录输出变化。
8. 主动制造失败
按以下顺序完成三组实验,每次保留滑条参数和一张截图:
| 实验 | 操作 | 观察重点 |
|---|---|---|
| 静止基线 | 按 1,调整参数直到稳定识别 | 哪些规则真正必要,哪些规则只是当前画面的巧合 |
| 直线运动 | 按 2,保持参数不变 | 轮廓、灯条角度和角点是否开始抖动 |
| 直线并自转 | 按 3,保持参数不变 | 哪个几何阈值最先导致漏检,放宽后又增加了什么误检 |
然后故意提高亮度阈值、降低颜色差阈值、扩大形态学核,分别观察漏检、误检和轮廓粘连。这个过程展示了传统视觉规则之间的取舍。
9. 验收标准
- 程序能够通过 SDK 自动切换 Shooting Range 并持续读取图像;
- 能解释
source_sequence、capture_timestamp_ns和 latest-only 的含义; - 能说明橙色候选灯条、黄色长轴、绿色角点和二值图分别代表什么;
- 能修改颜色、亮度、轮廓与灯条配对至少四类规则;
- 输出固定顺序的四个二维灯条端点;
- 完成静止、直线、直线并自转三组实验;
- 保存至少两个失败案例,并指出是哪条规则造成漏检或误检。
完成后再进入神经网络章节。此时“为什么要从手写规则转向数据驱动模型”不再只是一个结论,而是你已经亲自遇到的问题。