回答

收藏

[项目提交] 《2025 DigiKey AI应用创意挑战赛》智能篮球监测

2025 DigiKey AI应用创意挑战赛 2025 DigiKey AI应用创意挑战赛 317 人阅读 | 0 人回复 | 2025-12-04

一、项目名称:
智能篮球监测

二、项目概述:
基于 FRDM-MCXW71 的智能篮球监测(拍球计数 + 力量计算),这个项目是采用FRDM-MCXW71,适配低功耗、高性能的运动传感器应用。
实现智能篮球的拍球计数和力量监测功能。辅助篮球训练和练习。


三、作品实物图
这个是采用的FRDM-MCXW71开发板,并利用板载加速度传感器读取数据,进行处理。
开发板采用VS Code for MCUexpresso,导入sdk
运行的过程中,输出的是拍球的次数和排球的力量值。
这个数据可以比较完整的实现预计的数据采集和智能处理功能。采用uart口输出计算结果。
这个开发板可以采用内置在篮球中,并供电 ,这个就可以实现篮球的的数据采集和功能。目前,这个开发板还是比较大,采用这样的方案还需要更进一步的改进和提升。

四、演示视频
后补。


五、项目文档
5.1 核心代码,实现了对于x-y-z三轴的角向量转换和数据度量,这个是实现计算的数学基础。
以下代码基于 FXLS** 加速度传感器数据,实现篮球拍球计数和拍球力量计算。核心思路是:通过 Z 轴加速度变化检测拍球冲击(地面反弹),结合阈值过滤噪声,统计有效拍球次数;通过加速度峰值计算拍球力量(力量与加速度变化幅度正相关)。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <math.h>
  5. #include <time.h>

  6. // 传感器配置参数(根据硬件实际情况调整)
  7. #define ACCEL_THRESHOLD     800    // 拍球冲击阈值(Z轴原始值突变)
  8. #define IMPULSE_DURATION    0.2f   // 单次冲击有效时长(秒),避免重复计数
  9. #define SAMPLING_FREQ       50     // 传感器采样频率(Hz)
  10. #define GRAVITY             9.81f  // 重力加速度 (m/s²)
  11. #define SCALE_FACTOR        0.061f // FXLS**刻度因子 (mg/LSB),±2g量程对应0.061
  12. #define BASKETBALL_MASS     0.6f   // 篮球质量(kg),标准约0.6kg
  13. #define HISTORY_LEN         10     // 加速度历史数据长度

  14. // 拍球计数与力量统计结构体
  15. typedef struct {
  16.     uint16_t dribble_count;        // 累计拍球次数
  17.     double last_impact_time;       // 上一次有效冲击时间(秒)
  18.     int16_t accel_history[HISTORY_LEN]; // Z轴加速度原始值历史
  19.     uint8_t history_idx;           // 历史数据索引
  20.     float max_force;               // 最大拍球力量(N)
  21.     float avg_force;               // 平均拍球力量(N)
  22.     float force_history[100];      // 力量历史(最多存储100次)
  23.     uint8_t force_count;           // 力量数据计数
  24. } DribbleCounter;

  25. /**
  26. * @brief 初始化拍球计数器
  27. * @param counter 计数器结构体指针
  28. */
  29. void dribble_counter_init(DribbleCounter *counter) {
  30.     counter->dribble_count = 0;
  31.     counter->last_impact_time = 0.0;
  32.     counter->history_idx = 0;
  33.     counter->max_force = 0.0f;
  34.     counter->avg_force = 0.0f;
  35.     counter->force_count = 0;
  36.     // 初始化历史数据
  37.     for (int i = 0; i < HISTORY_LEN; i++) {
  38.         counter->accel_history[i] = 0;
  39.     }
  40.     for (int i = 0; i < 100; i++) {
  41.         counter->force_history[i] = 0.0f;
  42.     }
  43. }

  44. /**
  45. * @brief 传感器原始值转换为实际加速度 (m/s²)
  46. * @param raw_value 传感器原始值
  47. * @return 实际加速度值
  48. */
  49. float convert_accel(int16_t raw_value) {
  50.     float accel_g = raw_value * SCALE_FACTOR / 1000.0f;
  51.     return accel_g * GRAVITY;
  52. }

  53. /**
  54. * @brief 计算拍球力量(牛顿)
  55. * @param peak_accel 冲击时的峰值加速度 (m/s²)
  56. * @return 拍球力量值
  57. */
  58. float calculate_force(float peak_accel) {
  59.     // 简化模型:F = Δa * m(Δa为加速度变化量,m为篮球质量)
  60.     float delta_accel = fabs(peak_accel - GRAVITY);
  61.     return delta_accel * BASKETBALL_MASS;
  62. }

  63. /**
  64. * @brief 获取历史数据中的加速度峰值
  65. * @param counter 计数器结构体指针
  66. * @return 峰值加速度 (m/s²)
  67. */
  68. float get_peak_accel(DribbleCounter *counter) {
  69.     float peak = 0.0f;
  70.     for (int i = 0; i < HISTORY_LEN; i++) {
  71.         float accel = convert_accel(counter->accel_history[i]);
  72.         if (fabs(accel) > peak) {
  73.             peak = fabs(accel);
  74.         }
  75.     }
  76.     return peak;
  77. }

  78. /**
  79. * @brief 检测拍球冲击(核心逻辑)
  80. * @param counter 计数器结构体指针
  81. * @param z_raw Z轴加速度原始值
  82. * @return 1-检测到冲击,0-未检测到
  83. */
  84. uint8_t detect_impact(DribbleCounter *counter, int16_t z_raw) {
  85.     double current_time = (double)clock() / CLOCKS_PER_SEC; // 获取当前时间(秒)
  86.     float z_accel = convert_accel(z_raw);

  87.     // 1. 过滤短时间内的重复冲击
  88.     if (current_time - counter->last_impact_time < IMPULSE_DURATION) {
  89.         return 0;
  90.     }

  91.     // 2. 检测Z轴加速度突变(超过阈值)
  92.     if (fabs(z_accel) > (ACCEL_THRESHOLD * SCALE_FACTOR / 1000.0f * GRAVITY)) {
  93.         // 更新冲击时间
  94.         counter->last_impact_time = current_time;

  95.         // 计算本次拍球力量
  96.         float peak_accel = get_peak_accel(counter);
  97.         float current_force = calculate_force(peak_accel);

  98.         // 更新计数
  99.         counter->dribble_count++;

  100.         // 更新力量统计
  101.         if (counter->force_count < 100) {
  102.             counter->force_history[counter->force_count] = current_force;
  103.             counter->force_count++;
  104.         } else {
  105.             // 超出存储上限时,覆盖最早数据
  106.             for (int i = 1; i < 100; i++) {
  107.                 counter->force_history[i-1] = counter->force_history[i];
  108.             }
  109.             counter->force_history[99] = current_force;
  110.         }

  111.         // 更新最大力量
  112.         if (current_force > counter->max_force) {
  113.             counter->max_force = current_force;
  114.         }

  115.         // 更新平均力量
  116.         float sum_force = 0.0f;
  117.         for (int i = 0; i < counter->force_count; i++) {
  118.             sum_force += counter->force_history[i];
  119.         }
  120.         counter->avg_force = sum_force / counter->force_count;

  121.         return 1;
  122.     }

  123.     return 0;
  124. }

  125. /**
  126. * @brief 处理单帧传感器数据
  127. * @param counter 计数器结构体指针
  128. * @param x_raw X轴原始值
  129. * @param y_raw Y轴原始值
  130. * @param z_raw Z轴原始值
  131. * @param current_force 输出参数:本次拍球力量(N)
  132. * @return 当前累计拍球次数
  133. */
  134. uint16_t process_sensor_data(DribbleCounter *counter, int16_t x_raw, int16_t y_raw, int16_t z_raw, float *current_force) {
  135.     // 1. 保存Z轴原始值到历史数据
  136.     counter->accel_history[counter->history_idx] = z_raw;
  137.     counter->history_idx = (counter->history_idx + 1) % HISTORY_LEN;

  138.     // 2. 检测冲击
  139.     uint8_t impact_detected = detect_impact(counter, z_raw);

  140.     // 3. 获取当前力量
  141.     *current_force = 0.0f;
  142.     if (impact_detected && counter->force_count > 0) {
  143.         *current_force = counter->force_history[counter->force_count - 1];
  144.     }

  145.     // 4. 返回累计拍球次数
  146.     return counter->dribble_count;
  147. }

  148. // 模拟硬件读取传感器数据(实际使用时替换为I2C读取逻辑)
  149. void read_fxls**_data(int16_t *x, int16_t *y, int16_t *z) {
  150.     // 示例:固定返回初始数据,实际需从0x19地址读取
  151.     *x = -31;
  152.     *y = -100;
  153.     *z = 1063;
  154. }
复制代码
核心功能:
拍球计数:通过检测 Z 轴加速度突变(地面反弹冲击),结合时间阈值避免重复计数;
力量计算:基于加速度峰值和篮球质量(0.6kg)计算拍球力量(单位:牛顿);
数据统计:实时输出当前拍球次数、单次力量、最大力量、平均力量。
关键参数调整:
ACCEL_THRESHOLD:拍球冲击阈值,需根据实际使用场景校准(建议在无动作时采集基线数据,阈值设为基线 + 200 左右);
SCALE_FACTOR:FXLS** 的刻度因子,需匹配传感器实际量程(如 ±2g 量程对应 0.061mg/LSB,±4g 对应 0.122mg/LSB);
IMPULSE_DURATION:单次冲击有效时长,避免快速抖动导致重复计数(建议 0.1~0.3 秒)
5.2 核心函数
函数名
功能
dribble_counter_init
初始化计数器,清空历史数据和统计值
convert_accel
将传感器原始值转换为实际加速度(m/s²)
calculate_force
基于加速度峰值和篮球质量计算拍球力量(牛顿)
detect_impact
核心冲击检测逻辑(阈值判断 + 时间过滤,避免重复计数)
process_sensor_data
处理单帧传感器数据,整合历史存储、冲击检测、力量计算逻辑


标准输出为

5.3 硬件连接
FXLS** 传感器 I2C 地址:0x19(与开发板默认 I2C1 连接);
I2C 引脚:PTE24 (SDA)、PTE25 (SCL);
供电:3.3V(与开发板 VDD 连接)。

5.4 编译与烧录
  • SDK 配置:
    • 导入 FRDM-MCXW71 的 SDK 包(包含 I2C、SysTick、UART 驱动);
    • 启用 FPU(浮点运算),支持float类型计算;
  • 烧录:通过开发板的调试器烧录固件;
  • 串口工具:使用 Putty / 串口助手(115200 8N1)查看输出。

以下是编译成功的页面

以下是运行成功的页面



5.5 调试与校准
  • 阈值校准:
    • 先静止放置篮球,读取 Z 轴基线值(约 1024 左右,对应 1g);
    • 将ACCEL_THRESHOLD调整为基线 + 200~300(避免误触发)。
  • 采样频率调整:
    • 修改SAMPLING_FREQ和FXLS**_CTRL_REG1的 ODR 配置(100Hz/200Hz 可选)。
  • 力量校准:
    • 实际拍打篮球,对比测力仪数据,调整BASKETBALL_MASS系数。

5.6 代码附件
参加附件
BasketBallPaddling.zip (739.98 KB, 下载次数: 0)





分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条