资料链接
https://wiki.lckfb.com/zh-hans/lushan-pi-k230/
颜色阈值的设置
在 K230-CanMV
中,使用到颜色识别,就避免不了需要对应颜色的阈值,通过阈值去查找到指定的颜色。
【这里以红色阈值设置为案例】
1、使用 CanMV IDE K230
连接摄像头
打开 CanMV IDE K230
,连接上 K230-CanMV
摄像头。
调用摄像头教程:https://wiki.lckfb.com/zh-hans/lushan-pi-k230/image-recog/use-sensor.html
2、开启 CanMV IDE K230
采集颜色
案例是识别红色。我使用电脑上的画图软件,画出一个矩形。
然后使用摄像头采集图像数据到IDE。
3、打开阈值编辑器
在弹出的窗口中,选择帧缓冲区。
4、获取颜色阈值
通过调整滑块,将我们需要识别的颜色调整为白色,其他颜色调整为黑色。完成之后下方的LAB阈值就是颜色阈值了
案例一:串口通信
实现目标
使用 CanMV IDE K230
采集最大色块的中心位置,通过串口自定义格式 发送给开发板。
CanMV IDE K230
代码
内容讲解:查找图像中最大红色色块,通过串口2输出数据格式 "[%d,%d]" 给开发板的串口。
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
import time, os, sys
from media.sensor import *
from media.display import *
from media.media import *
from machine import UART
from machine import FPIOA
# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
sensor_id = 2
sensor = None
# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
#白色 颜色阈值
white_threshold = (53, 33, 11, 101, -124, 127)
# 查找最大色块函数
def find_max(blobs):
max_size=0
# 把blobs每个元素代入变量blob
for blob in blobs:
# 如果当前色块的像素量 跟之前比 是最多的
if blob.pixels() > max_size:
# 记录这个最多像素量的色块
max_blob=blob
# 更新最大像素量
max_size = blob.pixels()
return max_blob
uart.write("Hello World!\r") # 通过串口2输出固定字符串 “Hello World!”
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
# sensor.set_vflip(False)
# 设置通道0的输出尺寸为1920x1080
sensor.set_framesize(Sensor.VGA, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB888
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 使用IDE的帧缓冲区作为显示输出
Display.init(Display.VIRT, width=640, height=480, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# 在img对象中根据 传入的颜色阈值(white_threshold) 查找色块
blobs = img.find_blobs( [ white_threshold ] )
# 如果色块数量不为0
if blobs:
# 找到最大色块
max_blob=find_max(blobs)
# 在img图像中 对查找到的色块 画一个矩形
# blobs.rect() 色块的矩形参数
# draw_rectangle 画一个矩形框
img.draw_rectangle(max_blob.rect())
# 在图像上绘制一个十字。 cx代表中心x点 cy表示中心y点
img.draw_cross(max_blob.cx(), max_blob.cy())
# 整理字符串 将最大色块的 中心xy点 按照%d格式,格式化字符串
output_str="[%d,%d]" % ( max_blob.cx(), max_blob.cy() )
# 在IDE中输出字符串
print( 'Maximum color block position : ' + output_str + '\r\n' )
# 通过串口2输出字符串
uart.write( output_str + '\r\n' )
# 显示捕获的图像
Display.show_image(img)
except KeyboardInterrupt as e:
print("用户停止: ", e)
except BaseException as e:
print(f"异常: {e}")
finally:
# 停止传感器运行
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示模块
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
开发板配置
VCC(可以不接) | 5V0 |
GND | GND |
TXD | GPIO57 |
RXD | GPIO56 |
接下来我们配置 SYSCONFIG
- 双击 c2000.syscfg 文件,打开它。
- 点击 ADD 添加配置
- 配置
Ctrl + S
保存配置文件Ctrl + B
构建工一次工程(可能会报错,我们不用管!)然后我们所有设定的引脚和功能就会在 board.h 中定义。因为这个文件我们包含进了 tjx_init.h 所以我们只需要引用 tjx_init.h 即可。【这里的 tjx_init.h 就充当了芯片头文件的作用】
开发板代码
我们在工程中新建 module_driver
,并在其中新建两个文件 bsp_k230canmv.c
和 bsp_k230canmv.h
,并且将头文件路径添加到编译器中。
在文件bsp_k230canmv.c中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include "bsp_k230canmv.h"
#include "stdio.h"
#include "string.h"
/* 串口缓冲区的数据长度 */
#define K230_CANMV_USART_RECEIVE_LENGTH 100
uint8_t k230canmv_recv_buff[K230_CANMV_USART_RECEIVE_LENGTH]; // 接收缓冲区
volatile int k230canmv_recv_length = 0; // 接收数据长度
volatile char k230canmv_recv_flag = 0; // 接收数据完成标志位
void Clear_Recv_Buff(void)
{
// 清除接收完成标志位
k230canmv_recv_flag = 0;
// 清楚数据长度
k230canmv_recv_length = 0;
// 清除数据
memset(k230canmv_recv_buff, 0, K230_CANMV_USART_RECEIVE_LENGTH);
}
/******************************************************************
* 函 数 名 称:K230CanMV_DataAnalysis
* 函 数 说 明:解析K230 发送过来的自定义格式数据 [%d,%d] 解析
* 函 数 形 参:dat_buff:回传数据地址 buff_length:存储空间长度单位char
* 函 数 返 回: 0: 成功
* 1:未接到数据
* 2:未找到格式的头的第一个 '['
* 3:未找到找到结尾的 ']'
* 作 者:LCKFB
* 备 注:LP
******************************************************************/
uint8_t K230CanMV_DataAnalysis(uint8_t *dat_buff, uint8_t buff_length)
{
int head = 0, end = 0;
if (k230canmv_recv_flag == 0) return 1;
// 禁止 Module 串口接收中断
Interrupt_disable(INT_Module_Serial_RX);
#if K230_CANMV_DEBUG
lc_printf("RAW: %s\r\n", k230canmv_recv_buff);
#endif
// 找 '['
while (head < k230canmv_recv_length && k230canmv_recv_buff[head] != '[') {
head++;
}
if (head == k230canmv_recv_length) {
Clear_Recv_Buff();
Interrupt_enable(INT_Module_Serial_RX);
return 2;
}
// 从 head 开始再找 ']'
while (end < k230canmv_recv_length && k230canmv_recv_buff[head + end] != ']') {
end++;
}
if (end == k230canmv_recv_length) {
Clear_Recv_Buff();
Interrupt_enable(INT_Module_Serial_RX);
return 3;
}
// 复制 '[…]' 到 dat_buff
uint8_t len = end + 1; // 包括 ']' 的长度
if (len < buff_length) {
memcpy(dat_buff, &k230canmv_recv_buff[head], len);
dat_buff[len] = '\0';
#if K230_CANMV_DEBUG
lc_printf("PARSED: %s\r\n", dat_buff);
#endif
} else {
lc_printf("Data too large!\r\n");
}
Clear_Recv_Buff();
Interrupt_enable(INT_Module_Serial_RX);
return 0;
}
// module串口的中断服务函数
__interrupt void INT_Module_Serial_RX_ISR(void)
{
// 检查是否还有空间接收新数据
if (k230canmv_recv_length < K230_CANMV_USART_RECEIVE_LENGTH - 1) // 保留一个字符的空间用于'\0'
{
k230canmv_recv_buff[k230canmv_recv_length++] = SCI_readCharBlockingNonFIFO(Module_Serial_BASE); // 接收数据
}
else
{
uint8_t temp = SCI_readCharBlockingNonFIFO(Module_Serial_BASE); // 不保存数据
}
k230canmv_recv_buff[k230canmv_recv_length] = '\0'; // 确保字符串正确结束
k230canmv_recv_flag = 1; // 设置接收完成标志位
SCI_clearInterruptStatus(Module_Serial_BASE, SCI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
在文件bsp_k230canmv.h中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef __BSP_K230CANMV_H__
#define __BSP_K230CANMV_H__
#include "tjx_init.h"
#define K230_CANMV_DEBUG 0
uint8_t K230CanMV_DataAnalysis(uint8_t *dat_buff, uint8_t buff_length);
void Clear_Recv_Buff(void);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
案例验证
在 empty_driverlib_main.c
中输入代码如下:
#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "c2000ware_libraries.h"
#include "tjx_init.h"
#include "bsp_k230canmv.h"
void main(void)
{
/* The initialization code automatically generated by CCS [Start] */
Device_init();
Device_initGPIO();
Interrupt_initModule();
Interrupt_initVectorTable();
Board_init();
C2000Ware_libraries_init();
EINT;
ERTM;
/* The initialization code automatically generated by CCS [End] */
lc_printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
lc_printf("\r\n=== Welcome to use the LC-TJX-TMS320F28P550 ====\r\n");
lc_printf("\r\n============== www.lckfb.com ===================\r\n");
lc_printf("\r\n============== wiki.lckfb.com ==================\r\n");
lc_printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
while(1)
{
uint8_t dat_buff[50] = {0};
uint8_t ret = K230CanMV_DataAnalysis(&dat_buff, 50);
// 解析 lspi K230CanMV 发送过来的数据
if(ret == 0){
lc_printf("RcvData = %s\r\n", dat_buff);
}
else if(ret == 1){
lc_printf("Flag == 0\r\n");
}
else if(ret == 2){
lc_printf("NO Find Head!!\r\n");
}
else if(ret == 3){
lc_printf("NO Find end!!\r\n");
}
// RGB的B灯亮起,G灯熄灭
GPIO_writePin(RGB_B, 0);
GPIO_writePin(RGB_G, 1);
delay_ms(50);
// RGB的G灯亮起,B灯熄灭
GPIO_writePin(RGB_B, 1);
GPIO_writePin(RGB_G, 0);
delay_ms(50);
// RGB的B和G都熄灭
GPIO_writePin(RGB_B, 1);
GPIO_writePin(RGB_G, 1);
delay_ms(50);
}
}
__interrupt void INT_Debug_Serial_RX_ISR(void)
{
//清除接收中断标志位
SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
//清除中断标志位
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
上电效果:输出 K230
识别到的数据。
代码下载链接
【代码下载】
- 跳转到
下载中心
去下载CCS模块移植代码:【点击跳转🚀】
案例二:任意颜色线循迹
开发板代码
案例一:串口通信
CanMV IDE K230
代码
代码说明:实现任意颜色的寻线,需要知道线的颜色和位置。线的颜色识别通过色块查找的方式;而位置的判断,通过识别出线的颜色之后,在识别到的区域中心画一个十字型符号‘+’,并记录该色块中心位置命名为x_location。
如何识别线的颜色?通过寻找色块函数 find_blobs() 进行查找
将x_location与图像的X轴中心作比较,就可以得到线的位置Line_position。其中图像的X轴中心命名为centre
当 x_location > centre 时,Line_position设置为正数,x_location 越大,则Line_position 越大。
当 x_location < centre 时,Line_position设置为负数,x_location 越小,则Line_position 越小。
当 x_location = centre 时,Line_position设置为0。
如何实现循迹?
将 K230放 置在中间位置。
当Line_position 为负数,说明识别到线在图像的左侧。我们需要控制小车左转。
当Line_position 为正数,说明识别到线在图像的右侧。我们需要控制小车右转。
当Line_position 为0 , 说明识别到线在图像的中间。我们往前直行。
需要注意的是,线的颜色不可以和背景色相同,否则该案例失效。
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
import time, os, sys, math
import json
from media.sensor import *
from media.display import *
from media.media import *
from machine import UART
from machine import FPIOA
# 修改这里的颜色阈值可以做到任意颜色识别循迹
GRAYSCALE_THRESHOLD = (0, 42, 22, -41, 27, -23)
# 识别颜色的矩形区域
Identification_region = [0, 220, 640, 45]
# 色块的X轴位置
x_location = 0
# 图像的X轴中心
centre = 0
# 线的位置
Line_position = 0
# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
sensor_id = 2
sensor = None
# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
uart.write("Hello World!\r") # 通过串口2输出固定字符串 “Hello World!”
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
# sensor.set_vflip(False)
# 设置通道0的输出尺寸为640x480
sensor.set_framesize(Sensor.VGA, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 使用IDE的帧缓冲区作为显示输出
Display.init(Display.VIRT, width=640, height=480, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# 设置图像的X轴中心
centre = (img.width() / 2)
# 寻找 颜色阈值【GRAYSCALE_THRESHOLD】设置的颜色
blobs = img.find_blobs([GRAYSCALE_THRESHOLD], roi=Identification_region, merge=True)
# roi 是感兴趣区域的矩形元组(x,y,w,h)。如果指定[10,10,20,20],则在以(10,10)为起点的位置,以长宽都为20的大小查找色块
# 如果roi未指定,ROI即整个图像的图像矩形。 操作范围仅限于 roi 区域内的像素。
# merge 若为True,则合并所有没有被过滤掉的色块,这些色块的边界矩形互相交错重叠。
# 识别到色块
if blobs:
most_pixels = 0 # 最大色块的像素
largest_blob = 0 # 最大色块
for i in range(len(blobs)):
# 目标区域找到的颜色块(线段块)可能不止一个,找到最大的一个,作为本区域内的目标直线
if blobs[i].pixels() > most_pixels:
most_pixels = blobs[i].pixels() # 记录最大色块的像素
largest_blob = i # 记录最大色块
# 在查找到的最大色块处画矩形
img.draw_rectangle(blobs[largest_blob].rect(), thickness=5)
# 在最大色块的中心画一个十字
img.draw_cross(blobs[largest_blob].cx(), blobs[largest_blob].cy(), thickness=3)
# 记录中心点X轴位置
x_location = blobs[largest_blob].cx()
# 未识别到色块
else:
img.draw_string(2, 24, "!NONE!", color=(0, 0, 255), scale=2, mono_space=False)
x_location = centre
# 在图像的正中间画一个红色的十字,方便确定屏幕中间的位置
img.draw_cross(int(centre), int(img.height() / 2), color=(255, 0, 0), size=10)
# 在识别的区域内画蓝色矩形
img.draw_rectangle(Identification_region, color=(0, 0, 255), thickness=5)
# 计算色块距离屏幕中间的距离, (img.width() / 2)后,色块中心点在屏幕左边则是负值 色块中心点在屏幕右边则是正值
Line_position = "[%d]" % (x_location - centre)
# LCD显示图像,并在图像的(2,1)位置显示字符串output_str的内容,字符串颜色为蓝色,字体大小为6,字符间距不固定
img.draw_string(2, 1, "%s" % Line_position, color=(0, 0, 255), scale=6, mono_space=False)
# 通过串口2输出字符串
uart.write(Line_position + '\r\n')
# 小于-10输出左转
# 大于 10输出右转
# 在-10到10范围内则输出前进
num = x_location - centre
if -10 <= num <= 10:
print("前进")
elif num > 10:
print("右转")
elif num < -10:
print("左转")
# 输出
print(Line_position)
# 显示捕获的图像
Display.show_image(img)
except KeyboardInterrupt as e:
print("用户停止: ", e)
except BaseException as e:
print(f"异常: {e}")
finally:
# 停止传感器运行
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示模块
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
案例验证
案例三:矩形识别与中心判断
开发板代码
案例一:串口通信
CanMV IDE K230
代码
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
import time, os, sys, math
import json
from media.sensor import *
from media.display import *
from media.media import *
from machine import UART
from machine import FPIOA
# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
sensor_id = 2
sensor = None
# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
uart.write("Hello World!\r") # 通过串口2输出固定字符串 “Hello World!”
# 查找最大矩形块函数
def find_max(rects):
rect_max=0
# 把blobs每个元素代入变量rect
for rect in rects:
# 如果当前矩形的宽 跟之前比 是最大的
if rect.w() > rect_max:
global max_rect
# 记录这个最多大的矩形
max_rect = rect
# 更新最大矩形宽度
rect_max = rect.w()
#返回这个最大矩形元素
return max_rect
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
# sensor.set_vflip(False)
# 设置通道0的输出尺寸为128x160
sensor.set_framesize(Sensor.QQVGA2, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 使用IDE的帧缓冲区作为显示输出
Display.init(Display.VIRT, width=128, height=160, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# 查找到最大矩形块,查找范围x=0,y=20,w=128,h=120
rects = find_max(img.find_rects(roi=(0,20,128,120)))
# 将查找到的最大矩形画一个绿色的矩形框
img.draw_rectangle(rects.x(),rects.y(),rects.w(),rects.h(),color=(0,255,0))
# 计算矩形中心
#中心X定位:矩形像素宽度 / 2 + x轴起点位置
#中心Y定位:矩形像素高度 / 2 + y轴起点位置
x_central = (rects.w() / 2) + rects.x()
y_central = (rects.h() / 2) + rects.y()
#显示识别到的最大矩形中心的X,Y位置
img.draw_string(2,1,"x=%d y=%d"%(x_central,y_central))
#在查找到的矩形中心画一个蓝色的十字
img.draw_cross(int(x_central), int(y_central), color = (0, 0, 255), size = 5 )
#在查找区域画一个红色方框
#区域越小,帧数越高
img.draw_rectangle(0,20,128,120,color=(255,0,0))
# 显示捕获的图像
Display.show_image(img)
except KeyboardInterrupt as e:
print("用户停止: ", e)
except BaseException as e:
print(f"异常: {e}")
finally:
# 停止传感器运行
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示模块
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
案例验证
案例四:任意角度矩形识别与边角定位
开发板代码
案例一:串口通信
CanMV IDE K230
代码
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
import time, os, sys, math
import json
from media.sensor import *
from media.display import *
from media.media import *
from machine import UART
from machine import FPIOA
# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
sensor_id = 2
sensor = None
# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
uart.write("Hello World!\r") # 通过串口2输出固定字符串 “Hello World!”
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
# sensor.set_vflip(False)
# 设置通道0的输出尺寸为128x160
sensor.set_framesize(Sensor.QVGA, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 使用IDE的帧缓冲区作为显示输出
Display.init(Display.VIRT, width=320, height=240, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# 在图像中寻找矩形
for r in img.find_rects(threshold = 10000):
# 判断矩形边长是否符合要求
if r.w() > 20 and r.h() > 20:
# 在屏幕上框出矩形
img.draw_rectangle(r.rect(), color = (255, 0, 0), scale = 4)
# 获取矩形角点位置
corner = r.corners()
# 在屏幕上圈出矩形角点
img.draw_circle(corner[0][0], corner[0][1], 5, color = (255, 0, 0), thickness = 2, fill = False)
img.draw_circle(corner[1][0], corner[1][1], 5, color = (0, 255, 0), thickness = 2, fill = False)
img.draw_circle(corner[2][0], corner[2][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
img.draw_circle(corner[3][0], corner[3][1], 5, color = (255, 255, 0), thickness = 2, fill = False)
# 打印四个角点坐标, 角点1的数组是corner[0], 坐标就是(corner[0][0],corner[0][1])
# 角点检测输出的角点排序每次不一定一致,矩形左上的角点有可能是corner0,1,2,3其中一个
left_down_dot ="LD=[%d,%d]"%(corner[0][0],corner[0][1]) #红色点
right_down_dot ="RD=[%d,%d]"%(corner[1][0],corner[1][1]) #绿色点
right_up_dot ="RU=[%d,%d]"%(corner[2][0],corner[2][1]) #蓝色点
left_up_dot ="LU=[%d,%d]"%(corner[3][0],corner[3][1]) #黄色点
print(left_up_dot + "\n" + left_down_dot + "\n" + right_up_dot + "\n" + right_down_dot)
# 显示捕获的图像
Display.show_image(img)
except KeyboardInterrupt as e:
print("用户停止: ", e)
except BaseException as e:
print(f"异常: {e}")
finally:
# 停止传感器运行
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示模块
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
案例验证
案例五:激光识别与定位
将识别到的绿色激光以黑色方框圈出,并在IDE中输出激光XY轴位置。
开发板代码
案例一:串口通信
CanMV IDE K230
代码
# 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源
# 开发板官网:www.lckfb.com
# 技术支持常驻论坛,任何技术问题欢迎随时交流学习
# 立创论坛:www.jlc-bbs.com/lckfb
# 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
# 不靠卖板赚钱,以培养中国工程师为己任
import time, os, sys, math
import json
from media.sensor import *
from media.display import *
from media.media import *
from machine import UART
from machine import FPIOA
# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
sensor_id = 2
sensor = None
# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
uart.write("Hello World!\r") # 通过串口2输出固定字符串 “Hello World!”
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
# sensor.set_vflip(False)
# 设置通道0的输出尺寸为128x160
sensor.set_framesize(Sensor.QVGA, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 使用IDE的帧缓冲区作为显示输出
Display.init(Display.VIRT, width=320, height=240, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# -----跟踪激光部分-----
# 设置绿色激光颜色阈值
G_td = [(98, 81, -48, 41, 14, 72)]
# 根据阈值找到色块
for b in img.find_blobs(G_td,pixels_threshold=2, area_threshold=15, merge=True,invert = 0):
# 在识别到的激光处画一个黑色矩形
img.draw_rectangle(b.rect(), color = (0, 0, 0), scale = 1, thickness = 2)
# 打印激光色块的中心位置
# 使用b.x()获取色块矩形左上角X坐标
# 使用b.y()获取色块矩形左上角Y坐标
# 使用b.w()获取色块矩形宽度
# 使用b.h()获取色块矩形高度
# 矩形中心坐标为(x + w/2,y + h/2)
print("[%d,%d]"%( (b.x() + b.w()/2), (b.y() + b.h()/2 ) ))
break
# 显示捕获的图像
Display.show_image(img)
except KeyboardInterrupt as e:
print("用户停止: ", e)
except BaseException as e:
print(f"异常: {e}")
finally:
# 停止传感器运行
if isinstance(sensor, Sensor):
sensor.stop()
# 反初始化显示模块
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
案例验证
注意
如识别不出绿色,请按照颜色阈值的设置章节,配置你的绿色阈值。