保姆级教程:用K210和STM32F103C8T6实现串口收发图像坐标(附完整代码)
2026/6/10 21:06:57 网站建设 项目流程

从视觉识别到机械控制:K210与STM32串口通信实战指南

在嵌入式视觉项目中,K210凭借其强大的神经网络加速能力成为图像处理的理想选择,而STM32则以其稳定可靠的实时控制能力著称。当两者结合,便能构建起"视觉感知-决策-执行"的完整闭环系统。本文将手把手带您实现K210识别Apriltag标签并通过串口将坐标数据实时传输给STM32,最终通过舵机进行目标追踪的完整流程。

1. 硬件准备与环境搭建

1.1 所需硬件清单

  • K210开发板:如Sipeed Maix Dock,搭载Kendryte K210双核RISC-V处理器
  • STM32最小系统板:推荐BluePill(STM32F103C8T6)或BlackPill(STM32F411CEU6)
  • USB转TTL模块:用于调试串口通信
  • Apriltag标签:打印标准36h11系列标签( 官方生成器 )
  • SG90舵机:用于目标追踪演示
  • 杜邦线:若干,用于硬件连接

1.2 开发环境配置

K210端环境:

# 安装K210开发工具链 pip install kflash -U pip install maixpy -U # 下载Apriltag识别固件 wget https://dl.sipeed.com/shareURL/MAIX/MaixPy/release/master/maixpy_v0.6.2_72_g22a8555b5/maixpy_v0.6.2_72_g22a8555b5_minimum_with_kmodel_v4_support.bin

STM32端环境:

推荐使用PlatformIO + STM32CubeMX组合开发:

  1. VSCode安装PlatformIO IDE扩展
  2. 通过STM32CubeMX生成HAL库基础工程
  3. 配置USART1为异步模式,波特率115200

2. K210图像识别与数据打包

2.1 Apriltag识别实现

K210通过内置的Apriltag检测算法可以快速识别标签位置。以下是核心识别代码:

from maix import image, display, camera import ustruct def init_uart(): from fpioa_manager import fm from machine import UART fm.register(35, fm.fpioa.UART1_TX, force=True) fm.register(34, fm.fpioa.UART1_RX, force=True) return UART(UART.UART1, 115200, 8, None, 1, timeout=1000) uart = init_uart() while True: img = camera.capture() tags = img.find_apriltags(families=image.TAG36H11) if tags: tag = tags[0] # 取第一个检测到的标签 cx, cy = tag.cx(), tag.cy() data = ustruct.pack("<ff", cx, cy) # 打包为两个float(8字节) uart.write(data) display.show(img)

2.2 数据协议设计

为提高通信可靠性,建议采用以下数据帧格式:

字段类型说明
帧头0xAA固定起始标志
数据长度uint8后续数据字节数
数据内容float[2]cx, cy坐标值
校验和uint8前面所有字节的累加和

优化后的发送函数:

def send_coordinates(cx, cy): frame_header = b'\xAA' data = ustruct.pack('<ff', cx, cy) checksum = sum(frame_header) + len(data) + sum(data) uart.write(frame_header + bytes([len(data)]) + data + bytes([checksum % 256]))

3. STM32数据接收与解析

3.1 串口中断接收实现

在STM32CubeMX中启用USART1全局中断,并实现接收逻辑:

#define BUF_SIZE 64 uint8_t rx_buf[BUF_SIZE]; uint8_t rx_index = 0; bool frame_ready = false; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { static uint8_t state = 0; uint8_t byte = rx_buf[0]; switch(state) { case 0: // 等待帧头 if(byte == 0xAA) { state = 1; rx_index = 0; } break; case 1: // 获取长度 if(byte <= BUF_SIZE - 3) { // 减去帧头和校验和 data_len = byte; state = 2; } else { state = 0; } break; case 2: // 接收数据 if(rx_index < data_len) { rx_buf[rx_index++] = byte; } else { // 校验和验证 uint8_t checksum = 0xAA + data_len; for(int i=0; i<data_len; i++) checksum += rx_buf[i]; if((checksum % 256) == byte) { frame_ready = true; } state = 0; } break; } HAL_UART_Receive_IT(huart, rx_buf, 1); }

3.2 坐标数据处理

frame_ready标志置位时,解析坐标数据:

if(frame_ready) { float cx, cy; memcpy(&cx, rx_buf, 4); memcpy(&cy, rx_buf+4, 4); // 坐标归一化处理(假设图像分辨率320x240) float norm_x = (cx - 160) / 160.0f; float norm_y = (cy - 120) / 120.0f; // 触发控制逻辑 update_servo_position(norm_x, norm_y); frame_ready = false; }

4. 机械控制与系统闭环

4.1 舵机控制实现

使用STM32的PWM模块控制SG90舵机:

void update_servo_position(float x, float y) { // 限制输入范围 x = fmaxf(-1.0f, fminf(1.0f, x)); y = fmaxf(-1.0f, fminf(1.0f, y)); // 转换为PWM占空比(SG90典型范围:500-2500us) uint16_t servo_x = 1500 + (x * 1000); // 水平方向 uint16_t servo_y = 1500 + (y * 1000); // 垂直方向 // 更新PWM输出 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, servo_x); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, servo_y); }

4.2 系统调试技巧

  1. 通信稳定性测试

    • 使用逻辑分析仪抓取串口波形
    • 添加LED指示灯显示通信状态
    // 在接收回调中添加 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
  2. 性能优化建议

    • K210端限制识别帧率(如10FPS)
    • 添加数据滤波算法(滑动平均或卡尔曼滤波)
    # K210端简单滤波实现 filter_buf = [] def filtered_coord(cx, cy): filter_buf.append((cx, cy)) if len(filter_buf) > 5: filter_buf.pop(0) return tuple(sum(x)/len(filter_buf) for x in zip(*filter_buf))
  3. 故障排查清单

现象可能原因解决方案
无数据接收接线错误检查TX-RX交叉连接
数据乱码波特率不匹配确认双方波特率一致
偶发丢帧未启用流控添加硬件流控或软件ACK机制
舵机抖动电源不足单独为舵机供电

5. 进阶扩展方向

5.1 多目标识别与跟踪

修改K210代码支持多个Apriltag识别:

tags = img.find_apriltags(families=image.TAG36H11) if tags: data = bytearray() for tag in tags: data += ustruct.pack("<ffI", tag.cx(), tag.cy(), tag.id()) send_packet(0xAB, data) # 使用不同帧头区分

STM32端相应修改解析逻辑,建立目标ID与舵机动作的映射关系。

5.2 无线通信改造

将串口通信替换为无线方案:

  1. 方案对比表
方案传输距离速率复杂度成本
HC-121km115200bps¥30
ESP-NOW100m1Mbps¥50
LoRa10km300bps¥100
  1. ESP-NOW实现示例
// K210端(需ESP32协处理器) import espnow e = espnow.ESPNow() e.add_peer(b'\xaa\xbb\xcc\xdd\xee\xff') # STM32端MAC地址 e.send(b'\xaa\xbb\xcc\xdd\xee\xff', struct.pack('<ff', cx, cy))

5.3 三维姿态估计

利用Apriltag的pose estimation功能获取三维位置:

for tag in img.find_apriltags(families=image.TAG36H11): pose = tag.get_pose_estimation(0.1) # 标签实际大小(m) # 发送旋转矩阵和平移向量 data = ustruct.pack('<9f3f', *pose.r_mat.flatten(), *pose.t_vec)

在STM32端实现更精确的运动控制算法,如PID控制:

typedef struct { float Kp, Ki, Kd; float error, integral, derivative; float last_error; } PIDController; void PID_Update(PIDController* pid, float error, float dt) { pid->integral += error * dt; pid->derivative = (error - pid->last_error) / dt; pid->last_error = error; return pid->Kp * error + pid->Ki * pid->integral + pid->Kd * pid->derivative; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询