2025 电赛 E 题 MSPM0G3507 下位机 K230 上位机

基于多传感器融合的循迹打击系统

双核心协同 · 陀螺仪精准转弯 · 视觉靶心锁定

一、作品概述

本系统设计了一套双核心协同工作的智能打击系统,通过任务解耦实现高效控制:

🚗 下位机 (MSPM0)

负责四轮循迹小车控制。完成黑线循迹、编码器里程计测量以及基于 MPU6050 的 90° 精准转弯。

🎯 上位机 (K230)

负责二维云台控制系统。通过 GC2093 摄像头进行靶心识别,解算偏移量并控制舵机将激光对准靶心。

二、系统硬件架构

2.1 下位机部分(循迹小车)

模块 型号 功能描述
主控芯片MSPM0G3507TI Cortex-M0+,80MHz,核心运动控制
电机驱动TB6612驱动 4 个带编码器的直流减速电机
循迹传感器4 路红外对管检测地面黑线位置,输入 GPIO
姿态传感器MPU6050I2C 接口,提供陀螺仪数据用于角度积分
显示模块0.96 寸 OLED实时显示里程、角度等调试信息

2.2 上位机部分(云台系统)

模块 型号 功能描述
主控芯片K230嘉楠科技 RISC-V,内置 KPU,负责图像处理
摄像头GC2093CSI 接口,640x360 图像采集
执行机构2×数字舵机PWM 控制,分别负责水平 (Pan) 和垂直 (Tilt)
打击模块405nm 蓝紫色激光GPIO 控制开关,用于指示靶心

三、下位机核心代码解析 (MSPM0)

下位机采用 C 语言开发,核心在于陀螺仪积分算法状态机循迹逻辑

3.1 陀螺仪姿态解算

利用 MPU6050 的 Z 轴角速度进行积分,计算偏航角 (Yaw)。增加了角度归一化和最小角度差计算,防止过零点跳变。

C / MSPM0// 计算从 start 到 current 的最小角度差 [-180, 180]
float angle_diff(float start, float current) {
    float diff = current - start;
    while (diff > 180.0f) diff -= 360.0f;
    while (diff < -180.0f) diff += 360.0f;
    return diff;
}

// 陀螺仪积分更新 yaw
void checkout() {
    int16_t accel[3], gyro[3];
    float temperature;
    mpu6050_read(gyro, accel, &temperature);

    int16_t gz_raw = gyro[2];
    // 500°/s 模式下灵敏度 65.5 LSB/(°/s)
    float gz_dps = ((float)gz_raw) / 65.5f;
    float dt = 0.01f; // 10ms 采样间隔

    yaw += gz_dps * dt;

    // 归一化到 [-180, 180]
    while (yaw > 180.0f) yaw -= 360.0f;
    while (yaw < -180.0f) yaw += 360.0f;
}

3.2 精准转弯控制 (tm2)

采用差速驱动实现原地旋转。当陀螺仪检测到转动角度达到 22.0f(根据齿轮比换算为车身 90° 或其他目标角度)时立即停止。

C / MSPM0void tm2() {
    delay_ms(30);
    checkout();
    yaw = 0.0f; // 清零积分,消除累积误差
    float yaw_start = yaw;
    delay_ms(30);
    
    // 差速转弯:左轮正转,右轮反转
    Set_Speed(0, 3000);  
    Set_Speed(1, -3000); 
    
    while (1) {
        checkout();
        float delta = angle_diff(yaw_start, yaw);
        
        if (fabsf(delta) >= 22.0f) { // 达到目标角度
            Set_Speed(0, 0);
            Set_Speed(1, 0);
            break;
        }
        delay_ms(10);
    }
    delay_ms(200); // 等待惯性停止
}

3.3 状态机循迹算法 (tm1)

基于 4 路红外传感器的状态组合,采用分级调速策略。偏离越远,修正速度差越大。

C / MSPM0void tm1() {
    IR_Module_Read();

    // 情况 1 & 2: 直行 (全灭或中间两路亮)
    if((ir_dh2 && ir_dh3 && !ir_dh1 && !ir_dh4) || (!ir_dh1 && !ir_dh2 && !ir_dh3 && !ir_dh4)) {
        Set_Speed(0, BASE_SPEED);
        Set_Speed(1, BASE_SPEED);
    }
    // 情况 3: 偏左 (左内亮) -> 右轮加速
    else if(ir_dh2 && !ir_dh3) {
        Set_Speed(0, BASE_SPEED);
        Set_Speed(1, TURN_SPEED);
    }
    // 情况 4: 偏右 (右内亮) -> 左轮加速
    else if(!ir_dh2 && ir_dh3) {
        Set_Speed(0, TURN_SPEED);
        Set_Speed(1, BASE_SPEED);
    }
    // 情况 5: 极左 (最左亮) -> 大角度右转
    else if(ir_dh4 && !ir_dh1 && !ir_dh2 && !ir_dh3) {
        Set_Speed(0, TURN_SPEED / 2);
        Set_Speed(1, BASE_SPEED * 2);
    }
    // ... 其他情况兜底直行
    else {
        Set_Speed(0, BASE_SPEED);
        Set_Speed(1, BASE_SPEED);
    }
}

四、上位机核心代码解析 (K230)

上位机运行 Python 脚本,利用 K230 的图像处理能力进行靶心识别,并通过查表法控制云台。

4.1 视觉几何解算

基于小孔成像模型,将像素坐标偏移量转换为实际的角度偏移量,并根据靶心在图像中的高度估算距离。

Python / K230def get_angle_offset(cx, cy):
    """计算靶心相对于画面中心的偏移角度"""
    center_x = FRAME_W / 2
    center_y = FRAME_H / 2
    dx = cx - center_x
    dy = cy - center_y
    
    # 利用焦距参数 F_H/F_V 进行反正切计算
    angle_h = math.atan(dx / F_H)
    angle_v = -math.atan(dy / F_V) # Y 轴翻转校正
    
    return math.degrees(angle_h), math.degrees(angle_v)

def estimate_distance(blob_height_pixels):
    """通过靶心高度估算距离 (mm)"""
    if blob_height_pixels == 0:
        return None
    return (REAL_HEIGHT_MM * F_V) / blob_height_pixels

4.2 智能查表控制算法

为了简化调试并提高响应速度,采用查表法替代传统 PID。根据水平和垂直角度的不同区间,返回不同的舵机调整步长。

Python / K230def compute_change_h(angle_h, angle_v):
    """根据角度偏差查表获取水平修正量"""
    ah = abs(angle_h)
    
    # 区域 1: 垂直角度较小 (目标较近或较低)
    if angle_v < 13:
        if angle_h > 0: # 偏右
            if ah > 25: return 11
            elif ah > 10: return 5
            else: return 3
        else: # 偏左
            if ah > 20: return 2
            else: return 3
            
    # 区域 2: 垂直角度较大 (目标较远或较高)
    else:
        if ah > 25: return 13 # 大偏差快速修正
        elif ah > 10: return 5
        else: return 3
    
    return 0

4.3 主循环与靶心筛选

通过密度阈值 (`density`) 筛选出空心的环形靶心,排除实心干扰物。

Python / K230while True:
    img = sensor.snapshot()
    
    if not adjusted:
        # 寻找黑色色块
        blobs = img.find_blobs([black_tape_threshold], 
                               area_threshold=200, 
                               merge=True)
        if blobs:
            # 关键:筛选空心靶心 (密度小于阈值)
            hollow_blobs = [b for b in blobs if b.density() < DENSITY_THRESHOLD]
            
            if hollow_blobs:
                # 选择面积最大的目标
                max_blob = max(hollow_blobs, key=lambda b: b.pixels())
                bx, by = max_blob.cx(), max_blob.cy()
                
                # 解算角度并控制舵机
                angle_h, angle_v = get_angle_offset(bx, by)
                move_servo(angle_h, angle_v, max_blob.h())
                
                adjusted = True
                done_pin.value(1) # 发送完成信号给下位机
    
    time.sleep(0.005)

五、系统联调与测试结果

5.1 成品展示

小车照片 1 小车照片2

5.2 问题与解决方案

六、总结

本系统成功实现了双核协同的智能打击任务。MSPM0 保证了底层运动的实时性与稳定性,K230 发挥了强大的边缘计算能力进行视觉处理。两者协同工作,架构清晰,调试方便。

未来改进方向

完整工程代码已开源,欢迎交流指正!

查看 GitHub 源码