7浏览
查看: 7|回复: 4

[项目] 【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归

[复制链接]
【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图1

什么是 CanMV K230?
CanMV K230是一款高性价比的RISC-V边缘AI平台,凭借低功耗、强视觉处理能力和开放的开发生态,成为嵌入式AI开发的理想选择,尤其适合需要快速部署视觉与AI功能的创客、中小企业及教育场景。CanMV 是一套 AI 视觉开发平台,K230 是其核心芯片。该模块结合了图像采集、AI推理、边缘计算等能力,适合嵌入式视觉应用开发。

CanMV:类似 OpenMV 的图像处理框架,支持 Python 编程,简化视觉识别开发流程。
K230 芯片:嘉楠科技推出的 AIoT SoC,采用 RISC-V 架构,内置第三代 KPU(AI加速单元),算力高达 6 TOPS,性能是 K210 的 13.7 倍。


【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图2

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】CanMV K230 AI视觉识别之快速线性回归

知识点
巡线任务中,快速线性回归是核心技术,能从摄像头采集的道路标线(如黑色赛道上的白色线段)中,实时拟合出直线方程,为设备(机器人、小车)提供行驶方向指引,核心是 “快速计算 + 抗干扰”。核心结论:巡线场景的快速线性回归,优先用最小二乘法(计算简单、速度快),配合 “感兴趣区域(ROI)提取 + 坐标预处理”,可在毫秒级完成拟合,适配 K230 等边缘设备。

1、核心原理(巡线场景适配版)
巡线的本质是拟合 “标线的中心线”,输入是标线上的像素坐标(x,y),输出是直线方程 y = kx + b(或 ax + by + c = 0),最小二乘法通过最小化 “所有像素到直线的距离平方和”,求解最优参数 k(斜率)和 b(截距)。

2、关键简化(提升速度)
坐标转换:将图像坐标系(原点在左上角,y 向下)转换为 “车辆坐标系”(原点在车辆中心,y 向前),减少后续方向计算的复杂度。
ROI 提取:只处理图像下方的感兴趣区域(如底部 1/3 区域,即车辆前方最近的标线),过滤无关背景像素,减少计算量。
数据筛选:只保留标线上的像素(如白色标线用阈值分割提取,得到二值图中的白色像素坐标),避免杂点干扰。

3、快速实现步骤(巡线专属)
图像预处理:采集图像→灰度化→阈值分割(提取标线)→ROI 裁剪(只留前方区域)→得到标线像素坐标集合 (x1,y1), (x2,y2), ..., (xn,yn)。
线性回归计算(最小二乘法核心公式):
计算均值:x_mean = (x1+x2+...+xn)/n,y_mean = (y1+y2+...+yn)/n
计算分子分母:
分子 sum_xy = Σ(xi - x_mean)(yi - y_mean)(协方差和)
分母 sum_xx = Σ(xi - x_mean)²(x 的方差和)
求解参数:k = sum_xy / sum_xx(斜率),b = y_mean - k*x_mean(截距)
方向决策:根据斜率 k 判断标线偏移方向(如 k=0 为直行,k>0 向左偏,k<0 向右偏),输出转向控制信号。

4、巡线场景优化技巧(抗干扰 + 提速度)
数据去噪:
过滤孤立像素:只保留相邻像素数大于 5 的连通区域,剔除单点噪声。
异常值剔除:计算所有像素到初始拟合直线的距离,剔除距离过大的异常点(如超出 3 倍标准差),重新拟合。
速度优化:
减少计算量:ROI 区域设为 320x120(小尺寸),像素数控制在 1 万以内,单帧计算耗时 < 1ms。
硬件加速:在 K230 上,用 NPU 加速阈值分割和 ROI 提取,CPU 只负责回归计算,进一步降低延迟。
鲁棒性增强:
滑动窗口拟合:用最近 3 帧的拟合结果做滑动平均(如 k_final = (k1+2k2+3k3)/6),避免单帧噪声导致的方向突变。
多线段拟合:若标线断裂,分多个 ROI 分别拟合,取主要线段的斜率作为决策依据。

5、实操示例(Python+OpenCV,适配巡线)
python
  1. import cv2
  2. import numpy as np
  3. def fast_linear_regression(points):
  4.     """快速线性回归:输入像素坐标列表,输出k(斜率)、b(截距)"""
  5.     x = points[:, 0]
  6.     y = points[:, 1]
  7.     n = len(x)
  8.     if n < 5:  # 像素数过少,返回无效值
  9.         return 0, 0
  10.    
  11.     # 核心公式计算
  12.     x_mean = np.mean(x)
  13.     y_mean = np.mean(y)
  14.     sum_xy = np.sum((x - x_mean) * (y - y_mean))
  15.     sum_xx = np.sum((x - x_mean) ** 2)
  16.    
  17.     if sum_xx < 1e-6:  # 避免除以0(近似竖直线)
  18.         return 0, y_mean
  19.    
  20.     k = sum_xy / sum_xx
  21.     b = y_mean - k * x_mean
  22.     return k, b
  23. # 模拟巡线图像(黑色背景+白色标线)
  24. cap = cv2.VideoCapture(0)  # 替换为K230摄像头
  25. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
  26. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
  27. while True:
  28.     ret, frame = cap.read()
  29.     if not ret:
  30.         break
  31.    
  32.     # 1. 预处理:提取白色标线(巡线场景常用)
  33.     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  34.     _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)  # 白色阈值
  35.    
  36.     # 2. ROI提取:只取图像底部1/3区域(车辆前方)
  37.     h, w = thresh.shape
  38.     roi = thresh[int(h*2/3):h, 0:w]
  39.     roi_h, roi_w = roi.shape
  40.    
  41.     # 3. 提取标线像素坐标
  42.     points = np.column_stack(np.where(roi == 255))  # (y, x) 格式
  43.     if len(points) == 0:
  44.         cv2.putText(frame, "No Line Detected", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
  45.         cv2.imshow("Line Fitting", frame)
  46.         if cv2.waitKey(1) == ord('q'):
  47.             break
  48.         continue
  49.    
  50.     # 坐标转换:roi的y轴转换为全局y轴,x轴不变
  51.     points[:, 0] += int(h*2/3)  # y坐标偏移
  52.     points = points[:, [1, 0]]  # 转换为 (x, y) 格式
  53.    
  54.     # 4. 快速线性回归拟合
  55.     k, b = fast_linear_regression(points)
  56.    
  57.     # 5. 绘制拟合直线(在原图上)
  58.     x1 = 0
  59.     y1 = int(k * x1 + b)
  60.     x2 = w
  61.     y2 = int(k * x2 + b)
  62.     cv2.line(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
  63.    
  64.     # 6. 显示转向信息
  65.     direction = "Straight" if abs(k) < 0.1 else "Left" if k > 0 else "Right"
  66.     cv2.putText(frame, f"Direction: {direction} (k={k:.2f})", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
  67.    
  68.     cv2.imshow("Line Fitting", frame)
  69.     if cv2.waitKey(1) == ord('q'):
  70.         break
  71. cap.release()
  72. cv2.destroyAllWindows()
复制代码

6、K230 平台适配要点
性能优化:将 Python 代码转为 C/C++(K230 SDK 支持),单帧拟合耗时可降至 0.5ms 内,满足实时巡线需求(≥30fps)。
硬件协同:用 K230 的 ISP 模块预处理图像(自动曝光、降噪),NPU 加速阈值分割和 ROI 提取,CPU 专注回归计算。
接口适配:拟合结果(k,b)可直接通过 UART 或 GPIO 输出给电机控制模块,实现 “检测 - 决策 - 控制” 闭环。

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图2

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图1

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】CanMV K230 AI视觉识别之快速线性回归

【花雕动手做】CanMV K230 AI 视觉识别模块之巡线基础:快速线性回归
项目测试实验代码

  1. #【花雕动手做】CanMV K230 AI 视觉识别模块之巡线基础:快速线性回归
  2. import time, os, sys
  3. from media.sensor import *
  4. from media.display import *
  5. from media.media import *
  6. # 图像处理参数 / Image processing parameters
  7. THRESHOLD = (0, 100)  # 灰度阈值范围:检测0-100灰度值的像素 / Grayscale threshold range
  8. BINARY_VISIBLE = True  # 是否显示二值化图像 / Whether to show binary image
  9.                       # 注意:启用二值化可能降低FPS / Note: enabling binary mode may reduce FPS
  10. # 显示参数 / Display parameters
  11. DISPLAY_WIDTH = 640   # LCD显示宽度 / LCD display width
  12. DISPLAY_HEIGHT = 480  # LCD显示高度 / LCD display height
  13. SENSOR_WIDTH = 640    # 图像宽度 / Image width
  14. SENSOR_HEIGHT = 480   # 图像高度 / Image height
  15. def init_sensor():
  16.     """初始化摄像头 / Initialize camera sensor"""
  17.     # sensor = Sensor(width=1280, height=960)  # 4:3比例 / 4:3 aspect ratio (已注释)
  18.     sensor = Sensor()  # 创建传感器实例
  19.     sensor.reset()     # 重置传感器到默认状态
  20.    
  21.     # 设置图像分辨率为640x480
  22.     sensor.set_framesize(width=SENSOR_WIDTH, height=SENSOR_HEIGHT)
  23.    
  24.     # 设置像素格式为灰度图像
  25.     # 灰度图单通道处理,提高线性回归计算速度
  26.     sensor.set_pixformat(Sensor.GRAYSCALE)
  27.     return sensor
  28. def init_display():
  29.     """初始化显示 / Initialize display"""
  30.     # 初始化ST7701显示屏,同时输出到IDE便于调试
  31.     Display.init(Display.ST7701, to_ide=True)
  32.    
  33.     # 初始化媒体管理器,分配图像处理资源
  34.     MediaManager.init()
  35. def process_image(img):
  36.     """
  37.     处理图像并进行线性回归 / Process image and perform linear regression
  38.     参数: img - 输入的灰度图像
  39.     返回: line - 检测到的线性回归结果对象
  40.     """
  41.     # 二值化处理 / Binary thresholding
  42.     # 如果启用二值化显示,将图像转换为黑白二值图像
  43.     if BINARY_VISIBLE:
  44.         img = img.binary([THRESHOLD])  # 根据阈值将图像二值化
  45.     # 线性回归检测 / Linear regression detection
  46.     # 在图像中寻找符合阈值条件的像素点,并用直线拟合这些点
  47.     # 参数说明:
  48.     # - 如果BINARY_VISIBLE为True,则查找白色像素(255,255)
  49.     # - 如果为False,则在THRESHOLD阈值范围内查找像素
  50.     # magnitude(): 回归拟合度指标(0,INF], 0表示圆形,值越大越线性
  51.     # magnitude(): regression fitness index (0,INF], 0=circle, larger=more linear
  52.     line = img.get_regression([(255,255) if BINARY_VISIBLE else THRESHOLD])
  53.     # 如果检测到直线
  54.     if line:
  55.         # 在图像上绘制检测线 / Draw detected line on image
  56.         # line.line(): 返回直线的起点和终点坐标 (x1, y1, x2, y2)
  57.         # color=127: 灰色,在灰度图中显示为中等灰度
  58.         # thickness=4: 4像素线宽,确保线条清晰可见
  59.         img.draw_line(line.line(), color=127, thickness=4)
  60.         
  61.         # 打印直线信息(包含角度、长度、拟合度等)
  62.         print(line)
  63.     return line
  64. def main():
  65.     try:
  66.         # 初始化设备 / Initialize devices
  67.         sensor = init_sensor()  # 初始化摄像头
  68.         init_display()          # 初始化显示系统
  69.         sensor.run()            # 启动摄像头采集
  70.         # 初始化性能监控时钟
  71.         clock = time.clock()
  72.         # 计算显示偏移量以居中显示 / Calculate display offsets for center alignment
  73.         # 由于处理分辨率和显示分辨率相同(640x480),偏移量为0
  74.         x_offset = round((DISPLAY_WIDTH - SENSOR_WIDTH) / 2)    # (640-640)/2 = 0
  75.         y_offset = round((DISPLAY_HEIGHT - SENSOR_HEIGHT) / 2)  # (480-480)/2 = 0
  76.         # 主循环 - 实时线性回归检测
  77.         while True:
  78.             clock.tick()  # 更新时钟,用于FPS计算
  79.             # 捕获图像 / Capture image
  80.             img = sensor.snapshot()  # 从摄像头获取一帧灰度图像
  81.             # 处理图像 / Process image
  82.             line = process_image(img)  # 执行线性回归检测
  83.             # 显示图像 / Display image
  84.             Display.show_image(img, x=x_offset, y=y_offset)  # 全屏显示处理后的图像
  85.             # 打印FPS和拟合度 / Print FPS and magnitude
  86.             # magnitude: 线性拟合质量指标,值越大表示直线特征越明显
  87.             magnitude = str(line.magnitude()) if line else "N/A"  # 如果有直线则获取拟合度,否则显示"N/A"
  88.             print(f"FPS {clock.fps()}, mag = {magnitude}")  # 输出帧率和拟合度
  89.     except KeyboardInterrupt as e:
  90.         # 处理用户中断(Ctrl+C)
  91.         print("用户中断 / User interrupted: ", e)
  92.     except Exception as e:
  93.         # 处理其他所有异常
  94.         print(f"发生错误 / Error occurred: {e}")
  95.     finally:
  96.         # 清理资源 / Cleanup resources(确保资源正确释放)
  97.         if 'sensor' in locals() and isinstance(sensor, Sensor):
  98.             sensor.stop()        # 停止摄像头采集
  99.         Display.deinit()         # 关闭显示驱动
  100.         MediaManager.deinit()    # 释放媒体资源
  101. if __name__ == "__main__":
  102.     main()  # 程序入口点
复制代码



回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】CanMV K230 AI视觉识别之快速线性回归

代码结构
图像获取和处理:
通过摄像头捕获640x480分辨率的灰度图像
可以选择是否对图像进行二值化处理(将灰度值在0-100范围内的像素转为白色)

线性回归检测:
对图像进行线性回归分析,寻找图像中的直线特征
计算回归线的拟合度(magnitude值),数值越大表示越接近直线
在检测到直线时,在图像上绘制这条线

显示功能:
使用LCD屏幕显示处理后的图像
图像在屏幕上居中显示
实时显示FPS(每秒帧数)和直线拟合度

程序结构:
初始化部分:设置摄像头参数、显示器和媒体管理器
主循环部分:不断捕获图像、处理、显示的过程
异常处理:包含完整的异常处理机制和资源清理

快速线性回归算法
  1. get_regression()
  2. image.get_regression(thresholds[, invert=False[, roi[, x_stride=2[, y_stride=1[, area_threshold=10[, pixels_threshold=10[, robust=False]]]]]]])
复制代码

对图像所有阈值像素进行线性回归计算。这一计算通过最小二乘法进行,通常速度较快,但不能处理任何异常值。 若 robust 为True,则将使用泰尔指数。泰尔指数计算图像中所有阈值像素间的所有斜率的中值。 若在阈值转换后设定太多像素,即使在80x60的图像上,这一N^2操作也可能将您的FPS降到5以下。 但是,只要阈值转换后的进行设置的像素数量较少,即使在超过30%的阈值像素为异常值的情况下,线性回归也依然有效。
这一方法返回的是一个 image.line 对象。
thresholds 必须是元组列表。 [(lo, hi), (lo, hi), …, (lo, hi)] 定义你想追踪的颜色范围。 对于灰度图像,每个元组需要包含两个值 - 最小灰度值和最大灰度值。 仅考虑落在这些阈值之间的像素区域。 对于RGB565图像,每个元组需要有六个值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分别是LAB L,A和B通道的最小值和最大值。 为方便使用,此功能将自动修复交换的最小值和最大值。 此外,如果元组大于六个值,则忽略其余值。相反,如果元组太短,则假定其余阈值处于最大范围。

注:除了快速线性回归算法以外,巡线还有其它可行的方案,我们需要根据实际情况去尽量选择最优的一种解决方案。

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 1 小时前

【花雕动手做】CanMV K230 AI视觉识别之快速线性回归

实验串口返回情况

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图1

实验场景图

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图3

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图5

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图2

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图4

【花雕动手做】CanMV K230 AI 视觉识别之快速线性回归图6


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail