概述
2025年电赛C题主要考察位姿检测,目标检测和重叠图像分割的图像处理知识,以及功耗的测量和优化策略。
C题主要考察视觉手的能力,尤其发挥中的重叠部分偏向算法,需要考虑各种重叠情况。常见的opencv,K210,K230 micropython的API较少,开发难度较大。所以绝大部分队伍采用的是树莓派,香橙派等甚至jeston nano,N100等NUC,主要是这些设备已经有完善的适配的操作系统,以及强大的硬件资源,可以自由使用opencv等开源库。
猜测由于K230发布时间相对较晚,大多受众还在采用micropython固件,而之前的K210也并未有适配Linux的能力,K510知名度太小能力也不如K230,导致很少有队伍采用K230作为C题主力模块。目前K230的四种SDK中,RT+Linux资料较多可在C题做尝试,见参考。不嫌弃写API麻烦的,也可以用micropython。

相关链接
勘智开发者社区-Linux&RT Smart 开发文档
勘智开发者社区-Linux 开发文档
见解
由于使用K230 linux文件系统仅能在linux操作系统读取,本人在wsl浅试过也无法读取SD卡里文件,所以等我返校后拿到ubuntu副机再在K230上尝试Linux+RT固件吧。(如果您有其他读取方案可以告知我,VM除外)。以下的代码我在windows代运行,使用opencv为主力库。
基础部分视觉
基础部分的视觉部分难点仅在于测距,测距方案采用PNP即可,边长的精确估计只需要计算好特定几何图形和外围A4纸的面积比例和尽量提高相机分辨率即可。本人使用1080p相机在距纸张150cm使用PNP算法测试,获得结果误差基本在±2mm。
PNP的原理证明和应用各位可以自行了解,在opencv samples也有pnp例程。在K230 micropython cv_lite中,官方亦提供了rgb888_pnp_distance_from_corners和rgb888_pnp_distance作为简单调用,见参考。
听说有队伍采用大模型测距?????
相关链接:
知乎-上海交通大学AuTop战队开源算法讲解
发挥部分视觉

发挥部分难点集中在重叠正方形目标物。因为设备缘故,仅在PC上浅试,未作内轮廓、多轮廓、奇怪轮廓的检测识别。一个正方形轮廓如果想被识别,必须至少有三条边暴露在整体重叠部分的轮廓中,否则不可能被识别到。代码如下:
"""
@file: test.py
@brief: 电赛C题重叠正方形部分题目
@version: 1.0
@date: 2025-08-19
@author: An
"""
import cv2
import matplotlib.pyplot as plt
import glob
import os
import numpy as np
from itertools import combinations
folder_path = "img"
save_folder_path="save_img"
read_files = glob.glob(os.path.join(folder_path, "*.png"))
for img_path in read_files:
img = cv2.imread(img_path)
if img is None:
print("图片读取失败,请检查路径!")
else:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
epsilon = 0.005 * cv2.arcLength(contours[1], True)
approx = cv2.approxPolyDP(contours[1], epsilon, True)
corner_list = [tuple(pt[0]) for pt in approx]
for index,corner in enumerate(corner_list):
cv2.putText(img, str(index), corner, cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.8, color=(0,255,0), thickness=2)
convex_points=[]
concave_points=[]
point_status=[]
first_index=0
for i in range(len(corner_list)):
p0 = corner_list[i-1]
p1 = corner_list[i]
p2 = corner_list[(i+1) % len(corner_list)]
z = (p1[0]-p0[0])*(p2[1]-p0[1]) - (p1[1]-p0[1])*(p2[0]-p0[0])
if z < 0:
if first_index==0:
first_index=i
convex_points.append(tuple(p1))
point_status.append(0)
cv2.circle(img, tuple(p1), radius=3, color=(0,255,0), thickness=-1)
elif z > 0:
concave_points.append(tuple(p1))
point_status.append(1)
cv2.circle(img, tuple(p1), radius=3, color=(0,0,255), thickness=-1)
print(point_status)
print("角点偏移纠正,使-1状态点前置")
correct_corner_list=corner_list[first_index:] + corner_list[:first_index]
correct_point_status= point_status[first_index:] + point_status[:first_index]
print(correct_point_status)
flag_3=0
dot3_rec=[]
for index,dot_status in enumerate(correct_point_status):
if dot_status==1:
flag_3+=1
elif dot_status==0:
flag_3=0
if flag_3>=3:
dot3_rec.append(index-2)
correct_point_status[index-3]-=1
for dot_status in dot3_rec:
correct_point_status[(dot_status+3)%len(correct_point_status)]=-1
correct_point_status[dot_status]+=1
correct_point_status[(dot_status+1)%len(correct_point_status)]+=1
correct_point_status[(dot_status+2)%len(correct_point_status)]+=1
add_dot_x=correct_corner_list[dot_status][0]+correct_corner_list[dot_status+2][0]-correct_corner_list[dot_status+1][0]
add_dot_y=correct_corner_list[dot_status][1]+correct_corner_list[dot_status+2][1]-correct_corner_list[dot_status+1][1]
cv2.line(img, (correct_corner_list[dot_status][0],correct_corner_list[dot_status][1]),
(correct_corner_list[dot_status+1][0],correct_corner_list[dot_status+1][1]), color=(255,0,0), thickness=2, lineType=cv2.LINE_8)
cv2.line(img, (correct_corner_list[dot_status+1][0],correct_corner_list[dot_status+1][1]),
(correct_corner_list[dot_status+2][0],correct_corner_list[dot_status+2][1]), color=(255,0,0), thickness=2, lineType=cv2.LINE_8)
cv2.line(img, (add_dot_x,add_dot_y),
(correct_corner_list[dot_status][0],correct_corner_list[dot_status][1]), color=(255,0,0), thickness=2, lineType=cv2.LINE_8)
cv2.line(img, (add_dot_x,add_dot_y),
(correct_corner_list[dot_status+2][0],correct_corner_list[dot_status+2][1]), color=(255,0,0), thickness=2, lineType=cv2.LINE_8)
cv2.circle(img, (add_dot_x,add_dot_y), radius=5, color=(0,255,0), thickness=-1)
print("筛选出连续3角点正方形")
print(correct_point_status)
print(dot3_rec)
flag_2=0
dot2_rec=[]
for index,dot_status in enumerate(correct_point_status):
if dot_status==1:
flag_2+=1
if dot_status==0 or dot_status==-1:
flag_2=0
if flag_2==2 and ((index-1) not in dot3_rec):
print(index)
dot2_rec.append(index-1)
correct_point_status[index-2]-=1
for dot_status in dot2_rec:
correct_point_status[(dot_status+2)%len(correct_point_status)]-=1
correct_point_status[dot_status]+=1
correct_point_status[(dot_status+1)%len(correct_point_status)]+=1
A = np.array(correct_corner_list[dot_status])
B = np.array(correct_corner_list[dot_status + 1])
AB = B - A
length = np.linalg.norm(AB)
BC_np = np.array(correct_corner_list[(dot_status + 2) % len(correct_corner_list)]) - B
AD_np = np.array(correct_corner_list[dot_status -1]) - A
BC_len = np.linalg.norm(BC_np)
AD_len = np.linalg.norm(AD_np)
if BC_len==0 or AD_len == 0:
raise ValueError("方向向量长度为0")
if BC_len>=AD_len:
angle_to=BC_np / BC_len
else:
angle_to=AD_np / AD_len
C = B + angle_to * length
D = A + angle_to * length
C_zuobiao = C.tolist()
D_zuobiao = D.tolist()
A = tuple(map(int, correct_corner_list[dot_status]))
B = tuple(map(int, correct_corner_list[(dot_status+1) % len(correct_corner_list)]))
C = tuple(map(int, C_zuobiao))
D = tuple(map(int, D_zuobiao))
cv2.line(img, C, B, color=(255,0,0), thickness=2)
cv2.line(img, D, A, color=(255,0,0), thickness=2)
cv2.line(img, C, D, color=(255,0,0), thickness=2)
cv2.line(img, A, B, color=(255,0,0), thickness=2)
print("筛选出连续2角点正方形")
print(correct_point_status)
print(dot2_rec)
line=[]
flag_1=0
dot1_rec=[]
for index,dot_status in enumerate(correct_point_status):
if dot_status==-1 and flag_1==0:
flag_1=1
elif dot_status==1 and flag_1==1:
flag_1=2
elif dot_status==-1 and flag_1==2:
flag_1=3
else:
flag_1=0
if flag_1==3:
dot1_rec.append(index-2)
correct_point_status[index-2]-=1
for dot_status in dot1_rec:
correct_point_status[(dot_status+1)%len(correct_point_status)]+=1
correct_point_status[(dot_status+2)%len(correct_point_status)]-=1
line1_np = np.array(correct_corner_list[(dot_status + 2) % len(correct_corner_list)])-np.array(correct_corner_list[(dot_status + 1) % len(correct_corner_list)])
line2_np = np.array(correct_corner_list[(dot_status) % len(correct_corner_list)])-np.array(correct_corner_list[(dot_status + 1) % len(correct_corner_list)])
line.append([line1_np,correct_corner_list[(dot_status + 1) % len(correct_corner_list)]])
line.append([line2_np,correct_corner_list[(dot_status + 1) % len(correct_corner_list)]])
print("筛选出连续1角点正方形")
print(correct_point_status)
print(dot1_rec)
two_dot_flag=0
dot_line=[]
for index,dot_status in enumerate(correct_point_status):
if dot_status==-1:
two_dot_flag+=1
else:
two_dot_flag=0
if two_dot_flag>=2:
dot_line.append(index-1)
correct_point_status[index-1]-=1
for dot_status in dot_line:
correct_point_status[(dot_status+1)%len(correct_point_status)]-=1
line_np = np.array(correct_corner_list[(dot_status + 1) % len(correct_corner_list)])-np.array(correct_corner_list[(dot_status) % len(correct_corner_list)])
line.append([line_np,correct_corner_list[(dot_status) % len(correct_corner_list)]])
print("筛选出连续2凹点边")
print(correct_point_status)
print(dot_line)
print("打印不确定边line")
print(line)
if len(line)>=3:
print("存在被掩盖正方形")
tol = 100
all_triplets = []
for comb in combinations(line, 3):
v1, p1 = comb[0]
v2, p2 = comb[1]
v3, p3 = comb[2]
cond12 = cond23 = cond13 = 0
if abs(v1[0]*v2[1] - v1[1]*v2[0]) < tol:
cond12=1
elif abs(np.dot(v1, v2)) < tol:
cond12=2
if abs(v2[0]*v3[1] - v2[1]*v3[0]) < tol:
cond23=1
elif abs(np.dot(v2, v3)) < tol:
cond23=2
if abs(v1[0]*v3[1] - v1[1]*v3[0]) < tol:
cond13=1
elif abs(np.dot(v1, v3)) < tol:
cond13=2
if cond12 and cond23 and cond13:
if cond12==1:
all_triplets.append([comb[0],comb[2],comb[1]])
elif cond23==1:
all_triplets.append([comb[1],comb[0],comb[2]])
elif cond13==1:
all_triplets.append([comb[0],comb[1],comb[2]])
results = []
used_points = set()
for trip in all_triplets:
points = {trip[0][1], trip[1][1], trip[2][1]}
if not (points & used_points):
results.append(trip)
used_points |= points
print("组合点")
print(results)
get_dot=[]
for trip in results:
(v1, p1), (v2, p2), (v3, p3) = trip
den = v1[0]*v2[1] - v1[1]*v2[0]
t = ((p2[0] - p1[0])*v2[1] - (p2[1] - p1[1])*v2[0]) / den
dot1=(p1[0] + t*v1[0], p1[1] + t*v1[1])
cv2.circle(img, (int(dot1[0]), int(dot1[1])), radius=3, color=(0,255,255), thickness=-1)
den = v2[0]*v3[1] - v2[1]*v3[0]
t = ((p3[0] - p2[0])*v3[1] - (p3[1] - p2[1])*v3[0]) / den
dot2=(p2[0] + t*v2[0], p2[1] + t*v2[1])
cv2.circle(img, (int(dot2[0]), int(dot2[1])), radius=3, color=(0,255,255), thickness=-1)
cv2.line(img, (int(dot1[0]), int(dot1[1])),(int(dot2[0]), int(dot2[1])), color=(255,0,0), thickness=2, lineType=cv2.LINE_8)
get_dot.append([dot1,dot2])
print(get_dot)
else:
print("不存在被掩盖正方形")
print("\033[32m本图完成,在保存\033[0m")
imshow_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
save_path = os.path.join(save_folder_path, "processed_" + os.path.basename(img_path))
cv2.imwrite(save_path, imshow_img)
几张示例图前后识别对比效果如下(未作内轮廓、多轮廓、奇怪轮廓的检测识别,条件不足的轮廓不予识别)
1.四个重叠正方形,中心重叠严重的正方形仅画一条边代替


2.两个重叠正方形


3.四个重叠正方形,中心重叠严重的正方形仅画一条边代替


- 五个重叠正方形


- 五个重叠正方形,中心重叠严重的正方形仅画一条边代替


6.五个重叠正方形,中心重叠严重的正方形仅有两条边暴露在整体轮廓,不可能被判别出具体边长


7.三个重叠正方形,没写角点校验且外围短边多所以误差大


8.有个特殊轮廓的判断没写,自行尝试一下吧,下图
总结
等返校拿到linux电脑能读取固件后再尝试部署K230 linux+RT固件吧
以上代码仅为尝试代码,仅提供一些个人思路,纰漏很多。本人不擅长算法也没在电赛选C题,如有更好的想法请自行修改与尝试。