当前位置: 首页 > news >正文

从0开始做yolov5模型剪枝

文章目录

  • 从0开始做yolov5模型剪枝 **·**
    • 1 前言
    • 2 GitHub取源码
    • 3 原理
      • 3.1 原理
      • 3.2 network slimming过程
    • 4 具体实施步骤
      • 4.1 安装虚拟环境
      • 4.2 配置参数
        • 4.2.1 数据集参数
        • 4.2.2 模型结构参数
        • 4.2.3 train.py中的参数
      • 4.3 正常训练
        • 4.3.1 准备
        • 4.3.2 训练及问题解决
      • 4.4 稀疏化训练
        • 4.4.1 参数设置
        • 4.4.2 稀疏化训练与问题
      • 4.5 剪枝
        • 4.5.1 参数设置
        • 4.5.2 剪枝
      • 4.6 finetune剪枝的网络
        • 4.6.1 参数设置
        • 4.6.2 finetune
      • 4.7 循环稀疏训练->剪枝->finetune网络

从0开始做yolov5模型剪枝 ·

1 前言

【整个流程中,在正常train,sparityTrain,prune,finetune遇到10多个的问题,包括AttributeError、ModuleNotFoundError、RuntimeError、SyntaxError、TypeError等问题的解决方法,详见内容】

为了将现有模型移植到ARM平台,同时保证模型准确率的基础上,减少模型的算力消耗和推理时间。

之前有做实验对比了YOLOv5、 YOLOv7、 YOLOv8,结合不同版本模型推理时间和准确率,并查了很多资料,包括大部分人博客描述,结合大部分人经验,我们觉的yolov5的泛化能力较好。故在考虑训练自己的模型且在X86和ARM平台上部署,我们针对yolov5做模型的训练和剪枝,以便轻量化小模型的部署。

当然,我们还需要对最终的模型执行INT8量化的操作,以便降低目标检测的推理时间。

2 GitHub取源码

下载如下路径的源码:

https://github.com/midasklr/yolov5prune/tree/v6.0

本文为 上面GitHub上取6.0的版本做剪枝

3 原理

【根据一些博客/文章对yolov5剪枝的介绍,简单总结一下yolov5模型剪枝的原理】

3.1 原理

原理论文:Learning Efficient Convolutional Networks through Network Slimming

ref: Pruning Filters for Efficient ConvNets( https://arxiv.org/abs/1608.08710 )

ref: https://blog.csdn.net/qq_42835363/article/details/129125376?spm=1001.2014.3001.5501

ref: https://blog.csdn.net/IEEE_FELLOW/article/details/117236025

ref: Yolov5_5.0上做模型剪枝

输入经过BN(Batch Normalization)层获得归一化后的分布。BN层存在两个可训练参数γ(gamma)、β(beta)。

当gamma和betaγ趋于0时,输入相当于乘以了0,此时该channel上的卷积将输出0,这是无意义的。因此,可以认为剔除这样冗余的channel对模型性能没有影响。

普通网络训练时,由于初始化,gamma一般分布在1附近。为了使gamma趋于0,可以通过添加L1正则来约束,使得系数稀疏化。论文中把添加gammaL1正则的训练称为稀疏训练。

稀疏训练后,裁剪掉稀疏很小的层,对应激活也很小,所以对后面的影响非常小,反复迭代这个过程,可以获得小型的模型,步骤如图1。

在这里插入图片描述

图1

3.2 network slimming过程

① 先初始化网络,对BN层的参数添加L1正则并对网络训练。

② 统计网络中的γ(gamma),设置剪枝率对网络进行裁剪。

③ 将裁减完的网络finetune,完成剪枝工作。

4 具体实施步骤

4.1 安装虚拟环境

解压下载的源码,进入yolov5prune_6.0目录下,依次执行下面的操作

# 1 创建虚拟环境
conda create -n yolov5prune
# 2 激活虚拟环境
conda activate yolov5prune
# 3 安装虚拟环境(根据yolov5prune_6.0根目录下的requirements.txt安装)
pip install -r requirements.txt

4.2 配置参数

4.2.1 数据集参数

自己的数据集结构如下

--datasTrain
------images
----------train     	# 存放训练数据集的图片(.jpg)
----------val
----------test
------labels
----------train			# 存放训练图片对应的标签文件(.txt)
----------val
----------tes

在/yolov5prune_6.0/data/目录下,仿照coco128.yaml中的结构创建my_yolov5.yaml文件。其中内容如下

# Train/val/test sets as 
# 1) dir: path/to/imgs, 
# 2) file: path/to/imgs.txt, or 
# 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: /home/user/hlj/MyTrain/datasTrain3_More/  # dataset root dir
train: images/train/  # train images (relative to 'path') 128 images
val: images/val/      # val images (relative to 'path') 128 images
test:  images/test/   # test images (optional)nc: 11  # number of classes
names: ['pedes', 'car', 'bus', 'truck', 'bike', 'elec', 'tricycle', 'coni', 'warm', 'tralight', 'specVehi']

4.2.2 模型结构参数

修改yolov5prune_6.0/models/yolov5s.yaml中的目标检测类型,使其适配自己数据集的目标检测类型数量。如下

nc: 11

4.2.3 train.py中的参数

设置train.py中的参数,主要包括如下:

'--weights', default='./yolov5s.pt'  # 由于我要从头训练,所以注释了此参数
'--cfg', default='./models/yolov5s.yaml'
'--data', default='./data/my_yolov5.yaml'
'--epochs', default=300 		# 由于从头训练,所以epochs值设的比较大
'--batch-size', default=-1
'--imgsz', default=640			# 考虑部署

4.3 正常训练

4.3.1 准备

由于我是SSH链接,所以先创建/打开tmux会话

tmux new -s prunesession

若【先按下ctrl+b,然后再单独按d】退出会话的话,下次再进入会话,需要使用命令

tmux a -t prunesession

进入会话,先进入项目目录下,并激活虚拟环境(若已激活,可忽略)

cd ../yolov5prune_6.0/
source activate yolov5prune

训练结束之后要删除会话

tmux kill-session -t prunesession

4.3.2 训练及问题解决

执行如下命令,进行训练

python3 train.py

【问题1】

运行train.py文件后,报了如下错误

ModuleNotFoundError: No module named 'utils.loggers.wandb'

提示缺包,根据别人的攻略,下载U神对应yolov5_6.0版本的代码,然后把yolov5_6.0\utils\loggers\目录下的整个wandb文件夹拷贝到yolov5prune_6.0\utils\loggers目录下。

【问题2】

重新输入 python3 train.py 后,报如下问题,由此可见,设置train.py中的参数的时候,'–weights’参数不可以被注释掉。

AttributeError: 'Namespace' object has no attribute 'weights'

故将’weights’参数设置如下,表示不使用预训练权重,模型将从头开始训练。

'--weights', default=''

【问题3】

不知道为什么,ubuntu上又报了一个numpy的问题如下,本地运行是没有这个问题的

raise AttributeError(__former_attrs__[attr])
AttributeError: module 'numpy' has no attribute 'int'.

原来是因为 新版本的numpy里面没有np.int,可以修改源码解决。

修改yolov5prune_6.0/utils/ 目录下datasets.py中所有的…astype(np.int) 为 …astype(int),如下所示:

441  bi = np.floor(np.arange(n) / batch_size).astype(int)  # batch index
483  self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(int) * stride
854  b = xywh2xyxy(b.reshape(-1, 4)).ravel().astype(int)

修改yolov5prune_6.0/utils/ 目录下general.py中所有的…astype(np.int) 为 …astype(int),如下所示:

510  classes = labels[:, 0].astype(int)  # labels = [class xywh]
525  class_counts = np.array([np.bincount(x[:, 0].astype(int), minlength=nc) for x in labels])

【问题4】

File "/home/user/hlj/MyTrain/yolov5prune_6.0/utils/loss.py", line 217, in build_targets
indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) 
RuntimeError: result type Float can't be cast to the desired output type long int

参考 ref: https://blog.csdn.net/Thebest_jack/article/details/125649451 执行如下操作:

修改yolov5prune_6.0/utils/ 目录下loss.py源码,

#(1) 183行左右
for i in range(self.nl):anchors, shape = self.anchors[i], p[i].shape   # anchors = self.anchors[i]gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # xyxy gain
#(2)218行后
# indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))  
上一行代码改为如下
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1)))  # image, anchor, grid indices            

【问题5】

epoch的时候,报如下问题

File "..../yolov5prune_6.0/utils/plots.py", line 116, in text
w, h = self.font.getsize(text)  # text width, height
AttributeError: 'FreeTypeFont' object has no attribute 'getsize'

这是因为安装了新版本的 Pillow,pip install tf-models-official删除了该getsize功能
,降级到 Pillow 9.5 解决了该问题。可以尝试以下方法进行解决,参见【问题8】:

pip install Pillow==9.5

【问题6】

epoch 0 结束,val结束后,报如下问题

File ".....\yolov5prune_6.0\utils\callbacks.py", line 77, in runlogger['callback'](*args, **kwargs)
TypeError: on_fit_epoch_end() missing 1 required positional argument: 'fi'

找一下官方源码,把yolov5_6.0/utils/下的整个loggers文件复制进去,应该就行了,可能是版本不一致导致的。

【问题7】

yolov5prune_6.0/utils/general.py line471
return re.sub(pattern="[|@#!?·$€%&()=??^*;:,¨′><+]", repl="_", string=s)
SyntaxError:(unicoda error)'utf-8' code can't decode byte 0xal in position 6: invalid start byte。 

应该是’utf-8’ code不支持的问题,添加了下面的编码格式,但最后也没解决。看了一下对应函数的功能,只是为了清理字符串(用下划线替代特殊字符),所以就直接改了那一行的代码,对整个程序是没有影响的。

# -*- coding: utf-8 -*-

【总结】

虽然各种问题不断,而且【问题5】我也没管它 但是,最后总算是python3 train.py正常跑起来了。

4.4 稀疏化训练

4.4.1 参数设置

对train_sparity.py的参数进行设置

'--st', action='store_true',default=True,
'--sr', type=float, default=0.0001,
'--weights', type=str, default=ROOT / '',
'--cfg', type=str, default='./models/yolov5s.yaml',
'--data', type=str, default='./data/my_yolov5.yaml',
'--epochs', type=int, default=300
'--batch-size', type=int, default=-1,   # 注意【问题8】的发生
'--imgsz', '--img', '--img-size', type=int, default=640,
'--adam', action='store_true', default=True, 

4.4.2 稀疏化训练与问题

执行如下命令,进行稀疏化训练

python train_sparity.py

在这里插入图片描述

【问题8】

loggers.on_params_update({"batch_size": batch_size})
AttributeError: 'Loggers' object has no attribute 'on_params_update'

貌似是autobatch的原因,所以把参数’–batch-size’, type=int, default=-1, 先改为固定值 default=2,此后,可以正常epoch0。但是仍然存在【问题5】的问题,虽然不影响训练,但是觉的还是应该把它解决,毕竟是AttributeError的问题。解决办法如下:

# pillow版本太新的原因,新版的getsize属性被删除掉了。
pip3 uninstall pillow
pip3 nstall pillow==9.5

【问题9】

Epoch0的val结束之后,报了如下问题

File "/home/user/hlj/MyTrain/yolov5prune_6.0/utils/callbacks.py", line 77, in run
logger['callback'](*args, **kwargs)
TypeError: Loggers.on_fit_epoch_end() takes 5 positional arguments but 6 were given

这个问题是由于自己为了解决【问题6】,把项目中的utils/loggers/init .py文件换成了官方的文件了,发现init.py文件中def on_fit_epoch_end(self, vals, bn_weights, epoch, best_fitness, fi)少bn_weights,在这个项目中把prune项目下这个文件重新拷贝一下好了。

4.5 剪枝

4.5.1 参数设置

​ 设置裁剪比例参数,可以从小到大试。注意cfg的模型文件需要和weights对应上,否则会出现运行prune 过程中出现键值不对应的问题,裁剪完成会保存对应的模型pruned_model.pt。

在prune.py文件中,修改如下参数

'--data', type=str, default=ROOT / 'data/my_yolov5.yaml',
'--weights', nargs='+', type=str, default=ROOT / 'runs/train/spaweight/last.pt'
'--cfg', type=str, default='./models/yolov5s.yaml',
'--percent', type=float, default=0.1,
'--batch-size', type=int, default=16, 
'--imgsz', '--img', '--img-size', type=int, default=640,

运行

python prune.py

【问题10】

SyntaxError: Non-UTF-8 code starting with '\xe5' in file /home/user/hlj/MyTrain/yolov5prune_6.0/prune.py on line 400, but no encoding declared; see https://peps.python.org/pep-0263/ for details

解决方法:找到对应的行,发现是注释的内容code的格式的问题,把它删掉或者把中文改成英文即可。

【问题11】

return func(*args, **kwargs)
TypeError: run() got an unexpected keyword argument 'cfg'

解决方式在prune.py的源码run()函数中,增加参数如下

cfg = './model/yolov5s.yaml'

4.5.2 剪枝

对稀疏化训练后的模型best.pt进行剪枝。

若参数已设置好了,直接执行python prune.py

python prune.py

否则,传入的weight为稀疏化训练得到的权重。

python prune.py --weights runs/train/exp_sparity/weights/best.pt --percent 0.5 --cfg models/yolov5s.yaml

裁剪完成会在根目录下保存对应的模型pruned_model.pt。

4.6 finetune剪枝的网络

4.6.1 参数设置

更改finetune_pruned.py的相关参数如下

'--weights', type=str, default=ROOT / 'pruned_model.pt',
'--cfg', type=str, default='./models/yolov5s.yaml',
'--data', type=str, default=ROOT / 'data/my_yolov5.yaml', 
'--epochs', type=int, default=100
'--batch-size', type=int, default=16, 
'--imgsz', '--img', '--img-size', type=int, default=640,
'--adam', action='store_true', default=True, 
'--workers', type=int, default=8, 
'--project', default=ROOT / 'runs/finetune',

4.6.2 finetune

若没有修改finetune_pruned.py中的参数,如下执行。

python finetune_pruned.py --weights pruned_model.pt --adam --epochs 100

由于直接修改了finetune_pruned.py中的参数,直接执行

python finetune_pruned.py

执行时,报了【问题9】,按照相关解决方法可fine_tune正常开始。

4.7 循环稀疏训练->剪枝->finetune网络

相关文章:

从0开始做yolov5模型剪枝

文章目录 从0开始做yolov5模型剪枝 ****1 前言2 GitHub取源码3 原理3.1 原理3.2 network slimming过程 4 具体实施步骤4.1 安装虚拟环境4.2 配置参数4.2.1 数据集参数4.2.2 模型结构参数4.2.3 train.py中的参数 4.3 正常训练4.3.1 准备4.3.2 训练及问题解决 4.4 稀疏化训练4.4.…...

飞天使-k8s基础组件分析-安全

文章目录 名称空间解释访问kubernetes API的控制RBAC的介绍 kubeconfig用户的创建集群默认角色 给组创建授权针对pod配置服务账户参考文档 名称空间解释 名字是啥&#xff1f; 答&#xff1a;集群中每个对象的名称对于该类型的资源都是唯一的。并且每一个对象在整个集群中也有…...

Mysql安装使用

Mysql下载: MySQL :: Download MySQL Community Server Mysql解压&#xff1a; 解压后在根目录新建data文件夹和新建my.ini文件 my.ini文件内容如下: 注意&#xff1a;记得修改目录位置 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirD:\\mysql-5.7.30…...

聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化

聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化 目录 聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化&#xff0c;聚类结果可视化&#xff0c;MATLAB程…...

uniapp 使用 mui-player 插件播放 m3u8/flv 视频流

在UniApp中使用mui-player插件播放M3U8/FLV视频流&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 安装mui-player插件 &#xff1a;在UniApp项目根目录下&#xff0c;使用命令行工具执行以下命令安装mui-player插件&#xff1a; npm install mui-player --save2. 在需…...

大数据课程K4——Spark的DAGRDD依赖关系

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的DAG; ⚪ 掌握Spark的RDD的依赖关系; ⚪ 了解Spark对于DAG的Stage的划分; 一、DAG概念 1. 概述 Spark会根据用户提交的计算逻辑中的RDD的转换和动作来生成RDD之间的依赖关…...

disable 禁用元素后无法触发点击事件

业务需求点击被禁用的输入框触发事件 在被禁用元素上套一层div div上绑定事件 原本是不需要加事件穿透即可触发 但是最近谷歌更新触发不了 加一个事件穿透就好了 核心代码 style"pointer-events:none"style“pointer-events:none” 事件穿透 整体代码 <el-table-…...

uni-app开启gzip配置

指令&#xff1a;npm install webpack4.46.0 --save-dev 指令&#xff1a;npm install compression-webpack-plugin6.1.1 --save-dev vue.config.js const CompressionWebpackPlugin require(compression-webpack-plugin);module.exports {configureWebpack: config > {…...

房屋结构健康监测,科技助力让建筑更安全

房屋建筑是人们赖以生存的场所&#xff0c;然而当前我国许多房屋已经达到了使用寿命的中期&#xff0c;房屋的安全系数逐年降低&#xff0c;风险也随着时间的推移而累积。长期以来&#xff0c;我国的房屋普遍存在寿命短、隐患多的问题&#xff0c;“重建设&#xff0c;轻管理”…...

Android 面试之Glide做了哪些优化?

前言 Glide可以说是最常用的图片加载框架了&#xff0c;Glide链式调用使用方便&#xff0c;性能上也可以满足大多数场景的使用&#xff0c;Glide源码与原理也是面试中的常客。 但是Glide的源码内容比较多&#xff0c;想要学习它的源码往往千头万绪&#xff0c;一时抓不住重点.…...

【韩顺平 零基础30天学会Java】数组、排序和查找(2days)

数组、排序、查找和多维数组 数组可以存放多个同一类型的数据。数组也是一种数据类 型&#xff0c;是引用数据类型。 定义一个数组 double[] hens {3,5,1,3.4,2,50} 遍历数组得到数组所有元素的和 hens[下标]&#xff0c;下标是从0开始编号的。 可以通过数组名.lenght得到数组…...

VUE笔记(一)初识vue

一、vue的简介 1、什么是vue 官网地址:Vue.js Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。 构建用户界面&#xff1a;之前在学习vue之前通过原生js对DOM操作进行构建用户界面的 使用原生js构建用户界面的不足 - 没有规范&#xff0c…...

3D点云处理:学习总结(更新整理中)

文章目录 开发工具个人看法 微信&#xff1a;dhlddx B站演示视频 前置说明&#xff1a;仅是个人在使用pcl开发过程中的总结&#xff08;点云处理顺序或比较实用的功能&#xff09;&#xff0c;不喜勿喷&#xff1b; 开发工具 开发IDE&#xff1a;Qt Creator&#xff08;Windo…...

Day45|leetcode 70. 爬楼梯、322. 零钱兑换、279.完全平方数

leetcode 70. 爬楼梯 题目链接&#xff1a;70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 本题可以用背包问题来解决&#xff0c;就相当于楼顶是背包&#xff0c;台阶是物品&#xff0c;相当于之前写法的进阶版。 代码实现 class Solution { public:int climbStairs(in…...

arm:day9

1。思维导图 2..I2C实验&#xff0c;检测温度和湿度 iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "gpio.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4…...

【大模型AIGC系列课程 1-2】创建并部署自己的ChatGPT机器人

OpenAI API 调用 获取 openai api api-key https://platform.openai.com/account/api-keys 利用 python requests 请求 openai 参考 openai 接口说明:https://platform.openai.com/docs/api-reference/chat/create import json # 导入json包 import requests # 导入req…...

启动metastore服务报错

启动Metastore的时候报错&#xff1a; 简略的报错信息&#xff1a; MetaException(message:Error creating transactional connection factory)Caused by: MetaException(message:Error creating transactional connection factory)Caused by: javax.jdo.JDOFatalInternalExce…...

c 语言 算法 技巧 之 用移位来代替乘除

除法 当你需要计算一个数的一半时&#xff0c;通常我们会考虑使用除法运算&#xff08;/&#xff09;来实现。然而&#xff0c;计算机内部的运算中&#xff0c;除法通常比加法和乘法运算慢得多&#xff0c;因为除法需要更多的处理步骤。 位运算在这种情况下可以提供一个快速的…...

python爬虫实战零基础(3)——某云音乐

爬取某些云网页音乐&#xff0c;无需app 分析网页第二种方式批量爬取 声明&#xff1a;仅供参考学习&#xff0c;参考&#xff0c;若有不足&#xff0c;欢迎指正 你是不是遇到过这种情况&#xff0c;在pc端上音乐无法下载&#xff0c;必须下载客户端才能下载&#xff1f; 那么&…...

渗透测试漏洞原理之---【XSS 跨站脚本攻击】

文章目录 1、跨站 脚本攻击1.1、漏洞描述1.2、漏洞原理1.3、漏洞危害1.4、漏洞验证1.5、漏洞分类1.5.1、反射性XSS1.5.2、存储型XSS1.5.3、DOM型XSS 2、XSS攻防2.1、XSS构造2.1.1、利用<>2.1.2、JavaScript伪协议2.1.3、时间响应 2.2、XSS变形方式2.2.1、大小写转换2.2.2…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...