找回密码
 立即注册
查看: 681|回复: 5

国二,使用STC32G12K128 芯片为主控获得全国大学生电子设计竞赛国家二等奖

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-11-07 10:53:07
已绑定手机

1

主题

4

回帖

27

积分

新手上路

积分
27
发表于 2025-9-27 18:18:09 | 显示全部楼层 |阅读模式
       7月初,我们开始了全国大学生电子设计大赛的备赛。经过分析比较,我们队伍最终选择了 STC32G12K128 单片机 作为主控芯片。该芯片最高支持 80MHz 主频,单周期指令运行效率高;同时集成 12 位 ADC、PWM、多个定时器及 UART 接口,资源丰富且开发友好,能够很好地满足本题所需的实时性与多外设并行处理需求。
      在备赛阶段,我们以 23 年E题作为训练题,重点掌握了 PWM 舵机驱动、外部中断捕获 、步进电机驱动等功能。将一些必要的模块函数封装好后,电赛比赛开始了。在众多题目中,我们选择了 E 题建议自动瞄准装置,该题要求实现自动巡线、激光笔打靶功能。自动巡线部分使用赛题要求的MSPM0G3507,激光打靶系统主要由 STC32G12K128 主控芯片、双自由度云台、激光发射电路及显示模块 组成。在选择云台方案时,我们考虑了两种方案,方案一采用舵机驱动,其优势在于扭矩性能突出,如MG996R等典型舵机可输出≥12 kg·cm的扭矩,具备较强的直接负载驱动能力,同时成本低廉(单价约为步进电机的1/3),且集成度较高。然而舵机存在精度不足的问题,机械死区约0.5°–1°,并受齿隙和温度漂移影响,定位偏差可达±2°。相比之下,方案二的步进电机驱动系统虽扭矩较小,但凭借128细分微步控制可实现0.045°的角分辨率,定位精度远优于舵机。综合考虑本项目对扭矩需求不高而对精度要求严格,最终选用方案二,即步进电机云台系统。在比赛中,我们充分发挥芯片的高速与资源优势,成功实现了目标检测、角度解算、自动瞄准与精确发射。

1. 系统设计思路
本系统以 STC32G12K128 为核心控制器,基于“目标检测—角度解算—步进电机转动—瞄准发射—状态反馈”闭环逻辑工作。
  • 目标检测与定位:光敏阵列或超声波模块实时检测目标位置。
  • 角度解算与控制算法:根据目标误差计算步进电机转角,利用脉冲调节。
  • 自动瞄准与发射:当误差小于阈值时,触发发射模块。
  • 参数设定与显示:通过屏幕实时显示角度、目标位置、命中状态等。

2.云台控制部分
对于由两个步进电机构成的二维云台,激光笔固定于云台的正下方,此时激光笔相对于摄像头的坐标是固定的。
云台1控制X轴的运动,云台2控制Y轴的运动。假设云台1的角度为θ1,云台2的角度为θ2。云台的末端坐标为(x, y)。
步进云台我们采用双环控制,外环视觉定位(K230输出目标坐标)内环步进电机微步控制(STC32实现)
通过云台的运动,可以得到云台的坐标变化关系如下:
截图202509271814335323.jpg
其中,L 是步进云台到屏幕的距离。这就是由两个步进电机构成的二维舵机云台的运动学模型。
3.视觉部分设计
k230 模块因其学习资源、图像处理能力强大、高分辨率摄像头,我们综合考虑选择k230k230 模块是一款具备 ai 功能的摄像头模块,双核RISC-V CPU + 自研KPU NPU:支持最高4TOPSINT8)算力,兼顾通用计算与神经网络加速,满足YOLOv5s等轻量模型实时推理。能够独立完成图像处理和物体识别,无需外接处理器。它的高度集成化设计减少了对外部组件的依赖,同时其低功耗特性使之适合便携式设备。
截图202509271815115369.jpg
4.控制工作流程
步进电机云台控制部分根据视觉传来的偏差转换为步进电机x y轴需运动的脉冲数进行控制,并通过增加编码器进行闭环控制防止丢步。
截图202509271816565957.jpg
5.测试方法与数据结果分析
用数字万用表分别测试每条导线,看是否导通,有无断路和短路情况,看整个系统的地线是否都接在一起,看焊接点有没有虚焊等。装置可能会出现一些步进电机不转、抖动等故障,有可能是以下方面的问题:焊接不良、接线不正确或者接地电阻过大、 软件设计中参数设置不当、某个芯片已损坏。
1)装置一键启动激光能否击中心                                                                 
序号
移动时间
距离偏差cm
1
1.7s
1.9cm
2
1.8s
1.1cm
3
1.7s
1.8cm
4
1.4s
17cm
2)装置能否成功循迹
序号
装置是否成功循迹
1
2
3
4
依据上表显示该装置具备了循迹的能力,且具有自动瞄准的功能
在参加2025年电子设计竞赛的过程中,我们获得了许多宝贵的经验和深刻的感悟。
在竞赛过程中,我们深入理解了单片机控制、k230传感器应用以及步进电机驱动的核心技术。特别是在设计和调试阶段,我们学会了如何将这些技术高效整合,实现系统的预期功能。这不仅提升了队伍的硬件设计能力,也增强了我们在图像识别和自动化控制方面的技能。。
竞赛是一个团队合作的过程,我们的项目涉及到多个方面的技术,每个成员都有自己的专长。通过有效的沟通和协调,我们能够将各自的专长融入项目中,最终实现了系统的功能目标。这让我们深刻认识到团队合作在技术项目中的重要性,尤其是在面对复杂的系统设计和开发时。

截图202509271814207056.jpg
截图202509271816128621.jpg
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-11-07 10:53:07
已绑定手机

1

主题

4

回帖

27

积分

新手上路

积分
27
发表于 2025-9-27 18:31:01 | 显示全部楼层
补充下材料及中间拍的两张车照  
视频链接:https://www.bilibili.com/video/B ... 9507ceb2ff2f1ff7357
截图202511061024268548.jpg 截图202511061024196985.jpg
电赛国二获奖证明.png
截图202509271828361735.jpg
截图202509271828531948.jpg
电赛国二获奖证明.png
截图202509271830438698.jpg
截图202509271830483026.jpg

E题_简易自行瞄准装置.pdf

814.89 KB, 下载次数: 6

2025-E题.7z

455.47 KB, 下载次数: 24

E题_简易自行瞄准装置.pdf

814.89 KB, 下载次数: 15

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:55
  • 最近打卡:2025-12-05 11:04:53
已绑定手机

3

主题

6

回帖

366

积分

版主

积分
366
发表于 2025-9-28 08:24:56 | 显示全部楼层
       恭喜获奖
为了不影响您顺利拿到奖金,麻烦在该贴提供如下材料:
1.赛前在本坛连续3个月发布10次以上自己用 STCAI的MCU, 备赛/竞赛的图片的帖子链接
2.自己学校的校级证明,参与竞赛的技术文档,包括但不限于原理图,源程序,PCB版设计等。
3.关于此获奖项目的视频讲解

后续我们将邀请专家协助我们进行评审。资料提供完毕后,请耐心等候   
Tel: 0513-55012977     18051419563(微信同号)
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-11-07 10:53:07
已绑定手机

1

主题

4

回帖

27

积分

新手上路

积分
27
发表于 2025-11-6 09:42:13 | 显示全部楼层
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-11-07 10:53:07
已绑定手机

1

主题

4

回帖

27

积分

新手上路

积分
27
发表于 2025-11-7 10:53:07 | 显示全部楼层

#include "control.h"
// 命中判定计数(用于激光开关的去抖)
static uint16 aim_cnt = 0;

// 命中判定阈值与保持时间(像素阈值、连续周期数)
const uint16 err_x_th = 6;   // X 误差阈值(像素)
const uint16 err_y_th = 6;   // Y 误差阈值(像素)
const uint16 hold_need = 5;  // 连续满足阈值的周期数

// IMU 角速度与零偏(需要姿态时可用)
float        angle_z_0off = 0,angle_z=0,angle_y_0off=0,angle_y=0;
float Yaw_Integral;
// PID 结构体与实例(用于 XY 轴速度控制)
typedef struct
{
    float kp;
    float ki;
    float kd;
    float integral;
    float prev_error;
    float integral_min;
    float integral_max;
    float out_min;
    float out_max;
} pid_t;

// kp, ki, kd, integral, prev_error, integral_min/max, out_min/max
static pid_t pid_x = {0.40f, 0.00f, 0.02f, 0.0f, 0.0f, -200.0f, 200.0f, -80.0f, 80.0f};
static pid_t pid_y = {0.45f, 0.00f, 0.06f, 0.0f, 0.0f, -200.0f, 200.0f, -80.0f, 80.0f};

// 计算一步 PID 输出:像素误差 -> 期望转速(RPM),正负表示方向
static float pid_step(pid_t *pid, float error, float dt_s)
{
    float derivative = (error - pid->prev_error) / dt_s;
    pid->integral += error * dt_s;
    if (pid->integral > pid->integral_max) pid->integral = pid->integral_max;
    if (pid->integral < pid->integral_min) pid->integral = pid->integral_min;
    float out = pid->kp * error + pid->ki * pid->integral + pid->kd * derivative;
    if (out > pid->out_max) out = pid->out_max;
    if (out < pid->out_min) out = pid->out_min;
    pid->prev_error = error;
    return out;
}
void gyroOffsetInit(void)
{
        uint8 i = 100;
        float temp_z[100]={0};
        float temp=0;
        while (i)
        {
                i--;
                imu660ra_get_gyro();
                temp_z[i]=((float)(imu660ra_gyro_z)) / 16.4;
                temp += temp_z[i];
                delay_ms(5);
        }
        angle_z_0off = temp / 100;
}

uint8 imu_init_flag = 100;
void Imu_Init(void)
{
   
        if(imu660ra_init() == 0)
        {
                imu_init_flag = 1;
        }
    gyroOffsetInit();
    Yaw_Integral = 0;
}

void Angle_Get(void)
{
    imu660ra_get_gyro();
    angle_z = ((float)(imu660ra_gyro_z)-angle_z_0off)/16.4;
          angle_y = ((float)(imu660ra_gyro_y)-angle_y_0off)/16.4;
//                angle_z = imu660ra_gyro_transition(((float)(imu660ra_gyro_z)-angle_z_0off));
}


void Angle_Integral(void)
{
        Angle_Get();
                Yaw_Integral += angle_z*dt;

}

// 基于图像坐标误差的二维云台跟踪(速度模式 + PID)
void gimbal_track_xy(uint16 real_x, uint16 real_y, uint16 target_x, uint16 target_y)
{
    int32 x_err = (int32)target_x - (int32)real_x;
    int32 y_err = (int32)target_y - (int32)real_y;

    uint16 dead_zone = 5;     // 像素死区,避免微抖
    uint16 min_vel   = 1;     // 最小转速 RPM,克服静摩擦
    uint16 max_vel   = 60;    // 最大转速 RPM,结合机械能力

    // X 轴控制(电机1)
    {
        uint32 abs_x = (x_err >= 0) ? (uint32)x_err : (uint32)(-x_err);
        if (abs_x <= dead_zone)
        {
            Emm_V5_Vel_Control_1(1, 0, 0, 10, 0); // 停止
        }
        else
        {
            float u = pid_step(&pid_x, (float)x_err, dt); // PID 输出
            uint8 dir_x = (u < 0.0f) ? 1 : 0; // 负为右转,正为左转(与现有方向约定一致)
            uint16 vel_x = (uint16)((u < 0.0f) ? -u : u);
            if (vel_x < min_vel) vel_x = min_vel;
            if (vel_x > max_vel) vel_x = max_vel;
            Emm_V5_Vel_Control_1(1, dir_x, vel_x, 10, 0);
        }
    }

    // Y 轴控制(电机2)
    {
        uint32 abs_y = (y_err >= 0) ? (uint32)y_err : (uint32)(-y_err);
        if (abs_y <= dead_zone)
        {
            Emm_V5_Vel_Control_1(2, 0, 0, 10, 0); // 停止
        }
        else
        {
            float u = pid_step(&pid_y, (float)y_err, dt); // PID 输出
            uint8 dir_y = (u > 0.0f) ? 1 : 0; // 正为上转,负为下转(与现有方向约定一致)
            uint16 vel_y = (uint16)((u < 0.0f) ? -u : u);
            if (vel_y < min_vel) vel_y = min_vel;
            if (vel_y > max_vel) vel_y = max_vel;
            Emm_V5_Vel_Control_1(2, dir_y, vel_y, 10, 0);
        }
    }

    uint16 ax = (ex >= 0) ? (uint16)x_err : (uint16)(-x_err);
    uint16 ay = (ey >= 0) ? (uint16)y_err : (uint16)(-y_err);
    if (has_target && ax <= err_x_th && ay <= err_y_th)
    {
        if (aim_cnt < 1000) aim_cnt++;
    }
    else
    {
        aim_cnt = 0;
    }

    if (aim_cnt >= hold_need)
    {
        light_open();
    }
    else
    {
        light_close();
    }
}

// 目标处理:有目标 -> 跟踪;无目标 -> 低速左右搜索(俯仰保持)
void gimbal_track_handle(uint8 has_target, uint16 real_x, uint16 real_y, uint16 target_x, uint16 target_y)
{
    static uint16 scan_tick = 0; // 扫描计数器
    static uint8 scan_dir = 0; // 0 左, 1 右

    if (has_target)
    {
        // 发现目标:立即跟踪
        gimbal_track_xy(real_x, real_y, target_x, target_y);
        scan_tick = 0;
        return;
    }

    // 无目标:低速左右扫描 yaw,俯仰停住
    Emm_V5_Vel_Control_1(2, 0, 0, 10, 0); // 停止俯仰

    if (scan_tick++ >= 150) // 每隔固定周期换向
    {
        scan_tick = 0;
        scan_dir = (scan_dir == 0) ? 1 : 0;
    }
    Emm_V5_Vel_Control_1(1, scan_dir, 5, 10, 0);
}
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-11-07 10:53:07
已绑定手机

1

主题

4

回帖

27

积分

新手上路

积分
27
发表于 2025-11-7 11:09:50 | 显示全部楼层
视频:

redpandacompress_202511060906.mp4

12.76 MB, 下载次数: 0

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-12-8 08:51 , Processed in 0.111005 second(s), 68 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表