浙江省机器人比赛-K230 视觉方案

浙江省机器人比赛-K230 视觉方案

撰写人:Keruone
撰写日期 2025.5.26

由于总结分析部分中 k230的篇幅占的有点太大了,所以将视觉方案单独拿出来写。
可以阅读此处下载html


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);

    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇