| K210视觉模块 | MSPM0G3507 |
|---|---|
| 5V | 5V |
| GND | GND |
| RX | TX2 |
| TX | RX2 |
实物连接图

原理图



K210协议
| 实验例程 | 开始符 | 长度 | 例程编号 | 例程组 | 数据量 | x | 分隔符 | y | 分隔符 | w | 分隔符 | h | 分隔符 | 校验位 | 结束符 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 颜色识别 | $ | XX | 01 | BB | 04 | XXX | , | XXX | , | XXX | , | XXX | , | XX | # |
控制流程图
bsp_k210_use.c
x// 解析接收的完整消息 void deal_recvmsg(void){ ... if(r_index!=buf_len) { buf_len = 0; return ; } // 提取有效数据(过滤逗号) for(index = 0 ;index<number;index++) { if(buf_msg[4+index] == 0x2c && i_duo ==0) { i_duo = 1; continue; } data[data_i++]=buf_msg[4+index]; } // 重置接收状态 buf_crc = 0; r_index = 0; memset(buf_msg,0,sizeof(buf_msg)); deal_data(eg_num); }k210部分源码
xxxxxxxxxx# 数据发送函数:将坐标、尺寸、消息封装成协议格式并准备发送def send_data(x, y, w, h, msg): # 协议帧头(对应ASCII的'$') start = 0x24 # 协议帧尾(对应ASCII的'#') end = 0x23 # 基础长度(初始值,后续会根据数据长度更新) length = 5 # 例程编号(固定为0x01) class_num = 0x01 # 例程组(固定为0xBB) class_group = 0xBB # 数据量(初始值为0,后续统计实际数据个数) data_num = 0x00 # 分隔符(对应ASCII的逗号',') fenge = 0x2c # 校验和(初始值为0,后续计算) crc = 0 # 存储数据的列表(用于组装待发送数据) data = [] # 若坐标和尺寸全为0,不添加数据(跳过参数封装) if x == 0 and y == 0 and w == 0 and h == 0: pass else: # 封装x坐标(小端模式:低位字节在前,高位字节在后) low = x & 0xFF # 取x的低8位(低位字节) high = x >> 8 & 0xFF # 取x的高8位(高位字节) data.append(low) data.append(fenge) # 添加分隔符 data.append(high) data.append(fenge) # 添加分隔符 # 封装y坐标(小端模式) low = y & 0xFF high = y >> 8 & 0xFF data.append(low) data.append(fenge) data.append(high) data.append(fenge) ... # 若消息不为空,封装消息数据 if msg is not None: # 遍历消息的每个字符,转换后添加到数据列表 for i in range(len(msg)): hec = str_int(msg[i]) # 字符转十进制 data.append(hec) data.append(fenge) # 添加分隔符 # 更新数据量(数据列表的元素个数) data_num = len(data) # 更新总长度(基础长度 + 数据长度) length += len(data) # 组装核心数据(长度、例程编号、例程组、数据量 + 实际数据) send_merr = [length, class_num, class_group, data_num] for i in range(data_num): send_merr.append(data[i]) # 计算校验和(对核心数据的所有字节求和,再对256取模) for i in range(len(send_merr)): crc += send_merr[i] crc = crc % 256 # 组装完整协议帧:添加帧头、校验和、帧尾 ...# 定义中心采样方框(50x50像素,位于QVGA画面中心)BOX = 50r = [(320//2)-(BOX//2), (240//2)-(BOX//2), BOX, BOX]... # 组装完整协议帧(帧头 + 核心数据 + 校验和 + 帧尾) send_merr.insert(0, start) send_merr.append(crc) send_merr.append(end) …… img = sensor.snapshot() # 获取采样区域的颜色直方图(统计该区域内颜色分布,用于计算阈值) hist = img.get_histogram(roi=sample_roi) # 获取直方图1%分位值(对应颜色下限,过滤极暗/极淡的噪声像素) lo = hist.get_percentile(0.01) # 获取直方图70%分位值(对应颜色上限,覆盖目标物体的主要颜色范围) hi = hist.get_percentile(0.7) ……APP_K210X_Line_PID
| 函数原型 | void APP_K210X_Line_PID(void) |
|---|---|
| 功能描述 | 基于 K210 摄像头反馈的巡线 PID 控制函数:计算黑线在摄像头画面中的位置偏差(以屏幕中间 160 为基准,结合偏移量和目标区域参数),调用 APP_K210X_PID_Calc 得到转向控制量,通过 Motion_Ctrl 控制小车运动(直线速度为 K210X_SPEED) |
| 输入参数 | 无 |
| 返回值 | 无 |
BSP_Loop
| 函数原型 | void BSP_Loop(void) |
|---|---|
| 功能描述 | 主循环函数:检测按键短按事件切换 g_key_flag 状态。当 g_key_flag 为 0 时,停止电机、重置电机 PID;为 1 时,执行 APP_K210X_Line_PID 进行摄像头巡线控制 |
| 输入参数 | 无 |
| 返回值 | 无 |
下载打开CanMV IDE后,我们需要先将本节课程提供的k210源码将follow_line.py拖到CanMV IDE打开,然后连接IDE

IDE连接成功后现象如下

我们这里以follow_line.py为例,打开上方菜单栏的工具->保存当前打开的脚本为(main.py)到CanMV Cam


这里Yes/No都可以选择。出现以下状态就是写入成功了。

烧录程序后,打开电源开关,等待系统初始化完成后,LCD显示摄像头画面,并且屏幕中间有一个白色的方框,请将要识别的颜色放到白色方框内,等待白色方框变绿则开始采集颜色,采集完成绿框消失,开始运行程序。学习颜色学习成功后,按下开发板上的KEY1,小车就会开始巡线行驶,注意需要学习成功才能按下KEY1,否则需要根据环境调节直方图分位值。