# 蓝牙转WIFI计步上位机 **Repository Path**: tytokongjian/StepCountingUpperPC ## Basic Information - **Project Name**: 蓝牙转WIFI计步上位机 - **Description**: 这是一个WIFI上位机,接收底层MPU6050数据,途中转蓝牙从机透传,到蓝牙主机直连WIFI,PC端UDP通信,实现三轴加速度数据传送和计步功能。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 18 - **Forks**: 9 - **Created**: 2021-06-17 - **Last Updated**: 2025-09-12 ## Categories & Tags **Categories**: hardware **Tags**: None ## README

蓝牙转WIFI计步上位机

### 1. 设计简介 本上位机采用.NET平台下的WPF实现一个MPU6050数据从蓝牙从机——>蓝牙主机——>WIFI的UDP接收数据传输并可视化的功能。 ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617150217.jpg) #### 1.1 运行环境 .Net平台下的WPF。VS2019(需安装C#支持的环境)。拿到源码打开 BluetoothPC.sln 运行正常即可。 设计了一个很好看的UI图标:) ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617151415.PNG) ### 2. 设计流程 #### 2.1 设计框架 前台XAML的UI设计不过多介绍,主要看设计需求,逃不开模板、触发器、动画、样式之类的技术。 后台C#在UI主线程下开了三个子线程: 1. UDP数据监听接收线程。 2. 三轴数据UI更新线程。 3. 计步和进度条数据更新线程。 因没有碰到多个线程访问同一个UI控件或写同一个UI控件,所以没用到锁,但内部加了异步延迟,让UI更新顺滑一些。 #### 2.2 服务器连接设计 UDP端IP地址和端口号需正常且有效,加了错误弹窗,若出现下图请重新输入: ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617152230.jpg) ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617152334.PNG) #### 2.3 三轴加速度显示 有硬件:成功连接上之后需配合底层硬件,这里是接收UDP发过来的3轴加速度值。 无硬件:如果没有硬件也行,自行找个网络调试助手,开个UDP服务,本机连接就行,发送的数据需包含以下格式: * 任意字符(**:1.23938 mG**)任意字符。 解释:发送过来的数据必须包含在 **:xxxx mG** 内,**冒号**和**mG**不能少,可任意多组,每组代表一轴数据。 ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617153317.PNG) #### 2.4 计步显示 利用三轴加速度提供的数据处理步数。需打开左下角计步控制按钮。打开后弹出提示: ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617153532.PNG) ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617153604.PNG) 本计步算法仅支持手臂摆动的峰峰值计步,若有更好的算法请分享,万分感谢!!! 计步程序如下 ```c# /* * valueNum - 存放三轴数据(x,y,z)的个数 * tempValue - 用于存放计算阈值的波峰波谷差值的数组(在这个方法里存放值数组长度为5) * isDirectionUp - 是否上升的标志位 * continueUpCount - 持续上升的次数 * continueUpFormerCount - 上一点的持续上升的次数,为了记录波峰的上升次数 * lastStatus - 上一点的状态,上升还是下降 * peakOfWave - 波峰值 * valleyOfWave - 波谷值 * timeOfThisPeak - 此次波峰的时间 * timeOfLastPeak - 上次波峰的时间 * timeOfNow - 当前的时间 * gravityOld - 上次传感器的值 * initialValue - 动态阈值需要动态的数据,这个值用于这些动态数据的阈值,这个值是由大量数据得来的 * ThreadValue - 初始阈值,这个值是由大量数据得来的 * minValue - 初始最小值 计算出来的xyz数值乘重力加速度(9.8),此为手机拿在手里(不摆臂)(由自己多次测试得出的值) * maxValue - 初始最大值 自己设定的最大值(我们定位2)乘重力加速度(9.8),此为手机拿在手里(不摆臂)(由自己多次测试得出的值) * g - 重力加速度(9.8) * thisSteps 步数 */ private int valueNum = 5; //private double[] tempValue; private List tempValue = new List(); private Boolean isDirectionUp = false; private int continueUpCount = 0; private int continueUpFormerCount = 0; private Boolean lastStatus = false; private double peakOfWave = 0; private double valleyOfWave = 0; private double timeOfThisPeak = 0; private double timeOfLastPeak = 0; private double timeOfNow = 0; private double gravityOld = 0; private double initialValue = 1.7; private double ThreadValue = 2.0; private double minValue = 11; private double maxValue = 19.6; private double g = 9.8; private double thisSteps = 0; //当前步数 private double StepsCopy = 0; //步数复制 /// /// 监测新的步数 如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步 /// 符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中 /// /// 加速传感器三轴的平均值 public void detectorNewStep(double _values) { if (gravityOld == 0) { gravityOld = _values; } else { if (detectorPeak(_values, gravityOld)) { timeOfLastPeak = timeOfThisPeak; timeOfNow = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds); //时间差大于200ms,小于2s if (((timeOfNow - timeOfLastPeak) >= 200) && ((timeOfNow - timeOfLastPeak) <= 2000) && ((peakOfWave - valleyOfWave) >= ThreadValue)) { timeOfThisPeak = timeOfNow; //增加步数 thisSteps++; //增加步数复制 StepsCopy++; } if(((timeOfNow - timeOfLastPeak) >= 200) && ((peakOfWave - valleyOfWave) >= initialValue)) { timeOfThisPeak = timeOfNow; double _diffWaveVal = peakOfWave - valleyOfWave; ThreadValue = peak_Valley_Thread(_diffWaveVal); } } gravityOld = _values; } } /// /// 监测波峰 /// 以下四个条件判断为波峰 /// 1.目前点为下降的趋势:isDirectionUp为false /// 2.之前的点为上升的趋势:lastStatus为true /// 3.到波峰为止,持续上升大于等于2次 /// 4.波峰值大于minValue,小于maxValue /// 记录波谷值 /// 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值 /// 2.所以要记录每次的波谷值,为了和下次的波峰作对比 /// /// /// /// public Boolean detectorPeak(double _newValue, double _oldValue) { lastStatus = isDirectionUp; if (_newValue >= _oldValue) { isDirectionUp = true; continueUpCount++; } else { continueUpFormerCount = continueUpCount; continueUpCount = 0; isDirectionUp = false; } if (!isDirectionUp && lastStatus && (continueUpFormerCount >= 2 && (_oldValue >= minValue && _oldValue < maxValue))) { //满足上面波峰的四个条件,此时为波峰状态 peakOfWave = _oldValue; return true; } else if (!lastStatus && isDirectionUp) { //满足波谷条件,此时为波谷状态 valleyOfWave = _oldValue; return false; } else { return false; } } /// /// 阈值的计算 /// 1.通过波峰波谷的差值计算阈值 /// 2.记录4个值,存入tempValue[] 数组中 /// 3.在将数组传入函数averageValue中计算阈值 /// /// /// public double peak_Valley_Thread(double _value) { double _tempThread = ThreadValue; List _tempValue = new List(tempValue); if (tempValue.Count < valueNum) { tempValue.Add(_value); } else { //tempValue数组长度=valueNum=5 _tempThread = averageValue(tempValue); _tempValue.RemoveAt(0); _tempValue.Add(_value); tempValue = _tempValue; } return _tempThread; } /// /// 梯度化阈值 /// 1.计算数组的均值 /// 2.通过均值将阈值梯度化在一个范围里 /// 这些数据是通过大量的统计得到的 /// /// /// public double averageValue(List _value) { if (_value.Count != 0) { double _ave = 0; foreach (double i in _value) _ave += i; _ave = _ave / _value.Count; if(_ave >= 8) { _ave = 4.3; } else if (_ave >= 7 && _ave < 8) { _ave = 3.3; } else if (_ave >= 4 && _ave < 7) { _ave = 2.3; } else if (_ave >= 3 && _ave < 4) { _ave = 2.0; } else { _ave = 1.7; } return _ave; } else { return 1.7; } } ``` 计步效果如下所示: ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617154243.PNG) 开启步数控制按钮后总步数累加,进度条进度为50步,每到达50步距离弹出提示框,计步结束也弹提示框通知。 #### 2.5 倾力UI按钮设计 设计了一组拟物化按钮,目前无任何功能,有需要的小伙伴自行更改设计功能。 ![](https://tuyong.oss-cn-hangzhou.aliyuncs.com/img/20210617154608.jpg) ### 3. 总结 本上位机是课程设计的一个附属品,有需要的小伙伴拿去改改,望点个star <- .->!