首先在开头感谢东南大学2018年开源代码以及深圳大学、上海交大2019年开源代码对本套代码的完成提供的巨大帮助,希望这套代码也能够帮助其他队伍在 RM 这个舞台上得到更大的提升。
本代码是吉林大学TARS-GO战队Robomaster2020赛季步兵视觉算法,主要模块分为装甲板识别、大风车能量机关识别、角度解算、相机驱动及串口/CAN通信。
模块 | 功能 |
---|---|
装甲板识别 | 检测敌方机器人装甲板位置信息并识别其数字 |
大风车能量机关识别 | 检测待激活大风车扇叶目标位置信息 |
角度解算 | 根据上述位置信息解算目标相对枪管的yaw、pitch角度及距离 |
相机驱动 | 大恒相机SDK封装,实现相机参数控制及图像采集 |
串口/CAN通信 | 与下位机通信,传输机器人姿态信息及操作手反馈视觉的控制信息 |
装甲板识别采用基于OpenCV的传统算法实现装甲板位置检测,同时采用SVM实现装甲板数字识别。
考虑战场实际情况,机器人可打击有效范围在1m~7m之间,在此范围内,本套算法装甲板识别率达98%,识别得到装甲板在图像中四个顶点、中心点的坐标信息。
在640*480图像分辨率下,装甲板识别帧率可达340fps左右,引入ROI之后可达420fps。但考虑到识别帧率对于电控机械延迟的饱和,取消引入ROI操作,以此避免引入ROI之后无法及时探测全局视野情况的问题,加快机器人自瞄响应。
装甲板数字识别采用SVM,通过装甲板位置信息裁剪二值化后的装甲板图像并透射变换,投入训练好的SVM模型中识别,数字识别准确率可达98%。
角度解算方面使用了两种解算方法分距离挡位运行。第一档使用P4P算法,第二档使用小孔成像原理的PinHole算法。
此外还引入了相机-枪口的Y轴距离补偿及重力补偿。
使用标定板测试,角度解算计算的距离误差在10%以内,角度基本与实际吻合。
硬件 | 型号 | 参数 |
---|---|---|
运算平台 | Manifold2-G | Tx2 |
相机 | 大恒相机MER-050 | 分辨率640*480 曝光值3000~5000 |
镜头 | M0814-MP2 | 焦距8mm 光圈值4 |
软件类型 | 型号 |
---|---|
OS | Ubuntu 16.04/Ubuntu18.04 |
IDE | Qt Creator-4.5.2 |
Library | OpenCV-3.4.0 |
DRIVE | Galaxy SDK |
JLURoboVision/
├── AngleSolver
│ └── AngleSolver.h(角度解算模块头文件)
│ ├── AngleSolver.cpp(角度解算模块源文件)
├── Armor
│ ├── Armor.h(装甲板识别模块头文件)
│ ├── LightBar.cpp(灯条类源文件)
│ ├── ArmorBox.cpp(装甲板类源文件)
│ ├── ArmorNumClassifier.cpp(装甲板数字识别类源文件)
│ ├── findLights.cpp(灯条监测相关函数源文件)
│ └── matchArmors.cpp(装甲板匹配相关函数源文件)
│ ├── ArmorDetector.cpp(装甲板识别子类源文件)
├── General
│ ├── 123svm.xml(SVM模型文件)
│ ├── camera_params.xml(相机参数文件)
│ └── General.h(公有内容声明头文件)
├── GxCamera
│ ├── GxCamera.h(大恒相机类头文件)
│ ├── GxCamera.cpp(大恒相机类封装源文件)
│ └── include(相机SDK包含文件)
│ ├── DxImageProc.h
│ └── GxIAPI.h
└── Serial
│ ├── Serial.h(串口头文件)
│ └──Serial.cpp(串口源文件)
├── Main
│ ├── ArmorDetecting.cpp(装甲板识别线程)
│ ├── ImageUpdating.cpp(图像更新线程)
│ └── main.cpp(main函数,程序主入口源文件)
装甲板识别使用基于检测目标特征的OpenCV传统方法,实现检测识别的中心思想是找出图像中所有敌方颜色灯条,并使用找出的灯条一一拟合并筛选装甲板。
主要步骤分为:图像预处理、灯条检测、装甲板匹配、装甲板数字识别及最终的目标装甲板选择。
/**
*@brief: detect and delete error armor which is caused by the single lightBar 针对游离灯条导致的错误装甲板进行检测和删除
*/
void eraseErrorRepeatArmor(vector<ArmorBox> & armors)
{
int length = armors.size();
vector<ArmorBox>::iterator it = armors.begin();
for (size_t i = 0; i < length; i++)
for (size_t j = i + 1; j < length; j++)
{
if (armors[i].l_index == armors[j].l_index ||
armors[i].l_index == armors[j].r_index ||
armors[i].r_index == armors[j].l_index ||
armors[i].r_index == armors[j].r_index)
{
armors[i].getDeviationAngle() > armors[j].getDeviationAngle() ? armors.erase(it + i) : armors.erase(it + j);
}
}
}
首先对图像进行二值化操作,然后进行一定腐蚀和膨胀,通过边缘提取和条件限制得出待击打叶片(锤子形)。
在待击打叶片范围内进一步用类似方法寻找目标装甲板和流动条,在二者连线上寻找中心的“R”。
根据目标装甲板坐标和中心坐标计算极坐标系下的目标角度,进而预测待击打点的坐标(小符为装甲板本身,大符需要旋转)。
最后将待击打点坐标和图像中心的差值转换为yaw和pitch轴角度,增加一环PID后发送给云台主控板。
上下板之间的通信逻辑,主要由我们自定的通信协议体现:
协议共有16个字节,包括帧头占用的1字节,校验位需要的1字节,数据位的12个字节,以及两个字节的标志位。可以满足上位机与主控板之间的通信需求,且尽量精简了数据包体量以提高传输速度。
Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
---|---|---|---|---|---|---|---|
0xAA | CRC_8 | Yaw_data | Yaw_data | Yaw_data | Yaw_data | Pitch_data | Pitch_data |
Byte8 | Byte9 | Byte10 | Byte11 | Byte12 | Byte13 | Byte14 | Byte15 |
Pitch_data | Pitch_data | Dist_data | Dist_data | Dist_data | Dist_data | Targt)Num | Fire_cmd |
- 0xAA -帧头
- Yaw_data : 8 bit char - 接收视觉解算出来的云台 yaw 值
- Pitch_data : 8 bit char - 接收视觉解算出来的云台 pitch 值
- (改为传360坐标系,x10保留一位小数,距离直接传数值,同样x10精度mm,帧头0XAA)
- Dist_data : 8 bit char - 接收视觉解算目标到相机的距离值
- Target_num : 8 bit char - 优先目标数字-前三位(数字0-8,没有6)(stm32 -> PC)
- 模式选择 - 后五位(0 不处理,1-8留作模式选择, stm32 -> PC,1为自瞄,2为大风车)
- Fire-cmd是否开火:8 bit char - 在视觉判定枪口(摄像头)对准目标在误差范围内时发送——0 为不开火,1 为开火
可参考下列示例代码:
JLUVision_Demos各示例程序代码库
Armor_Demo为装甲板识别模块演示程序,可在Linux(.pro)/Windows(.sln)运行。
AngleSolver_Armor_GxCamera为大恒相机采图+装甲板+角度解算演示程序,需要连接大恒相机在Linux下运行。
代码还自定义了一套调试用的函数,将灯条、装甲板识别、角度解算等信息进行可视化输出,并可通过键盘控制部分识别参数,为代码的调试和优化带来便利。
//装甲板检测识别调试参数是否输出
//param:
// 1.showSrcImg_ON, 是否展示原图
// 2.bool showSrcBinary_ON, 是否展示二值图
// 3.bool showLights_ON, 是否展示灯条图
// 4.bool showArmors_ON, 是否展示装甲板图
// 5.bool textLights_ON, 是否输出灯条信息
// 6.bool textArmors_ON, 是否输出装甲板信息
// 7.bool textScores_ON 是否输出打击度信息
// 1 2 3 4 5 6 7
detector.showDebugInfo(0, 0, 0, 1, 0, 0, 0);
//角度解算调试参数是否输出
//param:
// 1.showCurrentResult, 是否展示当前解算结果
// 2.bool showTVec, 是否展示目标坐标
// 3.bool showP4P, 是否展示P4P算法计算结果
// 4.bool showPinHole, 是否展示PinHole算法计算结果
// 5.bool showCompensation, 是否输出补偿结果
// 6.bool showCameraParams 是否输出相机参数
// 1 2 3 4 5 6
angleSolver.showDebugInfo(1, 1, 1, 1, 1, 0);
本套代码主要实现了装甲板识别及大风车的识别这两个模块,结合角度解算模块对识别到的目标信息的解算,获取云台枪口控制转角,随后通过串口传输给下位机。
装甲板识别与大风车识别模块性能表现不错,识别率和帧率满足比赛需求;角度解算模块经过设计,提升了准确性及鲁棒性。
同时,代码整体经过封装,具有较强的可移植性。
//pointer visits all the data of srcImg, the same to bgr channel split 通道相减法的自定义形式,利用指针访问,免去了split、substract和thresh操作,加速了1.7倍
//data of Mat bgr bgr bgr bgr
uchar *pdata = (uchar*)srcImg.data;
uchar *qdata = (uchar*)srcImg_binary.data;
int srcData = srcImg.rows * srcImg.cols;
if (enemyColor == RED)
{
for (int i = 0; i < srcData; i++)
{
if (*(pdata + 2) - *pdata > armorParam.color_threshold)
*qdata = 255;
pdata += 3;
qdata++;
}
}
else if (enemyColor == BLUE)
{
for (int i = 0; i < srcData; i++)
{
if (*pdata - *(pdata+2) > armorParam.color_threshold)
*qdata = 255;
pdata += 3;
qdata++;
}
}
/**
*@brief: compare a_armor to b_armor according to their distance to lastArmor(if exit, not a default armor) and their area and armorNum
* 比较a_armor装甲板与b_armor装甲板的打击度,判断a_armor是否比b_armor更适合打击(通过装甲板数字是否与目标装甲板数字匹配,装甲板与lastArmor的距离以及装甲板的面积大小判断)
*/
bool armorCompare(const ArmorBox & a_armor, const ArmorBox & b_armor, const ArmorBox & lastArmor, const int & targetNum)
{
float a_score = 0; // shooting value of a_armor a_armor的打击度
float b_score = 0; //shooting value of b_armor b_armor的打击度
a_score += a_armor.armorRect.area(); //area value of a a_armor面积得分
b_score += b_armor.armorRect.area(); //area value of b b_armor面积得分
//number(robot type) priorty 设置a、b装甲板的分数
setNumScore(a_armor.armorNum, targetNum, a_score);
setNumScore(b_armor.armorNum, targetNum, b_score);
if (lastArmor.armorNum != 0) { //if lastArmor.armorRect is not a default armor means there is a true targetArmor in the last frame 上一帧图像中存在目标装甲板
float a_distance = getPointsDistance(a_armor.center, lastArmor.center); //distance score to the lastArmor(if exist) 装甲板距离得分,算负分
float b_distance = getPointsDistance(b_armor.center, lastArmor.center); //distance score to the lastArmor(if exist) 装甲板距离得分,算负分
a_score -= a_distance * 2;
b_score -= b_distance * 2;
}
return a_score > b_score; //judge whether a is more valuable according their score 根据打击度判断a是否比b更适合打击
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. Open source ecosystem
2. Collaboration, People, Software
3. Evaluation model