Funpack第八期—用Nano 33 BLE Sense做的投篮
Nano 33 BLE Sense 蓝牙,作业,投篮 、IMU、加速度、角速度
标签
嵌入式系统
aramy
更新2021-05-14
978

    项目介绍:funpack第八期开发板Arduino Nano 33 BLE Sense。这是一款非常小巧的开发板,集成了非常多的传感器,尤其吸引我的是,tinyml在这个开发板上得到了支持。
    这里我选择任务一:投篮运动手柄。利用NANO-33 BLE的加速度及角速度感应器,设计一款用于虚拟练习投篮的手柄。投篮者可通过手持手柄(开发板)模拟投篮动作,而手柄会根据初始物理参数(可由用户设定或者系统默认值),对投篮者的命中率作出预判,并且可以将改进建议的信息反馈给投篮者。优秀的模拟投篮手柄的确可以帮助用户提高投篮命中率。 做为作业去完成。

  FlF1utIc8mhBXk0aFvioe3OE0Axv
    解决问题思路:这里绘制了个简单的投篮示意图(灵魂画手,请勿介意)。人手给篮球一个力,这个力持续一段时间,使篮球速度产生变化。然后篮球脱离手,仅仅受到重力影响,做自由落体运动。篮球到篮筐的水平距离、垂直距离都是已知的,那么求出篮球脱手时的速度,和速度方向即可。篮球脱手的速度可以由篮球受到的加速度和时间的积分获得,而这个开发板带有加速度传感器的,通过加速度传感器就可以测量出三轴的加速度,再乘以时间,就可以获得累加起来的速度了。至于说速度方向,自然是和加速度方向一致。用加速度和角速度可以算出开发板的四元数,拿到开发板在空间中的仰角就知道加速度与水平面的夹角了。有了夹角,计算水平加速度和垂直加速度。水平加速度可以计算水平方向的速度,垂直方向加速度,累加上重力加速度,就可以计算竖直方向的速度了。

#include <Arduino_LSM9DS1.h> . // this is for IMU on BLE 33
#include <ArduinoBLE.h>
#include "SensorFusion.h" //SF
SF fusion;

float gx, gy, gz, ax, ay, az, mx, my, mz;
float pitch, roll, yaw;
float deltat;

void setup() {
  Serial.begin(115200);
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }

  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);
  }
  delay(3000);

}

void loop() {
  if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable() && IMU.magneticFieldAvailable()) {
    IMU.readAcceleration(ax, ay, az);
    IMU.readGyroscope(gx, gy, gz);
    IMU.readMagneticField(mx, my, mz);
    gx = gx - 1.77;     //校零
    gy = gy + 0.521;
    gz = gz + 0.431;
    float gyroScale = 3.1415926f / 180;
    gx = gx * gyroScale;
    gy = gy * gyroScale;
    gz = gz * gyroScale;

    deltat = fusion.deltatUpdate(); //this have to be done before calling the fusion update
    //choose only one of these two:
    //    fusion.MahonyUpdate(gx, gy, gz, ax, ay, az, deltat);  //mahony is suggested if there isn't the mag and the mcu is slow
    fusion.MadgwickUpdate(gx, gy, gz, ax, ay, az, mx, my, mz, deltat);  //else use the magwick, it is slower but more accurate

    pitch = fusion.getPitchRadians();
    roll = -fusion.getRollRadians();    //you could also use getRollRadians() etc
    yaw = fusion.getYawRadians();

    Serial.print(pitch);
    Serial.print("\t");
    Serial.print(roll);
    Serial.print("\t");
    Serial.print(yaw);
    Serial.print("\t");

    Serial.print(ax);
    Serial.print("\t");
    Serial.print(ay);
    Serial.print("\t");
    Serial.print(az);
    Serial.print("\t");

    Serial.print(gx);
    Serial.print("\t");
    Serial.print(gy);
    Serial.print("\t");
    Serial.println(gz);
  }

}

FtfHJ84xNplXNzMc-uNao2KKLygt

将四元数的姿态信息用processing做可视化后,才发现现实的残酷。四元数能够很好地反映开发板的姿态信息,通过仰角也是能够获得开发板与水平面的夹角,可是这个仰角数据滞后严重啊!尝试旋转开发板,然后水平放置在桌面上,图中的立方体,是缓慢恢复到水平态的!而用手投球,发力时长是按毫秒计算的。这样的滞后,感觉没法算了。

    更换思路:放弃通过加速度去计算速度的方法,换种思考角度,尝试通过机器学习来解决这个问题。传感器获得的信息是条件,计算出篮球脱手的速度和角度是方法,是否命中篮筐是结果。既然方法这块弄起来有困难,那么就给足够多的条件和结果,用机器学习的方式去推算方法。

  FtdyYXR1lONJqCrEp4cQzSBsn83y

    首先,收集几组投篮的数据,来明确条件的边界。上图中图像是将三个轴的加速度做了平方和的结果。可以看出平时的运动,三个轴加速度和基本为1,投篮时,加速度会变大,图像上有明显的突变。这里每个点之间是50ms(这里应该是串口影响,每次取样时间间隔近50ms,传感器的频率应该是110HZ,间隔应该在10ms才对),一次投篮的加速度差不多350ms左右。取加速度和为35作为阈值。开始训练,通过串口记录训练样本值。
    这里保留影响投篮的相关参数:三轴加速度,三轴角速度。其它传感器信息都不要。以加速度和超过35的点作为中心点,前后一共取32个点的数据。当出现符合条件的情况,就通过串口把数据传送给电脑,记录下来作为训练数据。实际测量时发现有投完球后也会有数据出现,就再从时间上限定,出现符合条件的数据后1秒内不再接受数据。
    因为需要接电脑收集数据,不方便使用篮球场收集数据(就是因为宅,且不会打篮球,而且也没有篮球)。就改用孩子的米奇玩偶,在室内的墙上做一个标记位,每次击中标记位即为投中。将开发板绑在手背上,用usb延长线与电脑连接。米奇每击中标记位,就记录一次数据,每组数据均是32*6=192位的数据。应该记录100次以上的,但是为了测试(宅男属性,体力不支)收集到了62组数据就停止了。

2.38	-3.86	3.05	109.99	-37.11	29.54	2.42	-4	3.15	119.2	-33.75	38.09	2.26	-4	2.83	119.51	-17.82	69.82	2.22	-3.68	2.02	107.6	-6.71	116.46	2.27	-3.05	1.59	89.66	8.48	171.02	1.95	-2.83	1.54	58.29	30.58	244.2	1.38	-2.61	1.4	2.14	63.96	316.47	1.44	-1.88	1.55	-91.19	105.35	365.23	1.13	-2.08	1.97	-211.43	149.72	428.16	0.98	-2.13	2.57	-268.55	260.19	506.71	-0.88	-2.48	2.99	-222.29	360.23	612.37	-3.98	-1.05	1.76	-105.83	356.38	700.93	-4	1.42	0.04	-56.15	430.66	745.18	-4	3.43	-0.39	-177.98	635.07	672.24	-4	4	-1.42	-141.91	619.14	616.46	-4	4	-3.21	-288.51	595.58	332.7	-4	4	-4	-406.19	628.66	125.49	-4	4	-4	-522.77	520.69	56.21	-4	0.99	-4	-439.39	152.71	-34.48	-3.48	-0.33	-3.64	-139.1	-85.88	-300.78	-2.19	-1.99	-3.22	127.01	-62.5	-454.53	-1.21	-3.13	-2.13	189.27	-53.65	-430.85	-0.34	-3.99	-0.93	112.3	-162.72	-322.51	0.73	-4	-0.11	41.26	-242.37	-220.83	0.38	-3.58	-0.02	43.4	-222.53	-187.74	0.73	-3.5	0.26	86	-164.92	-133.06	0.79	-2.52	-0.04	78.37	-105.16	-90.64	1.02	-2.03	0.01	19.78	-78.06	-57.62	0.88	-1.59	0.17	-48.77	-58.78	-32.35	0.68	-1.51	0.28	-69.15	-47.24	-1.16	0.37	-1.23	0.37	-62.87	-32.04	5.86	0.55	-0.65	0.19	-43.46	-17.09	0.85
1.79	-3.44	3.57	128.72	-54.99	59.2	1.82	-3.52	3.43	130.62	-53.96	81.91	1.94	-3.5	3.45	115.05	-29.79	125.55	1.9	-3.65	3.4	61.77	12.21	194.64	1.87	-4	3.33	-20.75	73.36	280.46	1.53	-3.98	3.38	-89.05	135.44	368.29	1.42	-3.27	3.48	-136.35	213.2	437.19	0.76	-2.55	3.62	-207.34	332.76	481.26	-0.45	-2.58	3.97	-256.71	486.88	517.33	-2.33	-2.91	3.57	-205.2	605.41	559.94	-3.98	-2.39	2.84	-46.57	654.91	608.52	-4	0.12	1.42	178.59	715.09	603.09	-4	2.68	1.49	338.62	791.2	533.69	-4	4	0.2	280.09	792.97	479.37	-4	4	0.35	21.12	810.55	411.07	-4	4	-2.11	-226.87	810.73	325.99	-4	4	-2.85	-437.19	638.37	226.56	-4	3.81	-3.64	-456.73	539.25	193.05	-4	1.9	-4	-234.99	430.18	76.11	-3.3	1.32	-3.99	54.44	220.95	-187.19	-2.56	1.23	-3.42	192.75	78.49	-420.78	-2.06	-0.63	-3	201.29	-2.93	-438.6	-1.68	-1.91	-2.19	77.45	-41.56	-441.47	-0.6	-1.38	-1.06	-57.92	-40.95	-428.16	-0.6	-3.58	-0.57	-74.04	-61.77	-305.85	0.2	-2.99	-0.15	-86.43	-70.31	-247.68	0.61	-2.22	-0.05	-79.71	-80.69	-155.76	0.66	-2.09	0.04	-70.31	-90.27	-47.12	0.71	-1.51	-0.1	-83.68	-91.13	-2.38	0.8	-1.3	-0.23	-98.75	-86.55	20.51	0.74	-1.21	-0.36	-106.75	-86.67	50.66	0.76	-0.95	-0.25	-119.08	-52.61	60.97
2.62	-2.9	2.84	88.93	-42.24	47.3	2.69	-3.39	3.11	126.16	-76.9	21.73	3.18	-3.64	3.33	138.73	-68.54	-17.03	3.22	-4	3.3	120.42	-40.16	-19.29	3.09	-4	2.79	92.1	-30.03	31.13	3.16	-4	2.08	70.19	0.79	73.97	2.97	-3.94	2.03	57.13	47.49	137.33	2.68	-3.6	1.6	51.57	85.14	225.22	2	-3.15	1.17	-6.29	139.1	323.67	1.16	-2.27	0.9	-102.48	206.42	446.66	-0.19	-1.81	0.85	-209.66	259.64	559.69	-3.03	0.16	0.19	-266.17	277.34	693.79	-4	1.68	0.02	-266.54	327.45	801.7	-4	3.24	-0.46	-89.54	438.35	758.73	-4	4	-0.79	94.42	535.64	518.8	-4	4	-1.78	134.22	605.1	240.84	-4	4	-2.27	13.67	581.97	-32.35	-4	4	-3.57	-206.18	424.5	-214.17	-4	2.76	-2.58	-392.88	258.06	-284.48	-3.9	-1.8	-2.07	-330.2	86.43	-265.81	-2.04	-3.35	-2.45	-90.52	-12.15	-273.56	-1.13	-2.71	-2.49	96.19	24.6	-300.72	-0.69	-2.12	-2.23	146	32.78	-275.51	-0.13	-1.81	-1.6	102.97	-58.96	-230.96	0.07	-1.88	-0.93	51.15	-168.15	-180.24	0.38	-2.19	-0.32	26.25	-198.18	-147.22	0.57	-2.25	0.07	2.14	-167.18	-141.48	1.04	-2.07	0.34	-25.94	-129.09	-122.62	1.33	-1.93	0.56	-45.41	-96.5	-54.81	0.65	-2.34	0.93	-58.96	-63.05	31.98	0.98	-1.52	0.9	-80.99	-45.78	56.4	0.83	-1.08	0.66	-90.64	-24.11	71.17

再在屋顶标记一个目标点,做为用力过猛的点,继续收集数据,收集到了60组数据。

0.09	0.99	-0.73	-2.26	37.78	51.51	0.08	0.99	-0.78	0.85	37.41	42.3	0.07	0.97	-0.81	1.1	33.69	35.58	0	1.05	-0.85	0.12	28.87	31.49	-0.1	1.07	-0.88	-0.49	24.54	29.54	-0.2	1.12	-0.88	0.67	23.44	31.13	-0.27	1.15	-0.9	1.34	28.14	36.01	-0.3	1.18	-0.95	-3.66	34.06	44.19	-0.34	1.34	-1.02	-13.49	38.76	50.17	-0.4	1.54	-1.13	-18.98	43.95	51.45	-0.54	1.76	-1.22	-10.68	50.17	47.55	-0.78	1.97	-1.36	4.27	59.14	35.83	-1.16	2.49	-1.54	11.17	67.26	22.46	-1.88	2.92	-1.63	14.89	69.09	10.19	-2.71	3.44	-2.27	8.24	85.33	7.39	-2.96	4	-3.38	-43.33	114.75	8.06	-2.57	4	-4	-68.3	140.93	-1.59	-1.87	4	-4	-39.18	125.18	-47.61	-1.21	3.75	-4	-9.7	71.72	-102.11	-0.84	3.41	-4	-19.78	-32.1	-191.41	-1.33	3.02	-3.61	-10.44	-113.77	-261.78	-0.43	2.38	-3.81	-47.73	-158.75	-331.85	0.03	2.24	-3.86	-142.21	-238.83	-362.61	0.19	1.6	-3.75	-218.08	-355.65	-402.53	0.25	1.03	-3.3	-304.38	-483.89	-446.17	0.36	-0.14	-2.38	-379.7	-635.99	-511.47	-1.18	-0.94	-0.63	-407.04	-847.29	-627.81	-4	-0.38	1.99	-304.02	-1102.17	-790.1	-4	-1.76	4	-14.89	-1289.79	-969.79	-4	-4	4	558.23	-1196.35	-800.78	-4	-4	3.84	759.03	-510.93	-295.84	-3.82	-4	2.76	575.32	-178.83	13.18
-0.72	0.84	-1.08	-0.18	18.19	27.22	-0.69	0.9	-1.11	-1.89	22.64	19.53	-0.7	0.93	-1.11	0.37	27.22	8.36	-0.75	0.94	-1.15	5.55	29.24	-3.17	-0.79	0.91	-1.19	9.22	32.23	-13.31	-0.87	0.88	-1.22	12.08	35.89	-21.42	-0.91	0.88	-1.29	12.94	39.98	-27.95	-1	0.91	-1.37	9.46	44.43	-33.14	-1.11	1.01	-1.47	4.39	48.4	-37.29	-1.33	1.14	-1.57	0.61	45.47	-42.36	-1.71	1.37	-1.74	-0.98	36.93	-48.28	-2.15	1.59	-2.02	1.1	30.58	-53.71	-2.54	1.83	-2.48	0.12	39.43	-61.1	-3.02	2.22	-3.16	-0.92	60.42	-81.79	-3.54	2.64	-3.94	5.92	85.02	-117	-3.67	2.63	-4	16.48	111.27	-156.01	-3.39	2.76	-4	11.84	125.18	-198.43	-2.85	2.68	-4	17.21	108.28	-254.03	-2.21	2.35	-4	26.31	46.57	-322.88	-1.67	2.28	-4	13.98	-37.17	-381.04	-1.61	2.65	-3.99	-20.45	-147.34	-417.48	-1.35	2.3	-3.78	-51.03	-281.56	-453.86	-1.38	3.13	-3.15	-74.46	-424.8	-493.29	-0.88	1.09	-2.76	-58.53	-599.06	-550.11	-1.33	-0.3	-1.79	-76.72	-798.03	-646.42	-2.64	-0.94	-0.48	-171.94	-1031.07	-834.41	-4	-1.61	0.28	-188.78	-1302.12	-975.34	-4	-3.12	0.64	-229.37	-1593.69	-1125.61	-4	-4	4	-267.7	-1458.44	-1121.4	-4	-4	4	-254.15	-523.25	-570.13	-4	-4	4	128.48	-244.81	-61.16	-4	-4	4	302.8	-152.77	280.52
-0.75	1.02	-0.99	-14.83	15.14	41.38	-0.85	1	-0.99	-11.05	13.31	33.63	-0.85	0.99	-0.99	-10.25	18.74	23.19	-0.81	1.01	-1	-9.52	23.25	12.45	-0.86	1.01	-1	-7.81	25.15	3.54	-0.92	1.04	-1.01	-6.9	27.89	-3.36	-0.99	1.06	-1.03	-6.84	31.25	-9.58	-1.05	1.07	-1.06	-7.87	36.32	-15.63	-1.07	1.1	-1.12	-11.54	43.09	-18.92	-1.1	1.16	-1.17	-17.64	50.05	-21.61	-1.25	1.28	-1.24	-19.1	55.79	-26.73	-1.55	1.47	-1.36	-17.33	58.35	-35.52	-2.09	1.91	-1.63	-14.4	59.69	-47.55	-2.89	2.5	-2	-10.01	60.85	-57.5	-3.58	2.87	-2.73	-10.86	64.33	-65.25	-3.8	2.92	-3.47	-22.64	72.2	-84.96	-3.44	2.8	-3.99	-34.97	83.01	-130.25	-2.96	2.74	-4	-35.58	77.76	-202.15	-2.7	2.19	-4	-29.72	34.85	-296.2	-2.42	2.44	-4	-40.65	-40.65	-396.67	-2.21	1.92	-3.96	-38.15	-138.12	-465.76	-2.3	2.57	-3.42	-43.88	-248.9	-514.59	-1.83	2.2	-2.91	-85.33	-356.81	-558.17	-1.44	1.93	-2	-168.4	-484.5	-619.63	-1.27	1.28	-1.42	-253.05	-636.84	-660.16	-1.2	2.03	-0.7	-346.37	-820.8	-734.37	-3.96	-0.65	-0.56	-350.52	-1091.98	-888.18	-4	-0.75	0.63	-308.84	-1445.92	-942.63	-4	0.85	2.89	-228.33	-1602.97	-1137.27	-4	-4	4	60.24	-1350.28	-1002.2	-4	-4	4	185.12	-460.94	-569.82	-4	-4	4	348.94	-176.76	-164.79

科学的办法应该是将传感器固定在手上,然后真实地去场地投篮,记录成功与失败的数据,要想细分,失败的数据还要进一步对失败原因进行标记,将每种情况进行分类。这里就先在屋中用测试数据代替了。仅仅分为投中和用力过猛两类。

    收集到这些数据条件后,对数据对应的结果进行标记。投中的标记为0,用力过猛的标记为1.数据标记后,做归一处理,通过查询,可以得知,加速度的量程为【-4,4】,角速度为【-2000,2000】,所以引入这样一个矩阵 dealzero=(4,4,4,2000,2000,2000)*32,所有的数据都与它除,就能将数据点,归到【-1,1】之间了。这里还发现一个问题,少量的加速度到了4这个值了,也就是说投球过程中,已经超出量程了,感觉【-4,4】这个量程不是太合适了。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/04/21 14:25
# @Author  : aramy
# @File    : nano33    deal_trandate.py
#处理训练数据
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow.keras as keras
dealzero=(4,4,4,2000,2000,2000)*32
def processDate(file,y_val):
    openD = pd.read_csv(file, header=None)
    dataX = np.array(openD)
    if dataX.shape[1]>192:
        dataX=dataX[:,:-1]
    dataX=dataX/dealzero
    dataY=np.ones((dataX.shape[0],))*y_val
    return dataX,dataY

def loaddata():
    dataX0,dataY0 = processDate('../res/succ.csv',0)
    dataX1,dataY1 = processDate('../res/fail.csv', 1)

    dataX = np.concatenate((dataX0, dataX1), axis=0)
    dataY=np.append(dataY0,dataY1)
    # print(dataX,dataY)
    permutationTrain = np.random.permutation(dataX.shape[0])
    # # print(permutationTrain)
    dataX = dataX[permutationTrain]
    dataY = dataY[permutationTrain]
    vfoldSize = int(dataX.shape[0] / 100 * 20)
    xTest = dataX[0:vfoldSize]
    yTest = dataY[0:vfoldSize]
    xTrain = dataX[vfoldSize:dataX.shape[0]]
    yTrain = dataY[vfoldSize:dataY.shape[0]]

    return xTest,yTest,xTrain,yTrain


if __name__ == '__main__':
    xTest,yTest,xTrain,yTrain=loaddata()
    print( xTest,yTest,xTrain,yTrain)

    model = keras.Sequential()
    model.add(keras.layers.Dense(32, input_shape=(6*32,), activation='relu'))
    model.add(keras.layers.Dense(16, activation='relu'))
    model.add(keras.layers.Dense(2, activation='softmax'))

    adam = keras.optimizers.Adam(0.000005)
    model.compile(loss='sparse_categorical_crossentropy', optimizer=adam, metrics=['sparse_categorical_accuracy'])
    model.summary()

    history = model.fit(xTrain, yTrain, batch_size=1, validation_data=(xTest, yTest), epochs=3000, verbose=1)
    converter = tf.lite.TFLiteConverter.from_keras_model(model)
    tflite_model = converter.convert()

    open("model", "wb").write(tflite_model)

    训练模型将归一后的数据用随机数的方式打乱,保证成功和用力过猛的数据均匀混合,取80%做为训练数据,20%做为验证数据。
    我们使用的是Sequential模型,Sequential模型形象地解释就是一个漏斗,数据只能从上层往下层流动,我们可以在这个漏斗中堆叠各种计算的层,以丰富这个模型的结构。这里使用了三层的神经网络。使用softmax激活函数。这部分自己还不是太懂,只能是照猫画虎。使用0.000005做为损失函数,训练3000次。在我的电脑上,大概是5分钟,训练完成,得到一个model的文件。使用命令:xxd -i model >> model.h 就生成了Arduino下能用的model.h文件了。

    使用训练结果:训练完成后,得到的model.h文件就要需要的模型了。我的理解是在Arudino收集到的运动数据来和模型作比较,看动作数据与之前训练中的特征值相似程度,用来判断当前动作的属性。在Arduino中增加tensor处理的框架,保证每次三轴加速度和超过35就触发tensor的判断,判断当前动作投篮是否成功。若投篮成功概率超过0.8,则在oled屏幕上显示“success”。若投篮用力过大的概率超过0.8,则显示“Too Hard”。将投篮次数、各自的概率显示在oled屏幕上。

#include <Arduino_LSM9DS1.h>
#include "Buff.h"
#include <TensorFlowLite.h>
#include <tensorflow/lite/micro/all_ops_resolver.h>
#include <tensorflow/lite/micro/micro_error_reporter.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include <tensorflow/lite/version.h>
#include "model.h"
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, NULL);


float ax, ay, az, gx, gy, gz;
float buff[actionnumber * 6];
uint32_t tim[actionnumber];
uint32_t rectime = 0;
const float zero[3] = {0.12620776, -0.360400501, -0.518010013};   //归零
Buff actionBuf;

tflite::MicroErrorReporter tflErrorReporter;
tflite::AllOpsResolver tflOpsResolver;
const tflite::Model* tflModel = nullptr;
tflite::MicroInterpreter* tflInterpreter = nullptr;
TfLiteTensor* tflInputTensor = nullptr;
TfLiteTensor* tflOutputTensor = nullptr;
constexpr int tensorArenaSize = 8 * 1024;
byte tensorArena[tensorArenaSize];
const char* ACTION[] = {
  "SUCCESS",
  "TOO HARD"
};
#define ACT_NUM (sizeof(ACTION) / sizeof(ACTION[0]))
float answer[sizeof(ACTION) / sizeof(ACTION[0])];
int action_num=0;         //投篮次数

void setup() {
  Serial.begin(115200);
  tflModel = tflite::GetModel(model);
  if (tflModel->version() != TFLITE_SCHEMA_VERSION) {
    Serial.println("Model schema mismatch!");
    while (1);
  }
  tflInterpreter = new tflite::MicroInterpreter(tflModel, tflOpsResolver, tensorArena, tensorArenaSize, &tflErrorReporter);
  tflInterpreter->AllocateTensors();
  tflInputTensor = tflInterpreter->input(0);
  tflOutputTensor = tflInterpreter->output(0);
  delay(1000);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);    //初始化oled
  display.clearDisplay();
  display.display();
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  delay(3000);

}

void loop() {
  if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable() ) {
    IMU.readAcceleration(ax, ay, az);
    IMU.readGyroscope(gx, gy, gz);
    actionBuf.insertnode(gx, gy, gz, ax, ay, az, millis());
    if (actionBuf.isavailable()) {
      actionBuf.getActionVal(buff, tim);
      if (millis() - rectime > 1000) {
        rectime = millis();
        judgeAcion();     //判断是否投中
        action_num++;
        dispactioninfo();    //显示信息
      }
    }
  }
}
//将收到的数据作推理,判断是否能进球
void judgeAcion() {
  float max = 0;
  char pos;
  //数据归一
  for (int i = 0; i < actionnumber; i++) {
    tflInputTensor->data.f[i * 6 + 0] = buff[i * 6 + 0] / 4;
    tflInputTensor->data.f[i * 6 + 1] = buff[i * 6 + 1] / 4;
    tflInputTensor->data.f[i * 6 + 2] = buff[i * 6 + 2] / 4;
    tflInputTensor->data.f[i * 6 + 3] = buff[i * 6 + 3] / 2000;
    tflInputTensor->data.f[i * 6 + 4] = buff[i * 6 + 4] / 2000;
    tflInputTensor->data.f[i * 6 + 5] = buff[i * 6 + 5] / 2000;
  }
  TfLiteStatus invokeStatus = tflInterpreter->Invoke();
  if (invokeStatus != kTfLiteOk) {
    Serial.println("Invoke failed!");
    while (1);
    return;
  }
  for (int i = 0; i < ACT_NUM; i++) {
    if (max < tflOutputTensor->data.f[i]) {
      max = tflOutputTensor->data.f[i];
      pos = i;
    }
    Serial.print(ACTION[i]);
    Serial.print(": ");
    Serial.print(tflOutputTensor->data.f[i], 6);
    Serial.print("   ");
    answer[i]=tflOutputTensor->data.f[i];
  }
  Serial.println();
}

//显示结果信息
void dispactioninfo(){
  display.clearDisplay(); //清空屏幕
  display.setCursor(0, 0); //设置起点
  display.setTextSize(2); //设置字体
  display.setTextColor(SSD1306_WHITE); //设置字体颜色
  display.println(F("NO."));
  display.setCursor(60, 0); //设置起点
  display.println(action_num);    //显示次数
  display.setTextSize(3); //设置字体
  display.setCursor(0, 24); //设置起点
  if(answer[0]>0.8){//投篮成功
    display.println("SUCCESS");
  }else if(answer[1]>0.8){
    display.println("TOO HARD");
  }else{
    display.println("FAIL");
  }
  display.setTextSize(1); //设置字体
  display.setCursor(0, 48); //设置起点
  display.print(answer[0],3);
  display.print(" , ");
  display.print(answer[1],3);
  display.display(); //刷新屏幕
  delay(3000);
}

    在实际测试中,效果并不理想,很多动作都会被判断为投篮成功。对整个过程复盘,感觉几个地方会有问题:1、加速度量程应该有点偏小,用到【-8g,8g】不知道效果会不会好点。2、采集动作时间长度不够,使用32个动作点,每个动作点之间按9ms计算,才不到300ms。感觉偏小。3、需要更多的训练样本,而且最好是实际中产生的训练样本。但是采集样本成本不低,如果大量采集数据,还走串口感觉很不方便,需要学习蓝牙传输数据。4、准备还是不够充分。测试中出现了很多问题,USB线松动,魔术贴粘住米奇,开发板脱落……还是需要做好准备。

    这次活动学习到了时下最流行的机器学习,这个方法对解决问题换了个全新的视角。自己水平实在有限,希望活动能继续,能够了解更多的机器学习的知识。

附件下载
arduino.zip
下位机程序
测试数据.zip
收集的投篮测试数据
deal_basketball.py
训练程序
团队介绍
团队成员
aramy
单片机业余爱好者,瞎捣鼓小能手。
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号