外观
视觉组笔试题
RoboMaster视觉组招新测试题(难)
致机器人爱好者:
你好!欢迎来到RoboMaster视觉组核心队员的招新测试。
这份测试旨在全面评估你的工程实践能力、算法设计思想以及对机器人视觉领域的理解。我们期望你不仅能实现功能,更能展现出代码的健壮性、结构性以及对技术细节的深入思考。
请将所有任务的解决方案保存在一个私有的Git仓库中。我们高度重视你的README.md
文档,它应清晰地阐述你的设计决策、实现方法和遇到的挑战。完成后,请将仓库访问权限分享给负责的学长学姐。(仓库地址请在填写申请表单时写上去)
注意:
对于AI的使用我们不做限制,但是我们希望看到你的代码风格。
我们同时也不强求你能做完所有题目,尽力而为!如果编程题目未能完成,也需要写出你的思路与伪代码。
核心考核要求:统一的测试与验证方式
为了保证评估的公平和高效,我们要求所有编程任务都提供一个统一的、自动化的测试方式。
- 对于每个编程任务,你必须提供一个
test.sh
脚本,或是一个带有test
目标的Makefile
。 - 这个脚本/目标的功能是完整地编译、运行并验证你的代码。
- 对于库类型的代码(如C++类),我们鼓励你编写一个独立的
test.cpp
文件来调用和展示其功能。 - 学长学姐在评估时,会直接进入每个任务的文件夹,执行
bash test.sh
或make test
来验证你方案的正确性。 - 对于Python环境管理,我们希望你可以使用一些包管理器(uv,conda)。
期待看到你卓越的工程作品。
第一部分:理论理解题
背景: 我们期望你对技术方案有深入的理解和批判性思维。
任务: 请仔细阅读《了解CV和RoboMaster视觉组》文档,并根据你的理解回答以下问题(请将答案写在task1_theory/README.md
中):
文档分别介绍了传统的灯条匹配方法和基于CNN的目标检测方法来识别装甲板。
请结合文档内容,从鲁棒性、实时性、开发成本、可维护性四个维度,对比这两种技术路线的优缺点。
文档中多次提到了卡尔曼滤波器(Kalman Filter)。
请阐述为什么在进行目标运动预测时,需要使用卡尔曼滤波器这类工具,而不是简单地用两帧之间的位置差来计算速度?它在机器人运动预测任务中主要解决了什么问题?
文档在介绍 Focal Loss 时提到,one-stage检测器面临着严重的正负样本不均衡问题。
请解释什么是正负样本不均衡,以及为什么它会对模型的训练产生负面影响?Focal Loss是如何通过修改损失函数来缓解这个问题的?
文档深入探讨了CNN是否具有平移不变性 (Translation Invariance)。
请总结一下,为什么说池化层(Pooling)和步长不为1的卷积(Strided Convolution)是破坏CNN平移同变性(Equivariance)的主要原因?
solvePnP
是视觉组进行位姿估计的核心函数之一。请阐述:1)
solvePnP
函数的主要作用是什么? 2) 为了成功调用它,除了2D图像上的目标角点坐标外,我们还必须提供哪些关键的先验信息?
第二部分:工作站环境配置与部署
背景: 任何复杂的机器人软件都始于一个稳定、可复现的开发环境。
任务:
- 开发环境自动化: 编写一个
setup_env.sh
Bash脚本,用于自动化安装机器人开发所需的核心工具集:git
,cmake
,docker.io
等等。脚本需妥善处理相关用户权限,以及不同架构识别的问题。 - 工程版本控制: 使用Git创建一个基础项目结构(如"Hello, RoboMaster!" 程序),并配置合理的
.gitignore
规则。 - 应用的容器化封装: 为你的项目编写一个
Dockerfile
,并使用多阶段构建技术,实现轻量化的部署。 - 测试要求: 对于任务3,你的
test.sh
脚本应包含构建Docker镜像 (docker build
) 和运行容器 (docker run
) 的命令。
第三部分:机器人核心软件开发 (C++/Python 二选一)
背景: 机器人的"大脑"由无数高效、可靠的软件模块构成。本部分将深入探索构建这些模块所需的核心编程技术。
C++ 方向
- 并发传感器数据处理:
- 场景: 机器人的决策依赖于多种传感器(如相机、IMU)的数据。这些传感器以不同频率并发地产生数据,需要一个高效的框架来处理。
- 任务: 实现一个线程安全的队列类
ThreadPool
。 - 测试要求: 请提供一个
test.cpp
文件,其中main
函数要能创建多个生产者线程和消费者线程,并发地对ThreadPool
实例进行操作,以验证其线程安全性。你的test.sh
或Makefile
必须能编译并运行这个test.cpp
。
- 泛型编程与编译期优化:
- 场景: 在机器人调试过程中,我们经常需要打印各种不同类型的数据结构(向量、自定义消息等)。编写一个通用的、高效的打印工具非常有用。
- 任务: 编写一个通用的
print
函数,能够接受并打印任意数量、任意类型的参数,并能特化处理STL容器。 - 测试要求: 提供一个
test.cpp
文件,在其中调用你的print
函数,测试多种不同类型参数(如int
,double
,std::string
,std::vector<int>
)的打印效果。
- 资源管理与矩阵运算:
- 场景: 在机器人学中,从坐标变换到姿态表示,矩阵运算无处不在。一个高效且内存安全的矩阵类是基础。
- 任务: 实现一个仅可移动 (Move-Only) 的
Matrix
类,用于存储二维动态数组。 - 具体实现要求:
- 内存布局: 内部数据必须以行主序 (Row-Major Order) 的方式存储在一维动态数组中(例如
new double[rows * cols]
)。 - 构造函数: 实现一个构造函数,接收行数
rows
和列数cols
,并动态分配所需的内存(例如,使用new double[rows * cols]
)。 - 析构函数: 实现析构函数,正确释放构造函数中分配的内存,防止内存泄漏。
- 禁用拷贝: 使用
= delete
语法显式禁用拷贝构造函数和拷贝赋值运算符。 - 实现移动: 正确实现移动构造函数和移动赋值运算符。在移动操作后,原对象应处于一个有效的、可被安全析构的状态(例如,其内部指针为空)。
- 元素访问: 重载
operator()
,使得可以通过matrix(row, col)
的方式方便地访问和修改矩阵中的元素。其内部实现应类似于return data[row * cols + col];
。 - (选做) 提供
get_rows()
和get_cols()
成员函数返回矩阵的维度。
- 内存布局: 内部数据必须以行主序 (Row-Major Order) 的方式存储在一维动态数组中(例如
- 测试要求: 提供一个
test.cpp
- 展示
Matrix
的正确创建和销毁。 - 展示通过移动构造和移动赋值,将一个
Matrix
对象管理的资源“转移”给另一个对象。 - 验证其拷贝构造和拷贝赋值已被禁用(尝试调用应导致编译错误,你可以将这部分代码注释掉,并在注释中说明)。
- 展示元素访问功能。
- 展示
Python 方向
异步设备通信框架:
- 场景: 一个机器人通常需要通过网络与多个外部设备(如电机控制器、激光雷达、服务终端)进行通信。使用异步IO可以高效地处理这些并发的网络连接。
- 任务: 使用
asyncio
编写一个客户端,它能并发地连接多个预设的TCP服务端口。 - 测试要求: 你的
test.sh
脚本应首先在后台启动几个简单的netcat
服务端(例如nc -l -p 8001 &
),然后再运行你的Python客户端程序进行连接测试。
机器人行为插件化加载器:
- 场景: 为了让机器人执行不同任务(如巡航、抓取、规避),我们希望将其"技能"设计成可插拔的插件,在需要时动态加载。
- 任务: 利用元编程,实现一个
SkillManager
,它能自动发现并注册所有继承自BaseSkill
的子类。 - 测试要求: 提供一个
test.py
文件,在其中定义几个继承自BaseSkill
的插件类,然后实例化SkillManager
并调用插件,以展示其自动注册和调用功能。
高效处理海量机器人日志:
场景: 机器人在运行时会产生巨大的日志文件(类似ROS bag),可能达到数GB,无法一次性加载到内存中分析。
任务: 编写一个脚本,使用生成器 (Generator) 来逐行读取并分析一个大型日志文件。脚本需要能在常数级内存消耗下,统计出包含关键词“ERROR”的日志条目数量。
测试: 我将提供一个1GB的log文件给你,你需要用这个文件对你的程序进行测试。
第四部分:基础算法与数据结构(C++/Python 二选一)
背景: 路径规划是机器人自主导航的核心技术之一。本部分要求你实现经典的A*搜索算法,为机器人在复杂的环境中找到最优路径。
任务:实现A*路径规划算法
场景描述: 在一个由二维栅格地图表示的环境中,一个机器人需要从起点(Start)移动到终点(Goal),只能直行,一次走一格。地图中包含无法通行的障碍物(Obstacles)。
实现要求:
任务: 你的程序必须可以通过命令行接收参数,格式为:
./your_program_name <map_file_path> <start_x> <start_y> <goal_x> <goal_y>
。正式测试文件
map.txt
内容: 请在你的项目中创建map.txt
文件,并使用以下内容:(1为障碍物)如不能通行,需要输出“I can’t go to the postion (x,y).”(x,y为你输入的goal_x和goal_y)0 0 1 0 0 0 1 0 1 0 0 1 0 0 1 1 1 0 1 1 0 0 0 1 0
输出: 程序需在标准输出打印出文本化的地图,并用
*
标记出从起点到终点的路径。测试要求: 你的
test.sh
或Makefile
应该能编译程序,并使用map.txt
和指定的起止点(例如,从 (0,0) 到 (4,4))来运行它。
第五部分:机器人系统集成与视觉算法
背景: 在现代机器人系统中,独立的软件模块需要通过通信框架(如ROS2)进行集成。本部分旨在考察你构建一个基本机器人感知应用的能力。
任务:创建并实现一个基础的视觉追踪ROS2包
总体目标: 创建一个名为 rm_vision_sim
且功能完整的ROS2功能包。该包将包含三个节点,它们协同工作,模拟一个机器人摄像头捕捉、检测并追踪一个移动目标的过程。
语言选择: C++ 或 Python 。
1. 节点功能详细说明
a) camera_node
(图像发布者)
- 功能: 模拟一个摄像头,持续生成图像数据流。
- 实现细节:
- 无需真实摄像头。 请使用OpenCV创建一个 640x480 分辨率的黑色背景图像。
- 在该图像中,绘制一个半径为20像素的蓝色实心圆。
- 让这个蓝色圆在图像范围内随机缓慢移动。
- 发布接口:
- 话题 (Topic):
/image_raw
- 消息类型 (Type):
sensor_msgs/msg/Image
- 发布频率: 10 Hz
- 话题 (Topic):
b) detector_node
(目标检测器)
- 功能: 订阅原始图像流,检测其中的目标,并发布目标位置。
- 实现细节:
- 使用OpenCV进行图像处理。
- 将接收到的BGR图像转换为HSV色彩空间。
- 通过颜色阈值分割,找出图像中的蓝色区域。
- 计算蓝色区域轮廓的几何中心点 (Centroid)。
- 订阅接口:
- 话题 (Topic):
/image_raw
- 消息类型 (Type):
sensor_msgs/msg/Image
- 话题 (Topic):
- 发布接口:
- 话题 (Topic):
/target_position
- 消息类型 (Type):
geometry_msgs/msg/PointStamped
(请填充point.x
和point.y
字段,以及header
中的时间戳)
- 话题 (Topic):
c) logger_node
(日志记录器)
- 功能: 订阅目标位置信息,并将其打印到控制台,用于调试和验证。
- 实现细节:
- 每接收到一条消息,就在终端(标准输出)打印出目标的x和y坐标。
- 打印格式示例:
Target detected at: [x=320.5, y=240.1]
- 订阅接口:
- 话题 (Topic):
/target_position
- 消息类型 (Type):
geometry_msgs/msg/PointStamped
- 话题 (Topic):
2. 系统启动与测试要求
Launch文件:
- 请在你的
rm_vision_sim
功能包中创建一个名为tracking_sim.launch.py
的launch文件。 - 这个launch文件必须能够一次性启动上述全部三个节点。
- 请在你的
统一测试 (
test.sh
):- 你的
test.sh
脚本或Makefile
中的test
目标必须能够自动化地完成以下流程:- 使用
colcon build
编译整个ROS2工作区。 source
正确的setup.bash
文件。- 使用
ros2 launch
命令启动你的tracking_sim.launch.py
文件。
- 使用
- 脚本运行时,我们应该能看到
logger_node
在终端持续打印坐标信息。
- 你的
验证说明 (
README.md
):- 在
README.md
中,除了test.sh
的说明,还需提供一个手动验证的方法。 - 这个方法应告知学长学姐,在系统运行后,可以通过在新终端执行
ros2 topic echo /target_position
命令来查看detector_node
发布的数据流,以此验证其功能是否正常。
- 在
算法鲁棒性分析 (理论题)
- 问题: 在上述
detector_node
中,如果模拟的视频流引入了剧烈的光照变化(时亮时暗),简单的颜色阈值分割会很不稳定。请阐述至少两种可以提升检测鲁棒性的图像预处理或算法改进方法,并简述其原理。
第六部分:机器人感知算法基础
背景: 深度学习是现代机器人视觉感知系统的核心技术。本部分要求你为机器人训练一个目标识别模型。
数据集说明:CIFAR-10
任务:实现并训练一个CNN模型,用于CIFAR-10图像分类
技术栈二选一 (Python/PyTorch 或 C++/LibTorch):
- Python (PyTorch): 使用
torchvision
自动下载并加载数据,完成模型训练。 - C++ (LibTorch): 手动下载数据集的二进制版本,并自行编写C++ Dataset类来解析和加载数据,完成模型训练。
测试要求: 你的 README.md
必须提供开始训练的确切命令。你的 test.sh
脚本可以就是这个训练命令,或者是一个调用训练脚本的命令。
提示
学长建议还是用Pytorch好一些哦!不要把跨语言看得那么难。
如果你还是想用libtorch,请看下面的建议
C++ (LibTorch)
- 数据加载:
- 你需要从官方网站手动下载数据集的 "CIFAR-10 binary version"。
- 二进制格式说明: 该数据集由多个文件组成,每个文件包含一系列3073字节的记录。每个记录的第1个字节是标签(0-9),后续的3072个字节依次是1024个红色像素、1024个绿色像素和1024个蓝色像素(32x32图像)。
- 核心任务: 你的首要任务是编写一个能正确解析这些二进制文件的C++ Dataset类(继承自
torch::data::Dataset
)。 - 实现建议:
- 先编写一个独立的函数,尝试从文件中正确读取并解析一条记录,将其转换为一个
torch::Tensor
(图像) 和一个torch::Tensor
(标签)。 - 验证图像Tensor的维度应为
(3, 32, 32)
。 - 成功后,再将此逻辑封装到你的自定义Dataset类中。
- 先编写一个独立的函数,尝试从文件中正确读取并解析一条记录,将其转换为一个
- 实现要求:
- 使用C++和LibTorch完成图像分类任务。你需要自行配置CMake以链接LibTorch,并用C++实现完整的训练与评估逻辑。
- 将模型权重保存为
.pt
文件。
提交说明:
请在力所能及地完成任务,完成后,将你的Git仓库访问权限分享给学长学姐。我们期待看到你严谨的代码、清晰的文档以及解决问题时展现出的工程思维。
祝你一切顺利,期待在队伍里见到你!