浙江省机器人比赛-K230 视觉方案
撰写人:Keruone
撰写日期 2025.5.26
由于总结分析部分中 k230的篇幅占的有点太大了,所以将视觉方案单独拿出来写。
可以阅读此处下载html
Contents
1. k230 视觉方案
视觉好啊,可惜我们探索的不算太多。
注意,如果你使用默认的摄像头,你会发现视野非常狭隘,所以我们使用的是160度的广角镜头。
1.1 视觉方案介绍
在本项目中,我们视觉主要是有以下几个目标:
1. 可以摆脱循迹的限制,自由移动
2. 可以识别圆柱,防止机械臂防止圆环时存在偏差,导致防止失败
3. 可以通过视觉的方案,实现抓取对方得分区的圆环和物块
为了轻便快速,我们最终决定使用yolov11作为解决方案。毕竟比较与SAM等视觉方案,yolo实在快的不得了。
1.2 k230 代码实现和分析
1.2.1 k230 摄像头配置
由于它的对于单个摄像头的案例还能用,就参考这里吧。不过这里只是单纯的使用摄像头。
摄像头与yolo(或者其它AI方案)的结合可以使用pipeline来搭建,可以参考这里。
但是这有一个问题,它以上的代码针对的都是单摄像头,对于多摄像头的配置并没有给出。如果你直接使用两次pipeline,那就完了。这里我觉得是这板子做的最拉跨的地方。
具体的多摄像头配置可以参考后文视觉代码
不过需要注意!使用了多摄像头后,延迟会大大增加!本来单摄像头一轮的间隔大约为50ms,现在一轮大约为200ms!
1.2.2 k230 yolov11配置
数据很重要,但是非常可惜,我们只能自己拍摄数据集。所以先分享我们获取图片的代码
# 获取图片的代码
import time, os, sys
from machine import Pin
from machine import FPIOA
from media.sensor import *
from media.display import *
from media.media import *
sensor_id = 1
sensor = None
# 创建FPIOA对象,用于初始化引脚功能配置
fpioa = FPIOA()
fpioa.set_function(53,FPIOA.GPIO53)
# 按键引脚为53,按下时为高电平,所以这里设置为下拉并设置为输入模式
button = Pin(53, Pin.IN, Pin.PULL_DOWN) # 使用下拉电阻
# 显示模式选择:可以是 "VIRT"、"LCD" 或 "HDMI"
DISPLAY_MODE = "LCD"
# 根据模式设置显示宽高
if DISPLAY_MODE == "VIRT":
# 虚拟显示器模式
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
# 3.1寸屏幕模式
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 480
elif DISPLAY_MODE == "HDMI":
# HDMI扩展板模式
DISPLAY_WIDTH = 1920
DISPLAY_HEIGHT = 1080
else:
raise ValueError("未知的 DISPLAY_MODE,请选择 'VIRT', 'LCD' 或 'HDMI'")
# 定义保存目录
save_dir = "./sdcard/saved_photos"
try:
# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像翻转
# 设置水平镜像
# sensor.set_hmirror(False)
# 设置垂直翻转
sensor.set_vflip(False)
# 设置通道0的输出尺寸为显示分辨率
sensor.set_framesize(width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
sensor.set_framesize(width=1920, height=1080, chn=CAM_CHN_ID_1)
# 设置通道0的输出像素格式为RGB565
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)
# 根据模式初始化显示器
if DISPLAY_MODE == "VIRT":
Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
elif DISPLAY_MODE == "LCD":
Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
elif DISPLAY_MODE == "HDMI":
Display.init(Display.LT9611, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
try:
files = os.listdir(save_dir)
photo_files = [f for f in files if f.startswith("photo") and f.endswith(".jpg")]
max_idx = -1
for f in photo_files:
try:
idx = int(f.replace("photo", "").replace(".jpg", ""))
if idx > max_idx:
max_idx = idx
except:
pass
i = max_idx + 1
print(f"检测到{len(photo_files)}张照片,将从编号{i}开始保存")
except:
i = 0
while True:
os.exitpoint()
# 捕获通道0的图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
img1 = sensor.snapshot(chn=CAM_CHN_ID_1)
# 显示捕获的图像,中心对齐,居中显示
Display.show_image(img)
#按键按下保存图片
if button.value() == 1:
time.sleep(0.02)
if button.value() == 1:
try:
save_path = f"./sdcard/saved_photos/photo{i}.jpg"
img1.save(save_path)
print(f"保存到当前目录成功: {save_path}")
except Exception as e:
print(f"保存到当前目录失败: {e}")
time.sleep(0.1)
i += 1
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()
此外,为了方便图片的处理,我们还编写了如下几段小代码
yolo训练的yaml文件
train: C:\\Users\\G\\Desktop\\robot-git\\yoloRobot\\data\\images\\train
val: C:\\Users\\G\\Desktop\\robot-git\\yoloRobot\\data\\images\\val
test: C:\\Users\\G\\Desktop\\robot-git\\yoloRobot\\data\\images\\test
nc: 9
# Classes
names:
0: platform
1: column
2: block
3: red_score
4: blue_score
5: red_circle
6: blue_circle
7: platform_red
8: platform_blue
0 图片批量添加前后缀
import os
import datetime
# ====== 在这里修改配置 ======
FOLDER_PATH = "train_photo0523_2" # 要处理的文件夹路径
ADD_PREFIX = "0523" # 要添加的前缀,留空则不添加
ADD_SUFFIX = "" # 要在扩展名前添加的后缀,留空则不添加
ADD_DATE = False # 是否添加日期
DATE_FORMAT = "%Y%m%d" # 日期格式
DATE_POSITION = "prefix" # 日期位置:"prefix"添加到开头,"suffix"添加到扩展名前
# ===========================
def batch_rename_files():
"""批量修改文件名"""
# 获取当前日期
date_str = datetime.datetime.now().strftime(DATE_FORMAT) if ADD_DATE else ''
# 遍历文件夹
for filename in os.listdir(FOLDER_PATH):
# 获取文件完整路径
old_path = os.path.join(FOLDER_PATH, filename)
# 跳过目录
if os.path.isdir(old_path):
continue
# 分离文件名和扩展名
name, ext = os.path.splitext(filename)
# 构建新文件名
if DATE_POSITION == 'prefix':
new_name = f"{ADD_PREFIX}{date_str}{name}{ADD_SUFFIX}{ext}"
else: # suffix
new_name = f"{ADD_PREFIX}{name}{ADD_SUFFIX}{date_str}{ext}"
# 新文件完整路径
new_path = os.path.join(FOLDER_PATH, new_name)
# 重命名文件
try:
os.rename(old_path, new_path)
print(f"Renamed: {filename} -> {new_name}")
except Exception as e:
print(f"Error renaming {filename}: {e}")
if __name__ == '__main__':
# 确认配置
print("=== 文件重命名配置 ===")
print(f"处理文件夹: {FOLDER_PATH}")
print(f"添加前缀: '{ADD_PREFIX}'")
print(f"添加后缀: '{ADD_SUFFIX}'")
print(f"添加日期: {ADD_DATE} ({DATE_FORMAT})")
print(f"日期位置: {DATE_POSITION}")
print("====================")
input("按Enter键开始重命名,或Ctrl+C取消...")
# 调用函数
batch_rename_files()
print("文件重命名完成!")
1 自己撰写的图片增强(可以不用)(当然你也可以使用Albumentations等现成的包)
2.yolo训练代码
import os
from ultralytics import YOLO
# 切换当前工作目录到changed_path
current_path = "C:\\Users\\G\\Desktop\\robot-git\\yoloRobot"
imgSavePath = "/ImgDetectResult"
os.chdir(current_path)
# Load a pretrained YOLO11n model
model = YOLO("yolov11\weights\yolo11s.pt")
train_results = model.train(
data="data.yaml", # Path to dataset configuration file
epochs=100, # Number of training epochs
imgsz=224, # Image size for training
device='cpu', # Device to run on (e.g., 'cpu', 0, [0,1,2,3])
)
# 如果你要用GPU,那就网上自己了解吧!
3. yolo训练模型转换为k230的kmodel代码,及错误分析
# 将 pt 模型转换为 kmodel 模型
# 参考https://developer.canaan-creative.com/k230_canmv/zh/main/zh/example/ai/YOLO%E5%A4%A7%E4%BD%9C%E6%88%98.html#yolo11
# 按照如下命令,对 runs/classify/exp/weights 下的 pt 模型先导出为 onnx 模型,再转换成 kmodel 模型:
# # 导出onnx,pt模型路径请自行选择
# yolo export model=runs/classify/train/weights/best.pt format=onnx imgsz=224
# cd test_yolo11/classify
# # 转换kmodel,onnx模型路径请自行选择,生成的kmodel在onnx模型同级目录下
# python to_kmodel.py --target k230 --model ../../runs/classify/train/weights/best.onnx --dataset ../test --input_width 224 --input_height 224 --ptq_option 0
# cd ../../
# """
# # Error list 1:
# Traceback (most recent call last):
# File "C:\Users\G\Desktop\robot-git\yoloRobot\test_yolo11\classify\to_kmodel.py", line 7, in <module>
# import nncase
# File "C:\Users\G\.conda\envs\yoloRobot\lib\site-packages\nncase\__init__.py", line 34, in <module>
# import _nncase
# ImportError: DLL load failed while importing _nncase: 找不到指定的模块。
# 解决方法: https://github.com/kendryte/nncase/issues/451
# 我下载了 相关 dll 文件,并 Place 64-bit DLLs in the '/Windows/System32' directory.
# """
# """
# # Error list 2:
# warn: Nncase.Hosting.PluginLoader[0]
# NNCASE_PLUGIN_PATH is not set.
# C:\Users\G\Desktop\robot-git\yoloRobot\test_yolo11\classify\to_kmodel.py:24: DeprecationWarning: `mapping.TENSOR_TYPE_TO_NP_TYPE` is now deprecated and will be removed in a future release.To silence this warning, please use `helper.tensor_dtype_to_np_dtype` instead.
# input_dict['dtype'] = onnx.mapping.TENSOR_TYPE_TO_NP_TYPE[onnx_type.elem_type]
# WARNING: The argument `input_shapes` is deprecated. Please use `overwrite_input_shapes` and/or `test_input_shapes` instead. An
# error will be raised in the future.
# Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'k230' was not present in the dictionary.
# at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
# at Nncase.Targets.TargetProvider.GetTarget(String name)
# at Nncase.CompilerServicesProvider.GetTarget(String name)
# at Nncase.Compiler.Interop.CApi.TargetCreate(Byte* targetNamePtr, UIntPtr targetNameLength)
# # 解决方法:
# 1. 解决NNCASE_PLUGIN_PATH is not set.
# 安装<2.0.0版本的nncase
# 参考:https://blog.csdn.net/Lieber_0402/article/details/132021911
# 2. 解决WARNING: The argument `input_shapes` is deprecated. Please use `overwrite_input_shapes` and/or `test_input_shapes` instead. An error will be raised in the future.
# onnx-simplifier改成0.3.6.(其实我本来似乎都没有这个包)
# 参考:https://github.com/kendryte/nncase/issues/909
# * 最终解决方法:****************************************************************************
# 1. 问题产生原因:nncase_kpu-2.9.0-py2.py3-none-win_amd64.whl 误装为 nncase-2.9.0-cp310-cp310-win_amd64.whl
# 2. 卸载nncase-2.9.0-cp310-cp310-win_amd64.whl
# 3. 安装nncase_kpu-2.9.0-py2.py3-none-win_amd64.whl
# """
import os
import shutil
working_path = "C:\\Users\\G\\Desktop\\robot-git"
pt_model_path = "yoloRobot/runs/detect/train20/weights/best.pt"
tempConvertPath = "yoloRobot/tempForConvert"
test_yolo11_path = "yoloRobot/test_yolo11"
dataset_path = "yoloRobot/data/images/train"
dataset_path = os.path.join(working_path, dataset_path)
os.chdir(working_path)
if __name__ == "__main__":
## Step 1: Copy the pt model to the tempConvertPath
command1 = f"yolo export model={pt_model_path} format=onnx imgsz=224" # 注意,该命令生成的位置同pt模型同级目录下
os.system(command1) # 导出onnx模型
# 生成的onnx模型路径
onnx_model_path = os.path.join(working_path, pt_model_path.replace(".pt", ".onnx"))
# ## Step 2: Change directory to test_yolo11/classify
# # 切换到 test_yolo11/classify 目录
# classify_path = os.path.join(test_yolo11_path, "classify")
# os.chdir(classify_path)
## Step 2: Change directory to test_yolo11/detect
# 切换到 test_yolo11/detect 目录
classify_path = os.path.join(test_yolo11_path, "detect")
os.chdir(classify_path)
## Step 3: Convert the onnx model to kmodel
command3 = f"python to_kmodel.py --target k230 --model {onnx_model_path} --dataset {dataset_path} --input_width 224 --input_height 224 --ptq_option 0"
os.system(command3) # 转换kmodel模型
"""
虽然会有一些警告信息,但最终还是能成功转换为kmodel模型的。
注意:转换后的kmodel模型在onnx模型同级目录下,命名为 best.kmodel。
"""
1.2.3 k230 串口配置
我们是打算使用串口,来将最终的识别结果传输给单片机的,多以串口是必不可少的
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
#---------------- 数据发送配置 ----------------
frame_header = 0xAA
uartDataRead = None
yoloTarget = 10
# 发送数据函数
import ustruct
def k230_yolo_send_data(single_yolo_data, uart, Sensor_Num):
"""本函数专门根据当前代码的yolodata格式发送数据,
发送格式为:帧头+数据个数+数据+校验和
发送 [center_x, center_y, bbx_width, bbx_height, 分类号]
置信度小于0.5的数据不发送
args:
single_yolo_data: yolodata数据[center_x, center_y, bbx_width, bbx_height, 置信度, 分类号]
uart: UART对象,用于发送数据
"""
# 帧头使用 frame_header
# 将坐标转换为实际像素值,并确保在u16范围内(0-65535)
center_x = int(single_yolo_data[0]) & 0xFFFF # 16位无符号整数
center_y = int(single_yolo_data[1]) & 0xFFFF # 16位无符号整数
bbx_width = int(single_yolo_data[2]) & 0xFFFF # 16位无符号整数
bbx_height = int(single_yolo_data[3]) & 0xFFFF # 16位无符号整数
# 不发送置信度
class_id = int(single_yolo_data[5]) & 0xFF # 8位无符号整数
Sensor_Num = int(Sensor_Num) & 0xFF # 8位无符号整数
# 置信度小于0.5的数据不发送
if single_yolo_data[4] < 0.5:
return
data_num = 10
# 计算校验和
checksum = (frame_header +
(data_num & 0xFF) +
((center_x >> 8) & 0xFF) + (center_x & 0xFF) +
((center_y >> 8) & 0xFF) + (center_y & 0xFF) +
((bbx_width >> 8) & 0xFF) + (bbx_width & 0xFF) +
((bbx_height >> 8) & 0xFF) + (bbx_height & 0xFF) +
class_id + Sensor_Num) & 0xFF
# 创建数据包
data = bytearray()
# data.append(0x00)
data.append(frame_header) # 帧头
data.append(data_num) # 数据个数
# 使用ustruct打包u16数据
data.extend(ustruct.pack('<B', (center_x>>8 & 0XFF))) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', center_x & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (center_y>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', center_y & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (bbx_width>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', bbx_width & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (bbx_height>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', bbx_height & 0XFF)) # 小端序16位无符号整数
data.append(class_id) # 分类号(8位)
data.append(Sensor_Num) # 分类号(8位)
# 添加校验和
data.append(checksum)
# 发送数据
uart.write(data)
#----------------------------------------------
但是只有这样还不够,由于一次只能发送一个对象,所以我们需要一个途径,能够让接收方自动选择需要什么样的数据。
因此,我们在代码中,添加一个接收的部分。单片机可以通过串口向k230发送信息,索要指定对象的数据。
uartDataRead = uart.read()
if uartDataRead: # 检查数据是否非空
try:
# 确保第一个字符是数字,并转换为整数
# uartDataRead = int(uartDataRead.decode('ascii')[0])
uartDataRead = uartDataRead[0] # 获取第一个字节的整数值
print("Received: {}".format(uartDataRead))
yoloTarget = uartDataRead
except (IndexError, ValueError) as e:
# 处理可能的错误:
# - IndexError: uartDataRead 是空字符串(无法取 [0])
# - ValueError: 首字符不是数字(int() 转换失败)
print("Error: Invalid data received -", e)
yoloTarget = None # 或设置默认值
else:
print("Warning: No data received from UART")
# yoloTarget = None # 或设置默认值
print(f"YoloTargetNow:{yoloTarget}")
使用于小车的视觉代码如下:
import time
import os
import sys
from media.sensor import *
from media.display import *
from media.media import *
from libs.YOLO import YOLO11
import os,sys,gc
import image
import ulab.numpy as np
#----------------- 串口导入 -----------------
import time
from machine import UART
from machine import FPIOA
from machine import Pin
#----------------- 串口配置 -----------------
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
#data = b''
#---------------- kmodel 配置 ------------------
kmodel_path1 = "/data/yoloKmodel/best.kmodel"
kmodel_path2 = "/data/yoloKmodel/best2.kmodel"
kmodel_path3 = "/data/yoloKmodel/best3.kmodel"
kmodel_path4 = "/data/yoloKmodel/best0523.kmodel"
labels1 = ["floor", "pile", "column", "platform", "red_score", "blue_score", "red_start", "blue_start", "block", "red_circle", "blue_circle"]
labels2 = ["platform", "column", "block", "red_score", "blue_score", "red_circle", "blue_circle", "platform_red", "platform_blue"]
#----------------- LED配置 -----------------
fpioa.set_function(62,FPIOA.GPIO62)
LED = Pin(20, Pin.OUT, pull=Pin.PULL_NONE, drive=7) # 红灯62 蓝灯63 绿灯20
LED.high() # 关闭红灯
#---------------- 数据发送配置 ----------------
frame_header = 0xAA
uartDataRead = None
yoloTarget = 10
# 发送数据函数
import ustruct
def k230_yolo_send_data(single_yolo_data, uart, Sensor_Num):
"""本函数专门根据当前代码的yolodata格式发送数据,
发送格式为:帧头+数据个数+数据+校验和
发送 [center_x, center_y, bbx_width, bbx_height, 分类号]
置信度小于0.5的数据不发送
args:
single_yolo_data: yolodata数据[center_x, center_y, bbx_width, bbx_height, 置信度, 分类号]
uart: UART对象,用于发送数据
"""
# 帧头使用 frame_header
# 将坐标转换为实际像素值,并确保在u16范围内(0-65535)
center_x = int(single_yolo_data[0]) & 0xFFFF # 16位无符号整数
center_y = int(single_yolo_data[1]) & 0xFFFF # 16位无符号整数
bbx_width = int(single_yolo_data[2]) & 0xFFFF # 16位无符号整数
bbx_height = int(single_yolo_data[3]) & 0xFFFF # 16位无符号整数
# 不发送置信度
class_id = int(single_yolo_data[5]) & 0xFF # 8位无符号整数
Sensor_Num = int(Sensor_Num) & 0xFF # 8位无符号整数
# 置信度小于0.5的数据不发送
if single_yolo_data[4] < 0.5:
return
data_num = 10
# 计算校验和
checksum = (frame_header +
(data_num & 0xFF) +
((center_x >> 8) & 0xFF) + (center_x & 0xFF) +
((center_y >> 8) & 0xFF) + (center_y & 0xFF) +
((bbx_width >> 8) & 0xFF) + (bbx_width & 0xFF) +
((bbx_height >> 8) & 0xFF) + (bbx_height & 0xFF) +
class_id + Sensor_Num) & 0xFF
# 创建数据包
data = bytearray()
# data.append(0x00)
data.append(frame_header) # 帧头
data.append(data_num) # 数据个数
# 使用ustruct打包u16数据
data.extend(ustruct.pack('<B', (center_x>>8 & 0XFF))) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', center_x & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (center_y>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', center_y & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (bbx_width>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', bbx_width & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', (bbx_height>>8) & 0XFF)) # 小端序16位无符号整数
data.extend(ustruct.pack('<B', bbx_height & 0XFF)) # 小端序16位无符号整数
data.append(class_id) # 分类号(8位)
data.append(Sensor_Num) # 分类号(8位)
# 添加校验和
data.append(checksum)
# 发送数据
uart.write(data)
#----------------------------------------------
# sensor0 为爪子摄像头
sensor0 = None
sensor0_id = 0
sensor0_AI_chn = CAM_CHN_ID_2
# sensor1 为圆台摄像头
sensor1 = None
sensor1_id = 1
sensor1_AI_chn = CAM_CHN_ID_1
try:
print("camera_test")
# 构建 Sensor 对象 sensor0
sensor0 = Sensor(id=sensor0_id)
sensor0.reset()
sensor0.set_hmirror(1)
sensor0.set_vflip(1)
# 绑定通道 0 到显示 VIDEO1 层
sensor0.set_framesize(width=960, height=540)
sensor0.set_pixformat(PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# # 绑定通道 2 到AI
sensor0.set_framesize(width=1280, height=720, chn=sensor0_AI_chn)
sensor0.set_pixformat(PIXEL_FORMAT_RGB_888_PLANAR, chn=sensor0_AI_chn)
bind_info = sensor0.bind_info(x=0, y=0, chn=CAM_CHN_ID_0)
Display.bind_layer(**bind_info, layer=Display.LAYER_VIDEO1)
# 构建 Sensor 对象 sensor1
sensor1 = Sensor(id=sensor1_id)
sensor1.reset()
# 绑定通道 0 到显示 VIDEO1 层
sensor1.set_framesize(width=960, height=540)
sensor1.set_pixformat(PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# # 绑定通道 2 到AI
sensor1.set_framesize(width=1280, height=720, chn=sensor1_AI_chn)
sensor1.set_pixformat(PIXEL_FORMAT_RGB_888_PLANAR, chn=sensor1_AI_chn)
bind_info = sensor1.bind_info(x=960, y=0, chn=CAM_CHN_ID_0)
Display.bind_layer(**bind_info, layer=Display.LAYER_VIDEO2)
# 初始化 HDMI 和 IDE 输出显示,若屏幕无法点亮,请参考 API 文档中的 K230_CanMV_Display 模块 API 手册进行配置
Display.init(Display.LT9611, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
# 多摄场景仅需执行一次 run
sensor0.run()
# ------------------------ YOLO配置 -----------------------------
display_mode="lcd"
rgb888p_size=[1280,720]
if display_mode=="hdmi":
display_size=[1920,1080]
screen_width = 1920
screen_height = 1080
else:
display_size=[800,480]
screen_width = 800
screen_height = 480
kmodel_path=kmodel_path4 # path
labels = labels2
confidence_threshold = 0.8
model_input_size=[224,224]
yolo=YOLO11(task_type="detect",mode="video",kmodel_path=kmodel_path,labels=labels,rgb888p_size=rgb888p_size,model_input_size=model_input_size,display_size=display_size,conf_thresh=confidence_threshold,debug_mode=0)
yolo.config_preprocess()
if labels == labels2:
platform_num = 0
else:
platform_num = 3
rgb888p_img = None
img_data = None
# temptime = time.ticks_ms()
while True:
# ========================== 读取数据 ==========================
uartDataRead = uart.read()
if uartDataRead: # 检查数据是否非空
try:
# 确保第一个字符是数字,并转换为整数
# uartDataRead = int(uartDataRead.decode('ascii')[0])
uartDataRead = uartDataRead[0] # 获取第一个字节的整数值
print("Received: {}".format(uartDataRead))
yoloTarget = uartDataRead
except (IndexError, ValueError) as e:
# 处理可能的错误:
# - IndexError: uartDataRead 是空字符串(无法取 [0])
# - ValueError: 首字符不是数字(int() 转换失败)
print("Error: Invalid data received -", e)
yoloTarget = None # 或设置默认值
else:
print("Warning: No data received from UART")
# yoloTarget = None # 或设置默认值
print(f"YoloTargetNow:{yoloTarget}")
# ========================== 爪子摄像头 ==========================
LED.low() # 点亮当前选择的LED
rgb888p_img = sensor0.snapshot(chn=sensor0_AI_chn)
img_data = rgb888p_img.to_numpy_ref()
# print(f"sen1转换数据时的时间:{time.ticks_ms() - temptime}")
res = yolo.run(img_data)
# print(f"sen1 yolo 时间:{time.ticks_ms() - temptime}")
if len(res)!=0:
for sub_res in res:
if sub_res[5]==yoloTarget: # 此处为测试,实际使用应选择圆台,即编号3
k230_yolo_send_data(sub_res, uart, 0)
# print("0 Send Successful")
# print(f"sen1 yolo 间隔:{time.ticks_ms() - temptime}")
# temptime = time.ticks_ms()
# ========================== 循迹摄像头 ==========================
LED.high() # 熄灭当前选择的LED
rgb888p_img = sensor1.snapshot(chn=sensor1_AI_chn)
img_data = rgb888p_img.to_numpy_ref()
res = yolo.run(img_data)
if len(res)!=0:
for sub_res in res:
if sub_res[5]==platform_num: # 此处为测试,实际使用应选择圆台,即编号3
k230_yolo_send_data(sub_res, uart, 1)
# print("1 Send Successful")
# print(f"sen1 yolo 间隔:{time.ticks_ms() - temptime}")
# temptime = time.ticks_ms()
os.exitpoint()
# print(f"一轮的时间:{time.ticks_ms() - temptime}")
# temptime = time.ticks_ms()
# time.sleep(1)
except KeyboardInterrupt as e:
print("用户停止")
except BaseException as e:
print(f"异常: '{e}'")
finally:
# 每个 sensor 都需要执行 stop
if isinstance(sensor0, Sensor):
sensor0.stop()
if isinstance(sensor1, Sensor):
sensor1.stop()
# 销毁显示
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
# 释放媒体缓冲区
MediaManager.deinit()
大车同理。
1.3 单片机接收&处理 K230 代码
1.3.1 单片机串口接收数据
首先,我们先进行串口的配置,如下所示,比方我们要使用USART1:
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Receive_IT(&huart1,(uint8_t *)&uart1_rx,1);
// 启动串口接收中断,接收1个字节
// 注意:这里的uart1_rx是一个全局变量,用于存储接收到的数据
// 你需要在代码中定义这个变量,如:uint8_t uart1_rx = 0;
}
接着,我们先定义一些type,分别用来表示接收到的数据和处理后的数据
// 相关定义
// 用于接收
typedef struct
{
uint8_t RxDataPtr; // 接收数据指针
uint8_t RxData[DataNum]; // 接收到的数据
uint8_t ValidData[DataNum]; // 有效的数据
}k230_UART_Rx_t;
// 用于处理后的数据
typedef struct
{
uint16_t x_center;
uint16_t y_center;
uint16_t x_width;
uint16_t y_height;
uint8_t label_num;
uint8_t sensor_num;
uint16_t x_left;
uint16_t y_up;
uint16_t x_right;
uint16_t y_down;
}k230_Item_Type;
// 相关变量
k230_UART_Rx_t k230_1_Rx;
k230_UART_Rx_t k230_1_Rx_0;
k230_UART_Rx_t k230_1_Rx_1;
k230_UART_Rx_t* k230_1_Rx_x;
k230_Item_Type k230_Item_0; // 爪子
k230_Item_Type k230_Item_1; // 圆台
k230_Item_Type k230_Item_2; // 备用
k230_Item_Type k230_Item_3; // 备用
实际的接收代码如下
/**
* @brief: 一个字节中断接收,在 回调函数中调用
* @param {type}
* @retval: None
*/
void k230_GetChar(uint8_t pData, k230_UART_Rx_t* k230_UART_Rx) {
// 串口
if (k230_UART_Rx->RxDataPtr >= DataNum) {
k230_UART_Rx->RxDataPtr = 0;
return;
}
// 信息结构:帧头+数据长度+数据+校验和
if (k230_UART_Rx->RxDataPtr == 0 && pData == k230_FRAME_START) { // 帧头
// 如果是帧头
k230_UART_Rx->RxData[k230_UART_Rx->RxDataPtr++] = pData;
} else if (k230_UART_Rx->RxDataPtr == 1) {
// 如果是数据长度
k230_UART_Rx->RxData[k230_UART_Rx->RxDataPtr++] = pData; // num
} else if (k230_UART_Rx->RxDataPtr !=0 && k230_UART_Rx->RxDataPtr < ((k230_UART_Rx->RxData[1] & 0x0f) + 3)) {
// 如果是数据 ( 判断条件是数据长度+3,3是帧头+数据长度+校验和 )
k230_UART_Rx->RxData[k230_UART_Rx->RxDataPtr++] = pData;
// 如果是最后一个数据(即校验和)
if (k230_UART_Rx->RxDataPtr == ((k230_UART_Rx->RxData[1] & 0x0f) + 3)) { // 接收完成
// 校验
if (k230_check_SUM(k230_UART_Rx->RxData, k230_UART_Rx->RxDataPtr)) {
tickGetData = HAL_GetTick();
for (uint8_t i = 0; i < ((k230_UART_Rx->RxData[1] & 0x0f) + 3); i++){
k230_UART_Rx->ValidData[i] = k230_UART_Rx->RxData[i]; // 数据存储
// printf ("%d\n", k230_UART_Rx->ValidData[i]);
}
// New Added 2025.5.3
if(k230_UART_Rx->ValidData[11] == 0){
k230_1_Rx_0 = *k230_UART_Rx;
}
else{
k230_1_Rx_1 = *k230_UART_Rx;
}
}
else{
// printf("error\n");
}
k230_UART_Rx->RxDataPtr = 0;
}
} else
k230_UART_Rx->RxDataPtr = 0;
}
校验和计算如下:
/**
* @brief: SUM校验,传输校验
* @param {type}
* @retval: None
*/
uint8_t k230_check_SUM(uint8_t* pData, uint8_t length) {
uint16_t sum = 0;
for (uint8_t i = 0; i < length - 1; i++) {
sum += pData[i];
}
if ((uint8_t)sum == pData[length - 1])
return 1;
return 0;
}
其次,记得在hal的中断中调用相关函数,如
if (huart->Instance == USART1)
{
k230_GetChar(uart1_rx,&k230_1_Rx);
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart1_rx, 1);
}
接着,对得到的数据进行处理
/**
* @brief: 用于在程序中调用并处理的函数
*
*/
void k230_GetData(k230_Item_Type * k230_Item, uint8_t sensor_num) {
if (sensor_num == 0)
k230_1_Rx_x = &k230_1_Rx_0;
else
k230_1_Rx_x = &k230_1_Rx_1;
k230_Item->sensor_num = k230_1_Rx_x->ValidData[11];
if (HAL_GetTick() - tickGetData > NO_TRANSMIT_TIME){
k230_ClearData(k230_Item);
}
else{
k230_Item->x_left = (k230_1_Rx_x->ValidData[2]<<8) + k230_1_Rx_x->ValidData[3];
k230_Item->y_up = (k230_1_Rx_x->ValidData[4]<<8) + k230_1_Rx_x->ValidData[5];
k230_Item->x_right = (k230_1_Rx_x->ValidData[6]<<8) + k230_1_Rx_x->ValidData[7];
k230_Item->y_down = (k230_1_Rx_x->ValidData[8]<<8) + k230_1_Rx_x->ValidData[9];
k230_Item->label_num = k230_1_Rx_x->ValidData[10];
k230_Item->x_center = (k230_Item->x_left + k230_Item->x_right)/2;
k230_Item->y_center = (k230_Item->y_up + k230_Item->y_down)/2;
k230_Item->x_width = k230_Item->x_right - k230_Item->x_left;
k230_Item->y_height = k230_Item->y_down - k230_Item->y_up;
} //更改括号位置从下面移动到上面
}
至此,单片机获取数据的代码已经撰写完毕
1.3.2 单片机串口发送需要的数据
首先,我们先进行简单的宏定义,表面我们需要的yolo识别对象
#define LABEL0_PLATFORM 0
#define LABEL0_COLUMN 1
#define LABEL0_BLOCK 2
#define LABEL0_RED_SCORE 3
#define LABEL0_BLUE_SCORE 4
#define LABEL0_RED_CIRCLE 5
#define LABEL0_BLUE_CIRCLE 6
#define LABEL0_PLATFORM_RED 7
#define LABEL0_PLATFORM_BLUE 8
接着,我们需要一个函数,来发送数据给k230
HAL_StatusTypeDef k230_SendData(uint8_t number)
{
return HAL_UART_Transmit(&huart1, &number, 1, 100); // 100ms超时
}
1.3.3 一些基于k230传输数据的应用
根据这些数据,其实可以做的处理已经有很多了
实际,我们完成了小车根据左侧摄像头的对于圆台的识别结果,来自动旋转驾驶,即:当圆台图像太大,说明小车太近了,此时小车应该远离;当圆台图像太小,说明小车太远了,此时小车应该靠近。如果圆台靠左,说明小车的车头有点往外偏,应该矫正回来,反之同理。
如:
/**
* @brief: 根据视觉内容pid矫正
* @param {None}
* @retval: Speed Correct
*/
int32_t k230_PID(k230_Item_Type * k230_Item){
int32_t diff_x_cent, diff_y_cent, diff_width, diff_height;
int32_t speed_correct = 0; // outter plus this value, inner sub this value
if(k230_Item->x_left == 0 && k230_Item->x_right == 0)
return 0;
// postive mean too far down
// shoule inner increase, outter decrease
diff_x_cent = SCREEN_X_CENTER - k230_Item->x_center ;
// postive mean too close
// shoule inner increase, outter decrease
diff_width = PLATFORM_WIDTH - k230_Item->x_width ;
// printf("%d %d\n",x_center, x_width); // 调试接口
diff_cent_acc += diff_x_cent;
diff_width_acc += diff_width;
// 计算矫正值
speed_correct = k230_KP_CENT * diff_x_cent + \
k230_KI_CENT * diff_cent_acc + \
k230_KD_CENT * (diff_x_cent - diff_cent_last) ;
speed_correct += k230_KP_WIDTH * diff_width + \
k230_KI_WIDTH * diff_width_acc + \
k230_KD_WIDTH * (diff_width - diff_width_last) ;
// 更改上一次偏差量
diff_cent_last = diff_x_cent;
diff_width_last = diff_width;
return speed_correct;
}
/**
* @brief: 让小车转圈
* @param {int32_t S_Speed} 小车的速度
* @retval: None
*/
uint8_t BreakFlag = 0;
void Go_Circle(int32_t S_Speed)
{
int32_t PID_Correct;
int32_t inner_speed;
int32_t outter_speed;
uint32_t in_while_Time = 0;
k230_Item_Type *k230_Item_Ptr = &k230_Item_1;
while (1)
{
k230_GetData(k230_Item_Ptr, SENSOR_Platform);
if (k230_Item_Ptr->x_center == 0 && k230_Item_Ptr->x_width == 0)
{
// 如果没有传输数据
// 1. 先保持原有速度行进一段时间
Set_speed(500, 500, 500, 500);
OSTimeDly(1000);
// 2. 再次检测
k230_GetData(k230_Item_Ptr, SENSOR_Platform);
// 3. 如果仍然检测不到,则原地转圈直到检测到
if (k230_Item_Ptr->x_center == 0 && k230_Item_Ptr->x_width == 0)
{
Set_speed(500, -500, -500, 500);
in_while_Time = HAL_GetTick();
while (k230_Item_Ptr->x_center == 0 && k230_Item_Ptr->x_width == 0)
{
if (HAL_GetTick() - in_while_Time > 1500)
break;
k230_GetData(k230_Item_Ptr, SENSOR_Platform);
}
}
}
// else{
// 如果有数据传输
PID_Correct = k230_PID(k230_Item_Ptr);
inner_speed = (OUT_RATIO_IN * S_Speed - PID_Correct);
outter_speed = (S_Speed + PID_Correct);
// 左上 右上 右下 左下
Set_speed(inner_speed, outter_speed, outter_speed, inner_speed);
// }
OSTimeDly(1);
if(BreakFlag == 1)
break;
}
}
```
爪子跟随同理,以下是爪子跟随的代码
```c
float k230_Arm4_Column_Correct(k230_Item_Type * k230_Item){
// 如果没有数据,则退出
if (k230_Item->x_left == 0 && k230_Item->x_right == 0)
return 0;
printf("y_center: %d", (int)(k230_Item->y_center));
printf("y_height: %d", (int)(k230_Item->y_height));
return (float)(k230_Item->x_center - Column_Target_x_center) * Arm4_Cofficient; // 计算舵机转动的角度
}
void Arm_See() //机械臂调整角度,k230看
{
// 更改参数,原来参数见老版代码
// 观察机械臂横向位置
SetServoAngle(4,174); // 177
SetServoAngle(1,180);
while(ServoTunnerOK() != 1);
SetServoAngle(2,162); // 原来157
SetServoAngle(3,111); // 原来96
Correct_Arm4_With_K230(174,20); // 调整云台(舵机4)
// 中间态
SetServoAngle(3,120);
while(ServoTunnerOK() != 1);
SetServoAngle(2,148);
SetServoAngle(1,87); // 0522
while(ServoTunnerOK() != 1);
// OSTimeDly(500);
// 放置前奏
float temp1 = 107;
float temp2 = 175;
float temp3 = 96;
// SetServoAngle(1,121); // 103
// while(ServoTunnerOK() != 1);
SetServoAngle(2,170); // 175 173
while(ServoTunnerOK() != 1);
SetServoAngle(3,82); // 96 84
while(ServoTunnerOK() != 1);
SetServoAngle(1,110);
while(ServoTunnerOK() != 1);
// OSTimeDly(500);
// for(uint8_t i = 0;i < 20; i ++){
// temp1--;
// temp3--;
// SetServoAngle(1,temp1);
// SetServoAngle(3,temp3);
// }
// 等待稳定,放置圆环
OSTimeDly(200);
SetServoAngle(0,97);
OSTimeDly(500);
}
这是两者的演示效果


除此之外,还有跟随圆台颜色识别代码:
typedef enum{
BLUE_TEAM = 0,
RED_TEAM
}TEAM_COLOR_TYPE;
typedef enum{
IS_OUR_COLOR = 0, // 是我方的,则执行放置
NOT_OUR_COLOR, // 不是我方,则移动
NOT_DETECT_COLOR // 不确定,八成摄像头坏了,那建议先放置
} IS_OURCOLOR_TYPE;
extern TEAM_COLOR_TYPE OUR_COLOR;
IS_OURCOLOR_TYPE ArmCheckPlatformColor(uint8_t maxDetectTimes,uint8_t toward){
uint8_t i = 0;
IS_OURCOLOR_TYPE colorType;
uint8_t isOurColor_Count = 0;
uint8_t notOurColor_Count = 0;
uint8_t notDetectColor_Count = 0;
k230_Item_Type *k230_Item_Ptr_Our = &k230_Item_0;
k230_Item_Type *k230_Item_Ptr_Other = &k230_Item_2;
k230_Item_Type *k230_Item_Ptr_Recycle = &k230_Item_3;
// 发送检测对象
if(OUR_COLOR == BLUE_TEAM)
k230_SendData(LABEL0_PLATFORM_BLUE);
else
k230_SendData(LABEL0_PLATFORM_RED); // 发送请求,需要圆柱的坐标
OSTimeDly(200);
// 比对
for( i = 0; i < maxDetectTimes; i++){
if(OUR_COLOR == BLUE_TEAM)
k230_SendData(LABEL0_PLATFORM_BLUE);
else
k230_SendData(LABEL0_PLATFORM_RED); // 发送请求,需要圆柱的坐标
OSTimeDly(200);
k230_GetDataWithClear(k230_Item_Ptr_Recycle, SENSOR_Gripper); // 清除可能是前一个请求的返回结果(货不对板)
OSTimeDly(300);
k230_GetDataWithClear(k230_Item_Ptr_Our, SENSOR_Gripper);
printf("A->OurColor:\n");
printf("x_left: %d, x_right: %d\n",(int)k230_Item_Ptr_Our->x_left,(int)k230_Item_Ptr_Our->x_right);
printf("y_up: %d, y_down: %d\n",(int)k230_Item_Ptr_Our->y_up,(int)k230_Item_Ptr_Our->y_down);
printf("x_center: %d, x_width: %d\n",(int)k230_Item_Ptr_Our->x_center,(int)k230_Item_Ptr_Our->x_width);
printf("y_center: %d, y_height: %d\n",(int)k230_Item_Ptr_Our->y_center,(int)k230_Item_Ptr_Our->y_height);
OSTimeDly(10);
if(OUR_COLOR == BLUE_TEAM)
k230_SendData(LABEL0_PLATFORM_RED);
else
k230_SendData(LABEL0_PLATFORM_BLUE); // 发送请求,需要圆柱的坐标
OSTimeDly(200);
k230_GetDataWithClear(k230_Item_Ptr_Recycle, SENSOR_Gripper); // 清除可能是前一个请求的返回结果(货不对板)
OSTimeDly(300);
k230_GetDataWithClear(k230_Item_Ptr_Other, SENSOR_Gripper);
printf("B->OtherColor:\n");
printf("x_left: %d, x_right: %d\n",(int)k230_Item_Ptr_Other->x_left,(int)k230_Item_Ptr_Other->x_right);
printf("y_up: %d, y_down: %d\n",(int)k230_Item_Ptr_Other->y_up,(int)k230_Item_Ptr_Other->y_down);
printf("x_center: %d, x_width: %d\n",(int)k230_Item_Ptr_Other->x_center,(int)k230_Item_Ptr_Other->x_width);
printf("y_center: %d, y_height: %d\n",(int)k230_Item_Ptr_Other->y_center,(int)k230_Item_Ptr_Other->y_height);
OSTimeDly(10);
if(toward == platformInRight)
colorType = CheckPlatformColor_Right(k230_Item_Ptr_Our,k230_Item_Ptr_Other);
else
colorType = CheckPlatformColor_Left(k230_Item_Ptr_Our,k230_Item_Ptr_Other);
if(colorType == IS_OUR_COLOR)
isOurColor_Count++;
else if(colorType == NOT_OUR_COLOR)
notOurColor_Count++;
else if(colorType == NOT_DETECT_COLOR)
notDetectColor_Count++;
}
if(isOurColor_Count > notOurColor_Count && isOurColor_Count > notDetectColor_Count){
printf("colorType:IS_OUR_COLOR\n\n");
return IS_OUR_COLOR;
}
if(notOurColor_Count > isOurColor_Count && notOurColor_Count > notDetectColor_Count){
printf("colorType:NOT_OUR_COLOR\n\n");
return NOT_OUR_COLOR;
}
if(notDetectColor_Count > isOurColor_Count && notDetectColor_Count > notOurColor_Count){
printf("colorType:NOT_DETECT_COLOR\n\n");
return NOT_DETECT_COLOR;
}
return colorType;
}
最后,还有环游过程中检测是否是对方得分区,并判断有无五块的代码,不过比较可惜的是代码目前还有一定问题,以后有空再修正吧
void CircleTaskSensor0(void *p_arg){
OSTimeDly(20);
while(1){
if(CIRCLE_FLAG == 1){
Go_Circle(500);
}
else{
BreakFlag = 1;
}
OSTimeDly(1);
}
}
// k230检测是否是敌方得分区
void CheckOtherScoreRegionInCenter(void){
uint8_t i = 0;
uint8_t j = 0;
uint8_t NoneItem_Num = 0;
int32_t allSpinDelayTime = 0;
int32_t allForwardDelayTime = 0;
int32_t tempDelayTime = 0;
k230_Item_Type *k230_Item_Ptr = &k230_Item_0;
// 寻找敌方得分区位置
if(OUR_COLOR == BLUE_TEAM){
k230_SendData(LABEL0_RED_SCORE);
}
else if(OUR_COLOR == RED_TEAM){
k230_SendData(LABEL0_BLUE_SCORE);
}
// 判断是否是在中心偏差一定范围内部
k230_GetData(k230_Item_Ptr, SENSOR_Gripper);
if(k230_Item_Ptr->x_center > 573 && k230_Item_Ptr->x_center < 706){ //判断是否是敌方得分区
// 关闭绕圈走
CIRCLE_FLAG = 0;
Set_speed(0,0,0,0);
// 开始检测物块
k230_SendData(LABEL0_BLOCK);
OSTimeDly(100);
// 防止读取到没用的信息
k230_GetDataWithClear(k230_Item_Ptr, SENSOR_Gripper);
OSTimeDly(100);
// 旋转
spin_right(H_SPEED);
OSTimeDly(100);
while(j++<5 && k230_Item_Ptr->x_center!=0){
// 放置机械臂
k230_SendData(LABEL0_BLOCK);
Arm_Grab_rectangle();
OSTimeDly(1000);
// 开始检测物块
k230_SendData(LABEL0_BLOCK);
OSTimeDly(100);
// 防止读取到没用的信息
k230_GetDataWithClear(k230_Item_Ptr, SENSOR_Gripper);
OSTimeDly(100);
// ***可以考虑在此添加提前退出***
// 使得车头对准物块
for(i = 0; i < 10; i++){
// 读取方块位置
k230_GetDataWithClear(k230_Item_Ptr, SENSOR_Gripper);
tempDelayTime=k230_CarSpin_Block_Correct(k230_Item_Ptr);
printf("1:tDT:%d\n", tempDelayTime);
Circle_ClockType_with_Delay(100,tempDelayTime);
allSpinDelayTime += tempDelayTime;
OSTimeDly(tempDelayTime);
OSTimeDly(100);
}
Set_speed(0,0,0,0);
// 移动到物块
for(i = 0; i < 10; i++){
// 读取方块位置
k230_GetDataWithClear(k230_Item_Ptr, SENSOR_Gripper);
tempDelayTime=k230_CarForward_Block_Correct(k230_Item_Ptr);
printf("2:tDT:%d\n", tempDelayTime);
Circle_Goxx_with_Delay(200,tempDelayTime);
allForwardDelayTime += tempDelayTime;
OSTimeDly(tempDelayTime);
OSTimeDly(100);
}
Set_speed(0,0,0,0);
// 存储物块
Arm_Store_Rectangle();
// 大概恢复初始位置
Circle_Goxx_with_Delay(200,-allForwardDelayTime);
Circle_ClockType_with_Delay(100,-allSpinDelayTime);
Set_speed(0,0,0,0);
}
spin_left(H_SPEED);
CIRCLE_FLAG = 1;
OSTimeDly(1000);
}
OSTimeDly(10);
}
void CircleTaskSensor1(void *p_arg){
OSTimeDly(20);
while(1){
if(CIRCLE_FLAG == 1){
Arm_Set_Circle_See_Side();
CheckOtherScoreRegionInCenter();
}
OSTimeDly(1);
}
}