6浏览
查看: 6|回复: 3

[项目] 【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机

[复制链接]
【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图2

什么是 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视觉识别模块之使用照相机图1

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图3

驴友花雕  高级技神
 楼主|

发表于 4 小时前

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机

本帖最后由 驴友花雕 于 2025-11-6 12:03 编辑

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机

勘智 K230(平头哥旗下)AI 视觉模块的照相机使用,核心是MIPI/USB 摄像头硬件适配 + 驱动配置 + 图像采集(含 AI 推理联动),以下是针对勘智 K230 模块的专属实操指南,覆盖从硬件到编程的完整流程:
一、勘智 K230 摄像头硬件适配
1. 摄像头选型(官方推荐兼容型号)
K230 模块的 CSI 接口和驱动对以下摄像头支持最优,无需额外修改设备树:
MIPI-CSI 摄像头(优先选,低延迟):OV5640(500 万像素)、GC2083(200 万像素)、OV2640(200 万像素),支持 640x480、1280x720 等分辨率。
USB 摄像头(兼容 fallback):免驱动 UVC 摄像头(如罗技 C270),支持 USB 2.0/3.0,适合快速测试。
2. 硬件连接步骤(以 MIPI 摄像头为例)
确认 K230 模块的 CSI 接口(通常标注 “CSI0”“CSI1”),使用 FPC 排线连接摄像头(注意排线金手指朝向,与接口丝印对齐,插紧卡扣)。
摄像头供电:多数兼容型号支持 3.3V 供电,直接从 K230 模块的 3.3V 引脚取电(避免外接电源导致电压不稳)。
若用 USB 摄像头:直接插入 K230 的 USB-A 接口,模块自动识别 UVC 设备。

二、环境与驱动配置(勘智 K230 专属)
1. 系统与 SDK 准备
安装勘智 K230 官方 SDK:从平头哥官网下载 k230_sdk_vx.x,包含定制化 Linux 镜像(Buildroot/Debian)、摄像头驱动、ISP 工具(tioxygen)。
烧录系统:用官方 kflash 工具将镜像写入 SD 卡,插入 K230 模块启动(默认已预装 v4l2-utils ffmpeg 等工具)。
2. 驱动加载与验证
MIPI 摄像头(以 OV5640 为例):
开机后执行 dmesg | grep ov5640,查看驱动是否自动加载(无报错则成功)。
若未加载,手动加载驱动:insmod /lib/modules/$(uname -r)/drivers/media/i2c/ov5640.ko。
USB 摄像头:插入后执行 ls /dev/video*,出现 /dev/video0 或 /dev/video1 即识别成功(内核自带 uvcvideo 驱动)。
验证摄像头能力:v4l2-ctl --device=/dev/video0 --list-formats-ext,查看支持的分辨率、帧率(如 YUYV 格式 640x480@30fps)。

三、基础操作:拍照 / 预览(快速上手)
1. 命令行操作(无需编程)
拍照保存:ffmpeg -f v4l2 -video_size 640x480 -i /dev/video0 -vframes 1 k230_capture.jpg,图像保存到当前目录。
实时预览:需 K230 模块连接 HDMI 显示器,执行 ffplay -f v4l2 -video_size 640x480 -i /dev/video0,屏幕显示实时画面。
连续录制视频:ffmpeg -f v4l2 -video_size 1280x720 -i /dev/video0 -t 10 k230_record.mp4(录制 10 秒 720P 视频)。
2. 编程实现(Python + OpenCV,勘智适配版)
K230 支持 Python 3.8+,需先安装 OpenCV(pip3 install opencv-python),以下代码适配 MIPI/USB 摄像头:
python
  1. import cv2
  2. import time
  3. # 初始化摄像头(/dev/video0 对应第一个摄像头)
  4. cap = cv2.VideoCapture(0)
  5. # 设置分辨率(需与摄像头支持格式匹配)
  6. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
  7. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
  8. cap.set(cv2.CAP_PROP_FPS, 30)  # 帧率30fps
  9. if not cap.isOpened():
  10.     print("勘智K230 摄像头打开失败!")
  11.     exit()
  12. print("摄像头启动成功,按 's' 拍照,按 'q' 退出")
  13. while True:
  14.     ret, frame = cap.read()  # 读取一帧图像
  15.     if not ret:
  16.         print("图像采集失败")
  17.         break
  18.     # 实时显示画面(标注K230标识)
  19.     cv2.putText(frame, "K230 AI Camera", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
  20.     cv2.imshow("K230 Capture", frame)
  21.     # 按键处理
  22.     key = cv2.waitKey(1) & 0xFF
  23.     if key == ord('q'):  # 退出
  24.         break
  25.     elif key == ord('s'):  # 拍照保存
  26.         filename = f"k230_capture_{time.strftime('%Y%m%d%H%M%S')}.jpg"
  27.         cv2.imwrite(filename, frame)
  28.         print(f"照片已保存:{filename}")
  29. # 释放资源
  30. cap.release()
  31. cv2.destroyAllWindows()
复制代码

四、AI 视觉联动:采集 + 识别(勘智 K230 核心能力)
K230 的 NPU(算力 1TOPS)可直接运行轻量 AI 模型,以下是 “摄像头采集 + 目标检测” 完整示例:
1. 模型准备
下载预训练轻量模型(如 YOLOv8n),用勘智官方工具 bmnetc 转换为 K230 支持的 bmodel 格式(命令:bmnetc --model yolov8n.onnx --output yolov8n_k230.bmodel)。
将转换后的 yolov8n_k230.bmodel 上传到 K230 模块的 /root/models 目录。
2. 采集 + 识别代码(Python)
python
  1. import cv2
  2. import numpy as np
  3. from k230_tengine import TEngine  # 勘智Tengine-Lite适配库
  4. # 初始化摄像头
  5. cap = cv2.VideoCapture(0)
  6. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
  7. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
  8. # 初始化K230 NPU模型(目标检测)
  9. model = TEngine(model_path="/root/models/yolov8n_k230.bmodel")
  10. class_names = ["person", "bicycle", "car", ...]  # YOLOv8n类别列表
  11. while True:
  12.     ret, frame = cap.read()
  13.     if not ret:
  14.         break
  15.     # 图像预处理(适配模型输入:320x320,归一化)
  16.     input_img = cv2.resize(frame, (320, 320))
  17.     input_img = input_img / 255.0
  18.     input_img = np.transpose(input_img, (2, 0, 1)).astype(np.float32)  # CHW格式
  19.     # NPU推理(获取检测结果)
  20.     results = model.infer([input_img])
  21.     # 绘制识别结果(框选目标+标注类别)
  22.     for det in results[0]:
  23.         x1, y1, x2, y2, conf, cls_id = det
  24.         if conf > 0.5:  # 置信度阈值
  25.             # 转换为原图坐标
  26.             x1 = int(x1 * 640 / 320)
  27.             y1 = int(y1 * 480 / 320)
  28.             x2 = int(x2 * 640 / 320)
  29.             y2 = int(y2 * 480 / 320)
  30.             cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
  31.             cv2.putText(frame, f"{class_names[int(cls_id)]} {conf:.2f}",
  32.                         (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
  33.     # 显示带AI识别的画面
  34.     cv2.imshow("K230 AI Detection", frame)
  35.     if cv2.waitKey(1) == ord('q'):
  36.         break
  37. cap.release()
  38. cv2.destroyAllWindows()
复制代码

五、勘智 K230 专属注意事项
ISP 优化:K230 内置 ISP 模块,可通过 tioxygen-tools 调整摄像头参数(如白平衡、曝光),命令:tioxygen-isp -d /dev/video0 -ex auto -wb auto(自动曝光 + 自动白平衡)。
性能调优:AI 推理时,建议将摄像头分辨率设为 640x480(与模型输入缩放比匹配),单帧处理延迟可控制在 50ms 内。
常见问题排查:
摄像头无画面:检查 dmesg | grep video 查看驱动报错,重新插拔 FPC 排线(MIPI)或 USB 接口。
推理卡顿:降低模型输入尺寸(如 320x320),关闭不必要的后台进程。
图像偏色:用 ISP 工具校准白平衡,或在代码中添加色彩校正(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))。
【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图1


回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 4 小时前

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机
项目测试实验代码

  1. #【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机
  2. # 导入必要的系统和操作系统模块
  3. # Import necessary system and OS modules
  4. import uos
  5. import time
  6. from media.sensor import *
  7. from media.display import *
  8. from media.media import *
  9. import ybUtils.YbKey as YbKey  # 导入按键模块
  10. # 定义图片保存路径和文件命名相关变量
  11. # Define variables for image saving path and file naming
  12. save_path = "/data/snapshot/"  # 保存基础路径 Base saving path
  13. prefix = time.ticks_us() % 10000  # 使用时间戳作为文件夹名 Use timestamp as folder name
  14. i = 1  # 照片计数器 Photo counter
  15. # 保存图像原始数据,可使用7yuv预览
  16. # save image raw data, use 7yuv to preview
  17. def ensure_dir(directory):
  18.     """
  19.     递归创建目录
  20.     (Recursively create directory)
  21.    
  22.     功能:确保指定的目录存在,如果不存在则递归创建
  23.     参数:directory - 要创建的目录路径
  24.     """
  25.     # 如果目录为空字符串或根目录,直接返回
  26.     # (If directory is empty string or root directory, return directly)
  27.     if not directory or directory == '/':
  28.         return
  29.     # 处理路径分隔符,确保使用标准格式
  30.     # (Process path separators to ensure standard format)
  31.     directory = directory.rstrip('/')
  32.     try:
  33.         # 尝试获取目录状态,如果目录存在就直接返回
  34.         # (Try to get directory status, if directory exists then return directly)
  35.         os.stat(directory)
  36.         print(f'目录已存在: {directory}')
  37.         # (Directory already exists: {directory})
  38.         return
  39.     except OSError:
  40.         # 目录不存在,需要创建
  41.         # (Directory does not exist, need to create)
  42.         # 分割路径以获取父目录
  43.         # (Split path to get parent directory)
  44.         if '/' in directory:
  45.             parent = directory[:directory.rindex('/')]
  46.             if parent and parent != directory:  # 避免无限递归
  47.                                                 # (Avoid infinite recursion)
  48.                 ensure_dir(parent)  # 递归创建父目录
  49.         try:
  50.             # 创建目录
  51.             # (Create directory)
  52.             os.mkdir(directory)
  53.             print(f'已创建目录: {directory}')
  54.             # (Directory created: {directory})
  55.         except OSError as e:
  56.             # 可能是并发创建导致的冲突,再次检查目录是否存在
  57.             # (Possible conflict due to concurrent creation, check again if directory exists)
  58.             try:
  59.                 os.stat(directory)
  60.                 print(f'目录已被其他进程创建: {directory}')
  61.                 # (Directory has been created by another process: {directory})
  62.             except:
  63.                 # 如果仍然不存在,则确实出错了
  64.                 # (If it still doesn't exist, there is definitely an error)
  65.                 print(f'创建目录时出错: {e}')
  66.                 # (Error creating directory: {e})
  67.     except Exception as e:
  68.         # 捕获其他可能的异常
  69.         # (Catch other possible exceptions)
  70.         print(f'处理目录时出错: {e}')
  71.         # (Error processing directory: {e})
  72. if __name__ == "__main__":
  73.     try:
  74.         # 初始化按键检测
  75.         # Initialize key detection
  76.         key = YbKey.YbKey()
  77.         # 使用默认配置构造传感器对象
  78.         # Construct a Sensor object with default configuration
  79.         sensor = Sensor()
  80.         
  81.         # 重置传感器
  82.         # Reset sensor
  83.         sensor.reset()
  84.         # 设置通道1的输出格式
  85.         # Set channel 1 output format
  86.         sensor.set_framesize(width=640, height=480, chn=CAM_CHN_ID_1)
  87.         sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)
  88.         # 初始化显示
  89.         # Initialize display
  90.         Display.init(Display.ST7701, width=640, height=480, to_ide=True)
  91.         # 初始化媒体管理器
  92.         # Initialize media manager
  93.         MediaManager.init()
  94.         
  95.         # 启动传感器
  96.         # Start sensor
  97.         sensor.run()
  98.         last_status = False  # 记录上一次按键状态,用于检测按键按下事件
  99.         
  100.         # 主循环
  101.         # Main loop
  102.         while True:
  103.             # 捕获图像
  104.             # Capture image
  105.             img = sensor.snapshot(chn=CAM_CHN_ID_1)
  106.             
  107.             # 创建显示用的图像缓冲
  108.             # Create image buffer for display
  109.             img2 = image.Image(640, 480, image.RGB565)
  110.             img2.clear()
  111.             img2.copy_from(img)  # 将原始图像复制到显示缓冲
  112.             
  113.             # 在图像上绘制信息文本
  114.             # Draw information text on image
  115.             img2.draw_string_advanced(10, 10, 30, "存储目录: " + str(prefix) + ", 照片 " + str(i) + " ", color=(255, 0, 0))
  116.             img2.draw_string_advanced(10, 45, 30, "Save Folder: " + str(prefix) + " , photo: " + str(i) + " ", color=(255, 0, 0))
  117.             
  118.             # 显示带文本的图像
  119.             # Display image with text
  120.             Display.show_image(img2, 0, 0, Display.LAYER_OSD2)
  121.             # 重新初始化按键检测(可能需要刷新状态)
  122.             # Reinitialize key detection (may need to refresh state)
  123.             key = YbKey.YbKey()
  124.             
  125.             # 按键检测和图片保存逻辑
  126.             # Button detection and image saving logic
  127.             if key.is_pressed() == 1:
  128.                 if last_status == False:
  129.                     # 检测到按键按下事件(从松开到按下)
  130.                     # Detected key press event (from released to pressed)
  131.                     last_status = True
  132.                     
  133.                     ######################### 保存图片流程 #########################
  134.                     # 确保保存目录存在
  135.                     # Ensure save directory exists
  136.                     ensure_dir(save_path + str(prefix) + "/")
  137.                     # 构建完整文件路径
  138.                     # Build complete file path
  139.                     path = save_path + str(prefix) + "/" + str(i) + ".jpg"
  140.                     i = i + 1  # 递增照片计数器
  141.                     
  142.                     print(path)  # 打印保存路径
  143.                     
  144.                     # 保存图像为JPEG格式
  145.                     # Save image as JPEG format
  146.                     img.save(path)
  147.                     
  148.                     print("已保存至:" + path)
  149.                     time.sleep_ms(1)  # 短暂延时,防止按键抖动
  150.                     #########################
  151.             else:
  152.                 # 按键未按下,重置状态
  153.                 # Key not pressed, reset state
  154.                 last_status = False
  155.                
  156.     except KeyboardInterrupt as e:
  157.         # 处理用户中断(Ctrl+C)
  158.         # Handle user interrupt (Ctrl+C)
  159.         print(f"用户停止程序 User stopped the program")
  160.     except BaseException as e:
  161.         # 处理其他所有异常
  162.         # Handle all other exceptions
  163.         print(f"发生异常 Exception occurred: '{e}'")
  164.     finally:
  165.         # 清理资源和退出程序
  166.         # Clean up resources and exit program
  167.         # 停止传感器
  168.         # Stop sensor
  169.         if isinstance(sensor, Sensor):
  170.             sensor.stop()
  171.             
  172.         # 反初始化显示
  173.         # Deinitialize display
  174.         Display.deinit()
  175.         # 启用睡眠模式
  176.         # Enable sleep mode
  177.         os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
  178.         time.sleep_ms(100)  # 等待资源完全释放
  179.         # 释放媒体缓冲
  180.         # Release media buffer
  181.         MediaManager.deinit()
复制代码

代码解读:

程序总体功能
这是一个完整的数码相机应用,使用CanMV K230模块实现实时预览、按键拍照和照片存储功能。

系统架构分析
核心组件
text
摄像头传感器 → 图像处理 → 显示输出 → 按键检测 → 文件存储

1. 初始化阶段
python
  1. # 硬件初始化流程
  2. sensor = Sensor()                    # 创建摄像头对象
  3. sensor.reset()                       # 重置摄像头
  4. sensor.set_framesize(640, 480)      # 设置分辨率
  5. sensor.set_pixformat(Sensor.RGB565) # 设置图像格式
  6. Display.init(...)                    # 初始化显示
  7. MediaManager.init()                  # 初始化媒体管理
  8. sensor.run()                         # 启动摄像头
复制代码

2. 主循环工作流程
text
捕获图像 → 添加文字水印 → 显示预览 → 检测按键 → 保存照片

关键技术实现
1. 智能目录管理
python
  1. def ensure_dir(directory):
  2.     # 递归创建目录结构
  3.     # 示例:/data/snapshot/1234/1.jpg
复制代码

递归创建:自动创建多级目录
容错处理:处理目录已存在、并发创建等边界情况
路径安全:规范化路径分隔符

2. 照片命名策略
python
  1. prefix = time.ticks_us() % 10000  # 时间戳作为文件夹名
  2. i = 1                             # 照片计数器
  3. path = f"/data/snapshot/{prefix}/{i}.jpg"
复制代码

唯一性:使用微秒时间戳避免冲突
组织性:每次运行创建独立文件夹
顺序性:自动递增照片编号

3. 按键检测机制
python
  1. if key.is_pressed() == 1:
  2.     if last_status == False:  # 边缘检测
  3.         # 执行拍照
  4.         last_status = True
复制代码

边缘触发:只在按键按下瞬间触发
防抖动:避免重复触发
状态管理:跟踪按键状态变化

4. 用户界面设计
python
  1. img2.draw_string_advanced(10, 10, 30, "存储目录: " + str(prefix) + ", 照片 " + str(i) + " ", color=(255, 0, 0))
复制代码

实时状态显示:显示当前目录和照片计数
视觉反馈:红色文字突出重要信息
双语言支持:中英文提示信息

性能优化特性
1. 图像处理优化
python
  1. img2 = image.Image(640, 480, image.RGB565)
  2. img2.copy_from(img)
复制代码

双缓冲机制:原始图像+显示图像分离
格式一致:使用RGB565节省内存
高效复制:直接内存拷贝避免格式转换

2. 资源管理
分层初始化:确保硬件正确初始化顺序
异常安全:完整的try-except-finally保护
彻底清理:程序退出时释放所有资源

错误处理机制
三级保护策略
python
  1. try:
  2.     # 主程序逻辑
  3. except KeyboardInterrupt:    # 用户主动中断
  4.     print("用户停止程序")
  5. except BaseException:        # 其他所有异常
  6.     print(f"发生异常: '{e}'")
  7. finally:                     # 强制清理
  8.     # 释放所有资源
复制代码

具体清理步骤
停止传感器:sensor.stop()
关闭显示:Display.deinit()
释放媒体资源:MediaManager.deinit()
系统睡眠:os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)

文件存储结构
生成的目录结构
text
/data/snapshot/
├── 8341/          # 时间戳命名的文件夹
│   ├── 1.jpg
│   ├── 2.jpg
│   └── 3.jpg
└── 9267/          # 另一次运行的文件夹
    ├── 1.jpg
    └── 2.jpg

用户体验特性
1. 实时反馈
视觉反馈:画面显示保存状态
控制台输出:打印保存路径和确认信息
计数显示:实时显示已拍照片数量

2. 操作便捷性
一键拍照:简单按键操作
自动管理:无需手动创建目录
防误操作:按键防抖动处理

技术亮点
1. 完整的相机功能链
从图像采集、处理、显示到存储的完整流程
2. 工业级错误处理
完善的异常处理和资源管理
3. 用户友好设计
直观的状态显示和操作反馈
4. 可扩展架构
模块化设计便于功能扩展

实验串口返回情况

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图1

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 3 小时前

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机

实验场景图

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图3

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图1

【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机图2
回复

使用道具 举报

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

本版积分规则

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

硬件清单

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

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

mail