一、前言
- 在工作中往往出现些奇葩需求。那个***需要跟踪,减速带、红绿灯、井盖,甚至是鸟、烟头、手指等。
- 今天就给大家分享下博主遇到那些奇葩需求,遇到了这些奇葩需求首先问候产品经理(斜眼笑.jpg)。
- 玩笑归玩笑,冷静下来还是要好好分析实际需求,想想如何解决这些实际问题。作为个优秀的工程师,解决问题才是关键。
二、奇葩需求
在低级别无人驾驶中,一般就是辅助倒车,辅助驾驶,辅助避让。或者说多数情况无人驾驶都是在特定的场景,如常说的高速场景。
而在高级别无人驾驶中如L4级别,要考虑的需求会大很多。当然,在 2024 年的今天,想在乘用车上实现无人驾驶还是有段距离,但在市场与政策双重刺激下,相信在不久的将来会实现。
今天给大家分享下一些在高级别无人驾驶过程中遇到的一些奇葩需求,遇到这些需求有哪些处理方法。
2.1、减速带
需求:当车体前方有减带时,我们要告知车子减速通行。
减速带在城市交通道路上是非常常见的。
![图片名称](https://i-blog.csdnimg.cn/blog_migrate/6e486d2193dd385aff32c1f9d575be22.png)
这个跟正常识别人、车等障碍物不太一样。类似减速带这类障碍物是可通行的,只是需要做减速通行。那么我们处理的逻辑就要改变。这里不仅仅是简单的检测。减速带的检测模型可以使用 segformer 模型或 TwinsNet 模型,也可以使用 yolo 系列做检测。
因为检测不可避免的存在错误检测。如果没有减速带检测出减速带,此时车子会出现明显卡顿,加速减速、加速减速,想想也知道场面多么滑稽。这里可以结合历史信息做一个平滑处理,主要是为了处理没有减速带而错误检测出减速带的情况。
int num = (objectMessage->contours.size() != 0) ? 1: 0;
resVec.push_back(num);
sum += num;
int length = resVec.size();
if (length > maxLength) {
sum -= resVec[0];
resVec.erase(resVec.begin());
length -= 1;
}
ans = (sum >= length * weight)? 1:0; // 判断是否输出
2.2、红绿灯
![图片名称](https://i-blog.csdnimg.cn/blog_migrate/38319f5e6912721e1bb8f922fcb1e6a1.jpeg)
城市道路红绿灯特别场景,许多做智能驾驶厂家都吹嘘自己可以做的很好,实测上很难评([捂脸])。
红绿灯场景处理比较复杂主要几个方面
- 红绿灯目标小,难以稳定检测(不像人、车);且数据集少,标注成本大。
- 场景大多数存在多个红绿灯,难以把控这个红绿灯干嘛的,那个红绿灯干嘛的。
- 红绿灯灯的种类多,有人字的、有自信车形状的、有圆形的;红绿灯种类多,有放马路中央的,有几米高的,也有放地上的。
- 红绿灯识别完需要对红绿灯分类,黄灯、红灯、绿灯、没有灯等等。
- 红绿灯交通规则较为复杂,对路径规划提出较大要求。
综上所述,可以得知红绿灯场景处理起来的确费时费力,且效果不一定好。对于标注、规划等问题,这里不详细讲述。这里主要讲解跟踪逻辑。
检测红绿灯这个直接用我们 yolo 系列就足够了。颜色识别我们用颜色分类做。还需要结合历史信息综合判断,如在 1s 的连续帧不会红——>绿——>红来回跳动。可以根据国家红绿灯标准制定相应的策略,可以容忍unkonw。
目标跟踪选用 Bytetrack 或 BotSort。个人更加推荐 BotSort。原因是:(1)红绿灯目标小对匹配要求高(2)车子轻微抖动会影响跟踪
这里放一段开源 BotSort python 代码。如果想了解论文详细思想可以参考博主之前博客 https://blog.csdn.net/qq_49560248/article/details/136026766。
import cv2
import numpy as np
import copy
def applyFeaures(self, raw_frame, detections=None):
# Initialize
height, width, _ = raw_frame.shape
frame = cv2.cvtColor(raw_frame, cv2.COLOR_BGR2GRAY)
H = np.eye(2, 3)
# Downscale image (TODO: consider using pyramids)
if self.downscale > 1.0:
# frame = cv2.GaussianBlur(frame, (3, 3), 1.5)
frame = cv2.resize(frame, (width // self.downscale, height // self.downscale))
width = width // self.downscale
height = height // self.downscale
# find the keypoints
mask = np.zeros_like(frame)
# mask[int(0.05 * height): int(0.95 * height), int(0.05 * width): int(0.95 * width)] = 255
mask[int(0.02 * height): int(0.98 * height), int(0.02 * width): int(0.98 * width)] = 255
if detections is not None:
for det in detections:
tlbr = (det[:4] / self.downscale).astype(np.int_)
mask[tlbr[1]:tlbr[3], tlbr[0]:tlbr[2]] = 0
keypoints = self.detector.detect(frame, mask)
# compute the descriptors
keypoints, descriptors = self.extractor.compute(frame, keypoints)
# Handle first frame
if not self.initializedFirstFrame:
# Initialize data
self.prevFrame = frame.copy()
self.prevKeyPoints = copy.copy(keypoints)
self.prevDescriptors = copy.copy(descriptors)
# Initialization done
self.initializedFirstFrame = True
return H
# Match descriptors.
knnMatches = self.matcher.knnMatch(self.prevDescriptors, descriptors, 2)
# Filtered matches based on smallest spatial distance
matches = []
spatialDistances = []
maxSpatialDistance = 0.25 * np.array([width, height])
# Handle empty matches case
if len(knnMatches) == 0:
# Store to next iteration
self.prevFrame = frame.copy()
self.prevKeyPoints = copy.copy(keypoints)
self.prevDescriptors = copy.copy(descriptors)
return H
for m, n in knnMatches:
if m.distance < 0.9 * n.distance:
prevKeyPointLocation = self.prevKeyPoints[m.queryIdx].pt
currKeyPointLocation = keypoints[m.trainIdx].pt
spatialDistance = (prevKeyPointLocation[0] - currKeyPointLocation[0],
prevKeyPointLocation[1] - currKeyPointLocation[1])
if (np.abs(spatialDistance[0]) < maxSpatialDistance[0]) and \
(np.abs(spatialDistance[1]) < maxSpatialDistance[1]):
spatialDistances.append(spatialDistance)
matches.append(m)
meanSpatialDistances = np.mean(spatialDistances, 0)
stdSpatialDistances = np.std(spatialDistances, 0)
inliesrs = (spatialDistances - meanSpatialDistances) < 2.5 * stdSpatialDistances
goodMatches = []
prevPoints = []
currPoints = []
for i in range(len(matches)):
if inliesrs[i, 0] and inliesrs[i, 1]:
goodMatches.append(matches[i])
prevPoints.append(self.prevKeyPoints[matches[i].queryIdx].pt)
currPoints.append(keypoints[matches[i].trainIdx].pt)
prevPoints = np.array(prevPoints)
currPoints = np.array(currPoints)
# Find rigid matrix
if (np.size(prevPoints, 0) > 4) and (np.size(prevPoints, 0) == np.size(prevPoints, 0)):
H, inliesrs = cv2.estimateAffinePartial2D(prevPoints, currPoints, cv2.RANSAC)
# Handle downscale
if self.downscale > 1.0:
H[0, 2] *= self.downscale
H[1, 2] *= self.downscale
else:
print('Warning: not enough matching points')
# Store to next iteration
self.prevFrame = frame.copy()
self.prevKeyPoints = copy.copy(keypoints)
self.prevDescriptors = copy.copy(descriptors)
return H
关于红绿灯定位。这个光靠相机很难实现精准定位。一般需要地图信息辅助,视觉方面可以辅助区分相机大致位置如:左红绿灯是人行道 前红绿灯是机动车道。这里放一段博主初略估计的效果图
![图片名称](https://i-blog.csdnimg.cn/blog_migrate/39b8777e8cdb1bb04928100d2f76c7db.png)
三、后记
除了上面这些需求,博主还遇到更加奇葩的,今天就到这,下次分享更奇葩的。欢迎大家交流。