智能视觉很强,但传统视觉还是香
我们最近完成了一个项目,在一台算力有限的移动小车上,搭载机械臂完成对魔方的精准抓取。系统使用的是手眼一体结构,相机固定在机械臂末端,通过图像识别引导夹爪执行操作。

得益于这种俯拍结构,魔方在图中呈标准正方形,边界清晰,便于后续处理。系统通过SpireCV实现目标检测,可稳定输出魔方的中心位置和标准边界框。但在实测中发现:若夹爪正对魔方对角抓取,接触面过小,魔方易滑动甚至损坏。为此我们希望通过视觉识别引导夹爪旋转对齐魔方棱边,提升抓取稳定性。问题在于,SpireCV返回的是标准边界框,不含目标相对于夹爪的偏移角度。而这个角度,正是能否抓稳的关键。
本文将分享我们在项目中总结的6种视觉方案,完整呈现从思考到取舍的全过程,供大家实战参考。随着传统视觉算法与智能视觉算法加速融合,如何依据场景灵活选型、合理组合,已成为视觉系统落地的关键。
01 实例分割算法
熟悉神经网络的读者可能会想到,既然目标检测获得不了目标的方向,那么可以尝试在目标检测的同时,进行实例分割获取目标的轮廓,通过轮廓拟合获取最小外包矩形,从而推断目标方向。这一思路在理论上可行,但在本项目中,由于机械臂平台算力有限,实例分割算法的识别帧率会骤降至2~3帧/秒,导致整个识别功能几乎不可用。这也是我们最早尝试后放弃的方案。
02 二值化与最小外包矩形结合
考虑到算力受限,我们转向尝试传统视觉方法。直观的思路是:既然生成mask的代价较高,是否可以通过 OpenCV的轮廓提取(cv::findContours)和最小外接矩形拟合(cv::minAreaRect),获取一个倾斜的外包矩形?只要能拟合出该矩形,就能计算出角度,从而推断目标朝向。
主要流程如下:
我们先提取目标的ROI区域,仅在该区域内计算角度,以降低整体计算开销。随后通过轮廓提取获取所有外轮廓,并利用 cv::contourArea选出面积最大者,再用cv::minAreaRect拟合其最小外接矩形。通过对该矩形的几何信息分析,即可直接得到目标的朝向角度。
核心代码:
// 1. 灰度、自适应二值化
cv::cvtColor(roi, gray, cv::COLOR_BGR2GRAY);
cv::adaptiveThreshold(gray, bin_roi, 255,
cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, 15, 2);
……
// 2. 轮廓提取
cv::findContours(bin_roi, contours, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);
……
// 3. 取最大轮廓并拟合最小外接矩形
……
cv::RotatedRect minRect =
cv::minAreaRect(contours[max_idx]);
……
// 4. 计算夹角
float angle_wrt_x = minRect.angle;
float angle_wrt_y = fabs(90 - fabs(angle_wrt_x));
测试结果:
该算法仅在魔方整个面有较大纯色块时,有一定的效果,如果魔方是一个打乱的状态,内部色块导致轮廓分散,效果较差,以下是对比效果。
03 线检测计算角度
方案二给了我们启发:传统视觉处理方法的新计算量主要在后处理,而不是深度学习推理,因此速度影响较小。即使方案二效果一般,我们仍可尝试传统视觉后处理与深度学习识别结合,以平衡精度与速度。魔方的一大特性是内部格子缝线呈井字形,且在俯拍状态下,主要线条互为垂直,而魔方外的区域线条不存在该特点。我们根据魔方的特性,提出以下线检测方法。
在整个流程中,我们利用了魔方的几何特征,提升了整体的鲁棒性。
1.选取距离目标检测中心最近的10根线,可以基本过滤掉外侧背景干扰。
2.从上述线中筛选互相垂直的线对,这是根据魔方结构的进一步筛选。通过该筛选,已经可以筛掉绝大部分的杂线。
3.计算这些线组的角度,通常为几组互余且差别不大的值,选取最小值作为最终输出。夹爪只需与棱边平行,两个互余角度均可作为夹爪旋转角。
核心代码:
// 1. 灰度、自适应二值化
cv::cvtColor(roi,
gray, cv::COLOR_BGR2GRAY);
cv::adaptiveThreshold(gray,
bin_roi, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, 15, 2);
……
// 2. 轮廓提取
cv::findContours(bin_roi,
contours, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);
……
// 3. 取最大轮廓并拟合最小外接矩形
……
cv::RotatedRect
minRect = cv::minAreaRect(contours[max_idx]);
……
// 4. 计算夹角
float
angle_wrt_x = minRect.angle;
float
angle_wrt_y = fabs(90 - fabs(angle_wrt_x));
测试结果:
通过传统视觉方法的组合并结合任务特性,我们在性能有限的平台上实现了高效、准确的角度识别与发布。
04 线检测与直方图分析
在项目最终采用的方案基础上,我们还探讨了其他角度统计方法。例如,线检测获取所有线段后,将其角度统计为直方图。由于魔方主方向在线段角度分布中表现为两个主峰(互差90°),可以直接选取主峰角度作为魔方朝向。只需输出一个锐角作为夹爪转动角度即可。该法在大多数情况下有效,但误识别线干扰时角度输出可能不准确。通过调整边缘和霍夫线检测参数可进一步提升鲁棒性。
核心代码:
//1.灰度转化
cv::cvtColor(roi, gray, cv::COLOR_BGR2GRAY);
……
//2.Canny边缘检测
cv::Canny(gray, edges, 80, 200, 3);
……
//3.Hough线检测
cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 80,
50, 10);
//4.每条线计算和X轴夹角,统计到直方图
for (const auto& line : lines) {
double dx = line[2] - line[0];
double dy = line[3] - line[1];
double angle = atan2(dy, dx) * 180.0 /
CV_PI;
……
}
//5.计算出现次数最多的两个角度(峰值)
int max1 = 0, max2 = 0, idx1 = 0, idx2 = 0;
for (int i = 0; i < BINS; ++i) {
if (hist[i] > max1) {
max2 = max1; idx2 =
idx1;
max1 = hist[i]; idx1 =
i;
} else if (hist[i] > max2) {
max2 = hist[i]; idx2 =
i;
}
}
//6.角度差计算
double angle1 = idx1, angle2 = idx2;
double diff = fabs(angle1 - angle2);
if (diff > 90) diff = 180 - diff;
05 模板定位
如果条件允许,比如抓取的始终是一个魔方,或者相同的魔方,摆设距离和光照变化不大。那么可以使用一个并不太“智能”的方法:模板定位。首先采集魔方正向的图像作为匹配模板。再对实时图像进行SIFT或ORB特征提取,利用特征匹配算法获取两种图中的对应点,然后计算出单应性矩阵,进而提取出旋转角度。这个方法可以完全摆脱神经网络的使用,但是相应的,场景局限性变得很大,适用范围有限。
06 定向边界框检测算法(OBB)
目前主流目标检测多采用水平边界框(HBB),而随着技术发展和任务需求,定向边界框(OBB)检测算法越来越受关注。OBB检测可以使用定向边界框更加紧密的贴合目标物体的实际形状与朝向,从而直接获取目标的朝向,下图展示了SpireCV-Pro中OBB算法检测结果的。受限于项目开发周期和任务需要,我们没能在该任务中使用该方法,读者可以自行尝试。
如果您有感兴趣的技术话题,请在留言区告诉我们!
关注阿木实验室,更多技术干货不断更新!
开发遇到棘手难题可以上阿木官方论坛:bbs.amovlab.com
有工程师亲自解答
10000+无人机开发者和你共同进步!
)