2025年1月

大语言模型具备从文字中推断情感和主题的能力。这种能力可用于获知客户对产品评价的情感、新闻或媒体文章的主题或倾向等。大语言模型的这种推断能力可被应用于舆情分析等场景。
推断可以看作是模型接收文本作为输入并执行某种分析的过程,其中涉及提取标签、提取实体、情感分析等。如果想要从一段文本中提取正面或负面情感,在传统的机器学习工作流程中,需要收集标签数据集、训练模型、确定如何在云端部署模型并进行推断。传统机器学习的这种做法也能达到还不错的效果,但是显然这个过程需要大量工作,而且对于每个不同任务,如情感分析、提取实体等,都需要分别训练和部署单独的模型。
大语言模型颠覆了这类任务的实现方式,我们只需要编写并提交一个提示就可以获得结果,而不需要进行大量繁琐的工作。得益于此,相关应用程序开发的效率大幅提升,而且我们还可以只使用一个模型结合API 来执行许多不同的任务,而不再需要训练和部署多个不同的模型。
例如对于电商网站的客户评价,我们可以利用推断来评估客户对于本次购物的情感体验。
以下用三个反引号分隔的产品评论的情感是什么?
```
包装很细心,盒子很精致,之前用水妍,这次换气韵生,水润不油腻,吸收也很好,这个季节,我比较爱出油,这个刚好可以,滋润的同时也不油腻,这款水乳蛮适合我的,很清爽,个人感觉也适合干性皮肤。基础的补水保湿够,使用感也不错,不会油腻。后真的很喜欢送小样,而且送的毫升数还不少。出门旅行或试用都很足够。春天时XX自营买过一套,用着挺好的,比较保湿还跟清爽。刚刚用完小天气丹,入冬了想着豁出去试试天气丹。有点贵没敢网购,让我姐出国带了,相比这个,天气丹很滋润,有一点点油,不过挺好吸收的,不会泛油光,更适合秋冬。这次趁做活动立马又囤了一套,明年夏天用。包装完好,东西齐全,生产日期新,到22年8月。第一次用感觉有点花露水的味道,用一段又会觉得香味挺清新的。乳液很容易涂抹,吸收很快保湿不错,第二天早晨花花嫩嫩的。目前XX自营买的东西都很满意,希望一直这样下去,保证正品!
```


我们看到模型先是给出了情感态度的结论,然后列举了判断的理由。如果需要在应用程序中继续处理判断出的情感结论,可能需要模型只是给出一个简洁的答案,此时我们可以明确要求模型以限定的词汇反馈,例如 以“正面” “负面”或“中性” 的形式给出答案。
以下示例基于节省篇幅的考虑,提示均以迭代的形式提出,在实际应用中,可以直接在一次提示中明确要求,直接获得期望的反馈。

情感在很多情况下仅以“正面”“负面”或“中性”评价是不够的,或许我们需要更精确地识别情感,此时可以要求模型以多个词汇推断文字所表达的情感。

或许在某些场景下,我们仅需要判断一段文字是否表达了某种特定的情感,此时也可以要求模型以简洁的判断结果返回,以便应用程序可以统计或继续处理。

推断不仅可以判断情感,也可以从文字中提取特定的信息,例如我们想知道上述评价涉及的物品名称、品牌、有效期等信息,可以尝试通过以下提示获得。
*从以下用三个反引号分隔的产品评价中识别以下信息:

  1. 评论者购买的物品名称
  2. 该物品的品牌
  3. 该物品的保质期
    请将你的响应以JSON格式输出,且以“物品”“品牌”和“保质期”为键。如果信息不存在,请使用“未知”作为值。
    你的回复尽可能简洁。
    ```[评价内容同以上提示]```*
    这段提示重新给出了评价文本,使用迭代时,模型会从历史会话中识别此次提示的上下文,但基于计算成本原因模型参考的历史会话长度是有限的,因此为了获得更好的效果,迭代多次以后,建议重新开启一个会话继续。上述提示模型给出的反馈如下图。

背景信息

2024-12-21 11:58:11 (UTC)

  1. 准备交易:
    https://app.blocksec.com/explorer/tx/eth/0x72a252277e30ea6a37d2dc9905c280f3bc389b87f72b81a59aa8f50baebd8eaa

2025-01-04 11:59:23 (UTC)

  1. 攻击交易 1:
    https://app.blocksec.com/explorer/tx/eth/0x6439d63cc57fb68a32ea8ffd8f02496e8abad67292be94904c0b47a4d14ce90d
  2. 攻击交易 2:
    https://app.blocksec.com/explorer/tx/eth/0xf1a494239af59cd4c1d649a1510f0beab8bb78c62f31e390ba161eb2c29fbf8b
  3. 攻击交易 3:
    https://app.blocksec.com/explorer/tx/eth/0x09b26b87a91c7aea3db05cfcf3718c827eba58c0da1f2bf481505e0c8dc0766b

漏洞合约:
https://vscode.blockscan.com/ethereum/0x5d16b8ba2a9a4eca6126635a6ffbf05b52727d50

sorraStaking
项目是一个质押奖励项目,用户质押 SOR 代币并锁定一段时间,解锁后获取 SOR 代币作为奖励。

Trace 分析

准备交易

在 2024-12-21 11:58:11 (UTC),攻击者进行了一笔
deposit
操作。

image

攻击交易

由于三笔攻击交易类似,所以只分析其中一笔。


deposit
操作的 13 天后,攻击者进行了后续的攻击操作。攻击者反复调用
withdraw
函数,虽然每次只取回
_amount = 1
的代币,但是能够获得大量的奖励代币。

image

然后将获得的 SOR 代币进行出售,最后支付 bundler 的费用。

image

代码分析

当用户执行
deposit
进行质押时,合约收取质押代币,并记录仓位

image

deposite → _updatePosition → _increasePosition

_increasePosition
函数记录质押的数量,时间和利率等相关信息。

image


withdraw
函数中,会先计算用户的奖励代币数量
rewardAmount
,然后将
(_amount + rewardAmount)
一同发给用户。

image

通过
_calculateRewards
函数来计算奖励金额。

image

代码问题:

  1. 每次所领取的奖励金额都是按照用户质押的总金额来计算的,而不是当前取款数额对应的奖励金额。
  2. withdraw
    函数可以将质押金额进行分批提取。

所以攻击者通过多次调用
withdraw
函数来赎回部分质押代币,却每次都能获取到对应所有质押金额的奖励代币,最终获得超额的收益。

一、概述

Spring AI

Spring
官方社区项目,旨在简化
Java AI
应用程序开发,让
Java
开发者像使用
Spring
开发普通应用一样开发
AI
应用。

Spring Cloud Alibaba AI
是一个将
Spring Cloud
微服务生态与阿里巴巴
AI
能力无缝集成的框架,帮助开发者快速构建具备
AI
功能的现代化应用。本文将介绍
Spring Cloud Alibaba AI
的基本概念、主要特性和功能,并演示如何完成一个
在线聊天

在线画图

AI
应用。

二、主要特性和功能

Spring Cloud Alibaba AI
目前基于
Spring AI 0.8.1
版本 API 完成通义系列大模型的接入。通义接入是基于阿里云
阿里云百炼
服务;而
阿里云百炼
建立在
模型即服务(MaaS)
的理念基础之上,围绕
AI
各领域模型,通过标准化的
API
提供包括模型推理、模型微调训练在内的多种模型服务。

主要提供以下核心功能:

2.1. 简单易用的集成

通过
Spring Boot
风格的自动配置机制,开发者只需少量代码配置,即可快速接入阿里云的
AI
服务。

2.2. 丰富的 AI 服务支持

支持以下核心能力:

  • 自然语言处理(NLP)
    :文本分析、智能问答、翻译。
  • 计算机视觉(CV)
    :图像生成、图像识别、目标检测。
  • 语音处理
    :语音识别、语音合成。
  • 数据分析与预测
    :数据建模、趋势分析。

2.3. 高度扩展性

通过配置中心和注册中心(如 Nacos)实现动态扩展,支持微服务架构的扩展需求。
提供接口定义,方便接入第三方
AI
平台。

三、构建 AI 应用

Spring Cloud Alibaba AI 对 Java 版本有要求,所以需要提前预装好 Java 17 环境。

3.1. 申请 API-KEY

登录阿里云,进入
阿里云百炼
的页面:

https://bailian.console.aliyun.com/?apiKey=1#/api-key

创建自己的
API-KEY

3.2. 添加依赖


Spring Boot
项目的
pom.xml
中添加
alibaba-ai
依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-ai</artifactId>
</dependency>

<repositories>
    <repository>
        <id>alimaven</id>
        <url>https://maven.aliyun.com/repository/public</url>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-snapshots</id>
        <url>https://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

3.3. 配置 API-KEY


application.yml
中配置 Kafka 的相关属性,包括服务器地址、认证信息等。

spring:
  cloud:
    ai:
      tongyi:
        connection:
          api-key: sk-xxxxxx
  • api-key
    配置在阿里云百炼里申请的api-key

3.4. 创建模型调用服务

@Service
@Slf4j
public class TongYiSimpleService {
    @Resource
    private TongYiChatModel chatClient;
    @Resource
    private TongYiImagesModel imageClient;

    public String chat(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return chatClient.call(prompt).getResult().getOutput().getContent();
    }

    public String image(String message) {
        ImagePrompt prompt = new ImagePrompt(message);
        Image image = imageClient.call(prompt).getResult().getOutput();
        return image.getB64Json();
    }
}

聊天和图片的服务,分别通过注入
TongYiChatModel

TongYiImagesModel
对象来实现,屏蔽底层通义大模型交互细节。

3.5. 创建controller

@RestController
@RequestMapping("/ai")
public class TongYiController {
    @Resource
    private TongYiSimpleService tongYiSimpleService;

    @GetMapping("/chat")
    public String chat(@RequestParam(value = "message") String message) {
        return tongYiSimpleService.chat(message);
    }

    @GetMapping("/image")
    public ResponseEntity<byte[]> image(@RequestParam(value = "message") String message) {
        String b64Str = tongYiSimpleService.image(message);
        byte[] imageBytes = Base64.getDecoder().decode(b64Str);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_JPEG);
        return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);
    }
}

3.6. 测试效果

3.6.1. 聊天接口

在浏览器输入:
http://localhost:8009/ai/chat?message=你是谁

3.6.2. 图片接口

在浏览器输入:
http://localhost:8009/ai/image?message=意大利面拌42号混凝土

3.6.3. 搭配聊天页面

四、总结

当前版本的
Spring Cloud Alibaba AI
主要完成了几种常见生成式模型的适配,涵盖对话、文生图、文生语音等。在未来的版本中将继续推进
VectorStore

Embedding

ETL Pipeline

RAG
等更多
AI
应用开发场景的建设。

完整的样例代码下载:
https://gitee.com/zlt2000/spring-cloud-ai-sample

AES对称加密

AES(Advanced Encryption Standard),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

高级加密标准(AES,Advanced Encryption Standard)是密码学中的高级加密标准,AES为分组加密法,把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文,在AES标准规范中,分组长度只能是128位,AES是按照字节进行加密的,也就是说每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。这导致密钥长度不同,推荐加密的轮数也不同。

  • 明文P

没有经过加密的数据

  • 密钥K

用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据

  • AES加密函数

设AES加密函数为E,则 C = E(K, P),其中P为明文,K为密钥,C为密文。也就是说,把明文P和密钥K作为加密函数的参数输入,则加密函数E会输出密文C

  • 密文C

经加密函数处理后的数据

  • AES解密函数

设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P

Python Crypto 加密库

# python不同版本不一样
pip install pycryptodome   (python3.7.3)

pip install cryptography    (python3.12.2)

python3.7.3 版本

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad


def encrypt_config(config_file, encrypted_file):
    """加密配置文件"""

    # 生成一个 16 字节的随机密钥
    key = get_random_bytes(16)
    print("Key: ", key)

    # 读取配置文件
    with open(config_file, 'rb') as f:
        plaintext = f.read()

    # 加密
    cipher = AES.new(key, AES.MODE_CBC)
    ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))

    with open(encrypted_file, 'wb') as f:
        f.write(cipher.iv)
        f.write(ciphertext)
    print(f'File encrypted: {encrypted_file}')


def encrypt_config_one_key(config_files, encrypted_files):
    """加密配置文件,多个文件用同一个密钥"""

    if not isinstance(config_files, list) or not isinstance(encrypted_files, list):
        raise TypeError('config_files and encrypted_files must be list')

    # 生成一个 16 字节的随机密钥
    key = get_random_bytes(16)
    print("Key: ", key)

    config_add_key(config_files[0], encrypted_files[0], key)
    config_add_key(config_files[1], encrypted_files[1], key)
    return


def config_add_key(config_file, encrypted_file, key):

    # 读取配置文件
    with open(config_file, 'rb') as f:
        plaintext = f.read()

    # 加密
    cipher = AES.new(key, AES.MODE_CBC)
    ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))

    with open(encrypted_file, 'wb') as f:
        f.write(cipher.iv)
        f.write(ciphertext)
    print(f'File encrypted: {encrypted_file}')


def decrypt_config(encrypted_file, key):
    """解密配置文件"""
    # 解密
    with open(encrypted_file, 'rb') as f:
        iv = f.read(16)
        encrypted_data = f.read()

    cipher = AES.new(key, AES.MODE_CBC, iv)
    config_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)

    print(f'解密后的数据: {config_data.decode()}')



if __name__ == '__main__':
    # file1 = "/home/pi/printer_data/config/printer.cfg"
    # file2 = "/home/pi/printer_data/config/flsun_func.cfg"
    # target1 = "/home/pi/printer_data/config/test/printer_test.cfg"
    # target2 = "/home/pi/printer_data/config/test/flsun_func.cfg"
    #
    # # encrypt_config(file1, target1)
    # # encrypt_config(file2, target2)
    # key1 = b'.)7[\x8c\xab\x12Y\xd6tm\x06\xe2\xd1l\xb5'
    # key2 = b'\xf9\x19Uf\x11\xe9q\x0ci\x87\xe32%\xf8:W'
    # # decrypt_config(target1, key1)
    # decrypt_config(target2, key2)
    config_files = ["/home/pi/printer_data/config/printer.cfg", "/home/pi/printer_data/config/flsun_func.cfg"]
    target_files = ["/home/pi/printer_data/config/test/printer_test.cfg", "/home/pi/printer_data/config/test/flsun_func.cfg"]

    # encrypt_config_one_key(config_files, target_files)

    key = b'F.r\xeaH\xfb\xf5\x9d\xe9_\x1b\xba\rW`\xaa'

    # decrypt_config(target_files[0], key)
    decrypt_config(target_files[1], key)

python3.12.2 版本


import pickle
import configparser

from cryptography.fernet import Fernet
import os

def cfg2bin(file, result_file):
    # 读取配置文件
    config = configparser.ConfigParser()
    config.read(file)
    # 将配置文件对象序列化为二进制文件
    with open(result_file, 'wb') as f:
        pickle.dump(config, f)


def encrypt_config(config_file, encrypted_file):
    """加密配置文件"""
    # 生成密钥
    key = Fernet.generate_key()
    print("Key: ", key)

    # 读取配置文件内容
    with open(config_file, 'r') as f:
        config_data = f.read()

    # 使用密钥加密配置数据
    fernet = Fernet(key)
    encrypted_data = fernet.encrypt(config_data.encode())

    # 写入加密后的文件
    with open(encrypted_file, 'wb') as f:
        f.write(encrypted_data)

    print(f'配置文件已加密并保存为: {encrypted_file}')


def decrypt_config(encrypted_file, key):
    """解密配置文件"""
    # 读取加密后的文件
    with open(encrypted_file, 'rb') as f:
        encrypted_data = f.read()

    # 使用密钥解密配置数据
    fernet = Fernet(key)
    config_data = fernet.decrypt(encrypted_data).decode()

    print(f'解密后的数据: {config_data}')


if __name__ == '__main__':
    file1 = "/home/cln/printer_data/config/printer.cfg"
    file2 = "/home/cln/printer_data/config/mainsail.cfg"
    target1 = "/home/cln/printer_data/config/test/printer_test.cfg"
    target2 = "/home/cln/printer_data/config/test/mainsail.cfg"

    # encrypt_config(file1, target1)
    # encrypt_config(file2, target2)
    key1 = b'FN8eGSYuXtK2K7-X2jUcm6r1FK16PAzdTMOpg6wyLGQ='
    key2 = b'dbBmLzpyO20jqphgQsIbCPxIpNAG-q7s6E8rxu3K4Wo='
    decrypt_config(target1, key1)
    # decrypt_config(target2, key2)

加密效果

涉及模块

  • moonraker 动态修改配置文件模块
  • klippy 读取配置模块

挤出机配置剥离

  • 在打印机初始化加载完配置文件后,只针对挤出机的参数进行赋值,达到弃用配置文件里的参数值
  • 挤出机配置参数赋值实现,可以创建一个常量类(放在util目录下),或者单独创建一个挤出机的ini配置,放在klippy目录下
  • 或者在toolhead模块加载挤出机对象之前进行挤出机配置参数赋值

  • 配置文件参数

  • extruder 用到的所有参数值信息

前言

一个WIFI上位机,接收底层MPU6050数据,途中转蓝牙从机透传,到蓝牙主机直连WIFI,PC端UDP通信,实现三轴加速度数据传送和计步功能。

项目介绍

本项目基于.NET平台,使用WPF开发了一个应用程序,用于实现MPU6050传感器数据从蓝牙模块传输到主机,并通过WiFi以UDP协议接收这些数据并进行可视化展示。

具体而言,MPU6050作为从设备通过蓝牙连接到主控设备(蓝牙主机),再由主控设备经由WiFi网络将数据以UDP包的形式发送至服务器或客户端进行处理和图形化显示。

项目运行环境

1、开发平台

项目基于.NET Framework,采用 WPF 开发用户界面。

2、集成开发环境

Visual Studio 2019,确保已安装 C# 开发工作负载以支持项目开发与运行。

3、启动项目

获取源代码后,通过打开 BluetoothPC.sln 解决方案文件即可。确保所有依赖项正确配置后,直接运行解决方案,应用程序应能正常启动。

4、用户界面特色

应用程序配备了一个直观且美观的用户界面,图标设计精心,提供优秀的用户体验和视觉享受。

项目设计流程

1、设计框架

前台XAML的UI设计不过多介绍,主要看设计需求,逃不开模板、触发器、动画、样式之类的技术。

后台C#在UI主线程下开了三个子线程:

1、UDP数据监听接收线程。

2、三轴数据UI更新线程。

3、计步和进度条数据更新线程。

因没有碰到多个线程访问同一个UI控件或写同一个UI控件,所以没用到锁,但内部加了异步延迟,让UI更新顺滑一些。

2、服务器连接设计

UDP端IP地址和端口号需正常且有效,加了错误弹窗,若出现下图请重新输入:

3、三轴加速度显示

有硬件:成功连接上之后需配合底层硬件,这里是接收UDP发过来的3轴加速度值。

无硬件:如果没有硬件也行,自行找个网络调试助手,开个UDP服务,本机连接就行,发送的数据需包含以下格式:

任意字符(:1.23938 mG)任意字符。

解释:

发送过来的数据必须包含在 :xxxx mG 内,冒号和mG不能少,可任意多组,每组代表一轴数据。

4、计步显示

利用三轴加速度提供的数据处理步数。需打开左下角计步控制按钮。

打开后弹出提示:

本计步算法仅支持手臂摆动的峰峰值计步,若有更好的算法请分享,万分感谢!!!

计步程序如下

/** 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<double> tempValue = new List<double>();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;//步数复制 /// <summary> ///监测新的步数 如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步///符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中/// </summary> /// <param name="_values">加速传感器三轴的平均值</param> 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;
}
}
/// <summary> ///监测波峰///以下四个条件判断为波峰///1.目前点为下降的趋势:isDirectionUp为false///2.之前的点为上升的趋势:lastStatus为true///3.到波峰为止,持续上升大于等于2次///4.波峰值大于minValue,小于maxValue///记录波谷值///1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值///2.所以要记录每次的波谷值,为了和下次的波峰作对比/// </summary> /// <param name="_newValue"></param> /// <param name="_oldValue"></param> /// <returns></returns> 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;
}
}
/// <summary> ///阈值的计算///1.通过波峰波谷的差值计算阈值///2.记录4个值,存入tempValue[] 数组中///3.在将数组传入函数averageValue中计算阈值/// </summary> /// <param name="_value"></param> /// <returns></returns> public double peak_Valley_Thread(double_value)
{
double _tempThread =ThreadValue;
List
<double> _tempValue = new List<double>(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;
}
/// <summary> ///梯度化阈值///1.计算数组的均值///2.通过均值将阈值梯度化在一个范围里///这些数据是通过大量的统计得到的/// </summary> /// <param name="_value"></param> /// <returns></returns> public double averageValue(List<double>_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;
}
}

计步效果如下所示:

开启步数控制按钮后总步数累加,进度条进度为50步,每到达50步距离弹出提示框,计步结束也弹提示框通知。

5、倾力UI按钮设计

设计了一组拟物化按钮,目前无任何功能,有需要的小伙伴自行更改设计功能。

项目地址

Gitee:
https://gitee.com/tytokongjian/StepCountingUpperPC

总结

以上仅展示了蓝牙转WIFI计步上位机的部分功能。更多实用特性和详细信息,请大家访问项目地址。

希望通过本文能为上位机机开发方面提供有价值的参考。欢迎在评论区留言交流,分享您的宝贵经验和建议。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!