
什么是 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视觉识别模块之使用照相机
勘智 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
运行
import cv2
import time
# 初始化摄像头(/dev/video0 对应第一个摄像头)
cap = cv2.VideoCapture(0)
# 设置分辨率(需与摄像头支持格式匹配)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_FPS, 30) # 帧率30fps
if not cap.isOpened():
print("勘智K230 摄像头打开失败!")
exit()
print("摄像头启动成功,按 's' 拍照,按 'q' 退出")
while True:
ret, frame = cap.read() # 读取一帧图像
if not ret:
print("图像采集失败")
break
# 实时显示画面(标注K230标识)
cv2.putText(frame, "K230 AI Camera", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
cv2.imshow("K230 Capture", frame)
# 按键处理
key = cv2.waitKey(1) & 0xFF
if key == ord('q'): # 退出
break
elif key == ord('s'): # 拍照保存
filename = f"k230_capture_{time.strftime('%Y%m%d%H%M%S')}.jpg"
cv2.imwrite(filename, frame)
print(f"照片已保存:{filename}")
# 释放资源
cap.release()
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
运行
import cv2
import numpy as np
from k230_tengine import TEngine # 勘智Tengine-Lite适配库
# 初始化摄像头
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 初始化K230 NPU模型(目标检测)
model = TEngine(model_path="/root/models/yolov8n_k230.bmodel")
class_names = ["person", "bicycle", "car", ...] # YOLOv8n类别列表
while True:
ret, frame = cap.read()
if not ret:
break
# 图像预处理(适配模型输入:320x320,归一化)
input_img = cv2.resize(frame, (320, 320))
input_img = input_img / 255.0
input_img = np.transpose(input_img, (2, 0, 1)).astype(np.float32) # CHW格式
# NPU推理(获取检测结果)
results = model.infer([input_img])
# 绘制识别结果(框选目标+标注类别)
for det in results[0]:
x1, y1, x2, y2, conf, cls_id = det
if conf > 0.5: # 置信度阈值
# 转换为原图坐标
x1 = int(x1 * 640 / 320)
y1 = int(y1 * 480 / 320)
x2 = int(x2 * 640 / 320)
y2 = int(y2 * 480 / 320)
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, f"{class_names[int(cls_id)]} {conf:.2f}",
(x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 显示带AI识别的画面
cv2.imshow("K230 AI Detection", frame)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
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视觉识别模块之使用照相机
项目测试实验代码
#【花雕动手做】CanMV K230 AI视觉识别模块之使用照相机
# 导入必要的系统和操作系统模块
# Import necessary system and OS modules
import uos
import time
from media.sensor import *
from media.display import *
from media.media import *
import ybUtils.YbKey as YbKey # 导入按键模块
# 定义图片保存路径和文件命名相关变量
# Define variables for image saving path and file naming
save_path = "/data/snapshot/" # 保存基础路径 Base saving path
prefix = time.ticks_us() % 10000 # 使用时间戳作为文件夹名 Use timestamp as folder name
i = 1 # 照片计数器 Photo counter
# 保存图像原始数据,可使用7yuv预览
# save image raw data, use 7yuv to preview
def ensure_dir(directory):
"""
递归创建目录
(Recursively create directory)
功能:确保指定的目录存在,如果不存在则递归创建
参数:directory - 要创建的目录路径
"""
# 如果目录为空字符串或根目录,直接返回
# (If directory is empty string or root directory, return directly)
if not directory or directory == '/':
return
# 处理路径分隔符,确保使用标准格式
# (Process path separators to ensure standard format)
directory = directory.rstrip('/')
try:
# 尝试获取目录状态,如果目录存在就直接返回
# (Try to get directory status, if directory exists then return directly)
os.stat(directory)
print(f'目录已存在: {directory}')
# (Directory already exists: {directory})
return
except OSError:
# 目录不存在,需要创建
# (Directory does not exist, need to create)
# 分割路径以获取父目录
# (Split path to get parent directory)
if '/' in directory:
parent = directory[:directory.rindex('/')]
if parent and parent != directory: # 避免无限递归
# (Avoid infinite recursion)
ensure_dir(parent) # 递归创建父目录
try:
# 创建目录
# (Create directory)
os.mkdir(directory)
print(f'已创建目录: {directory}')
# (Directory created: {directory})
except OSError as e:
# 可能是并发创建导致的冲突,再次检查目录是否存在
# (Possible conflict due to concurrent creation, check again if directory exists)
try:
os.stat(directory)
print(f'目录已被其他进程创建: {directory}')
# (Directory has been created by another process: {directory})
except:
# 如果仍然不存在,则确实出错了
# (If it still doesn't exist, there is definitely an error)
print(f'创建目录时出错: {e}')
# (Error creating directory: {e})
except Exception as e:
# 捕获其他可能的异常
# (Catch other possible exceptions)
print(f'处理目录时出错: {e}')
# (Error processing directory: {e})
if __name__ == "__main__":
try:
# 初始化按键检测
# Initialize key detection
key = YbKey.YbKey()
# 使用默认配置构造传感器对象
# Construct a Sensor object with default configuration
sensor = Sensor()
# 重置传感器
# Reset sensor
sensor.reset()
# 设置通道1的输出格式
# Set channel 1 output format
sensor.set_framesize(width=640, height=480, chn=CAM_CHN_ID_1)
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)
# 初始化显示
# Initialize display
Display.init(Display.ST7701, width=640, height=480, to_ide=True)
# 初始化媒体管理器
# Initialize media manager
MediaManager.init()
# 启动传感器
# Start sensor
sensor.run()
last_status = False # 记录上一次按键状态,用于检测按键按下事件
# 主循环
# Main loop
while True:
# 捕获图像
# Capture image
img = sensor.snapshot(chn=CAM_CHN_ID_1)
# 创建显示用的图像缓冲
# Create image buffer for display
img2 = image.Image(640, 480, image.RGB565)
img2.clear()
img2.copy_from(img) # 将原始图像复制到显示缓冲
# 在图像上绘制信息文本
# Draw information text on image
img2.draw_string_advanced(10, 10, 30, "存储目录: " + str(prefix) + ", 照片 " + str(i) + " ", color=(255, 0, 0))
img2.draw_string_advanced(10, 45, 30, "Save Folder: " + str(prefix) + " , photo: " + str(i) + " ", color=(255, 0, 0))
# 显示带文本的图像
# Display image with text
Display.show_image(img2, 0, 0, Display.LAYER_OSD2)
# 重新初始化按键检测(可能需要刷新状态)
# Reinitialize key detection (may need to refresh state)
key = YbKey.YbKey()
# 按键检测和图片保存逻辑
# Button detection and image saving logic
if key.is_pressed() == 1:
if last_status == False:
# 检测到按键按下事件(从松开到按下)
# Detected key press event (from released to pressed)
last_status = True
######################### 保存图片流程 #########################
# 确保保存目录存在
# Ensure save directory exists
ensure_dir(save_path + str(prefix) + "/")
# 构建完整文件路径
# Build complete file path
path = save_path + str(prefix) + "/" + str(i) + ".jpg"
i = i + 1 # 递增照片计数器
print(path) # 打印保存路径
# 保存图像为JPEG格式
# Save image as JPEG format
img.save(path)
print("已保存至:" + path)
time.sleep_ms(1) # 短暂延时,防止按键抖动
#########################
else:
# 按键未按下,重置状态
# Key not pressed, reset state
last_status = False
except KeyboardInterrupt as e:
# 处理用户中断(Ctrl+C)
# Handle user interrupt (Ctrl+C)
print(f"用户停止程序 User stopped the program")
except BaseException as e:
# 处理其他所有异常
# Handle all other exceptions
print(f"发生异常 Exception occurred: '{e}'")
finally:
# 清理资源和退出程序
# Clean up resources and exit program
# 停止传感器
# Stop sensor
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示
# Deinitialize display
Display.deinit()
# 启用睡眠模式
# Enable sleep mode
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100) # 等待资源完全释放
# 释放媒体缓冲
# Release media buffer
MediaManager.deinit()代码解读:
程序总体功能
这是一个完整的数码相机应用,使用CanMV K230模块实现实时预览、按键拍照和照片存储功能。
系统架构分析
核心组件
text
摄像头传感器 → 图像处理 → 显示输出 → 按键检测 → 文件存储
1. 初始化阶段
python
# 硬件初始化流程
sensor = Sensor() # 创建摄像头对象
sensor.reset() # 重置摄像头
sensor.set_framesize(640, 480) # 设置分辨率
sensor.set_pixformat(Sensor.RGB565) # 设置图像格式
Display.init(...) # 初始化显示
MediaManager.init() # 初始化媒体管理
sensor.run() # 启动摄像头
2. 主循环工作流程
text
捕获图像 → 添加文字水印 → 显示预览 → 检测按键 → 保存照片
关键技术实现
1. 智能目录管理
python
def ensure_dir(directory):
# 递归创建目录结构
# 示例:/data/snapshot/1234/1.jpg
递归创建:自动创建多级目录
容错处理:处理目录已存在、并发创建等边界情况
路径安全:规范化路径分隔符
2. 照片命名策略
python
prefix = time.ticks_us() % 10000 # 时间戳作为文件夹名
i = 1 # 照片计数器
path = f"/data/snapshot/{prefix}/{i}.jpg"
唯一性:使用微秒时间戳避免冲突
组织性:每次运行创建独立文件夹
顺序性:自动递增照片编号
3. 按键检测机制
python
if key.is_pressed() == 1:
if last_status == False: # 边缘检测
# 执行拍照
last_status = True
边缘触发:只在按键按下瞬间触发
防抖动:避免重复触发
状态管理:跟踪按键状态变化
4. 用户界面设计
python
img2.draw_string_advanced(10, 10, 30, "存储目录: " + str(prefix) + ", 照片 " + str(i) + " ", color=(255, 0, 0))
实时状态显示:显示当前目录和照片计数
视觉反馈:红色文字突出重要信息
双语言支持:中英文提示信息
性能优化特性
1. 图像处理优化
python
img2 = image.Image(640, 480, image.RGB565)
img2.copy_from(img)
双缓冲机制:原始图像+显示图像分离
格式一致:使用RGB565节省内存
高效复制:直接内存拷贝避免格式转换
2. 资源管理
分层初始化:确保硬件正确初始化顺序
异常安全:完整的try-except-finally保护
彻底清理:程序退出时释放所有资源
错误处理机制
三级保护策略
python
try:
# 主程序逻辑
except KeyboardInterrupt: # 用户主动中断
print("用户停止程序")
except BaseException: # 其他所有异常
print(f"发生异常: '{e}'")
finally: # 强制清理
# 释放所有资源
具体清理步骤
停止传感器: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. 可扩展架构
模块化设计便于功能扩展
实验串口返回情况

实验场景图




返回首页
回到顶部
评论