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

实时车辆行人多目标检测与跟踪系统(含UI界面,Python代码)

算法架构:

目标检测:yolov5
目标跟踪:OCSort

其中, Yolov5 带有详细的训练步骤,可以根据训练文档,训练自己的数据集,及其方便。
另外后续 目标检测会添加 yolov7 、yolox,目标跟踪会添加 ByteTrack、deepsort等经典算法,代码主要部分添加了详细注释,方便自己学习。

一、简介

本文将详细介绍如何使用深度学习中的YOLOv5和OCTrack算法实现车辆、行人等多目标的实时检测和跟踪,并利用PyQt5设计了简约的系统UI界面。在界面中,您可以选择自己的视频文件进行检测和跟踪,也可以通过电脑自带的摄像头进行实时处理。此外,您还可以更换自己训练的yolov5模型,进行自己数据的跟踪。

该系统界面优美,检测精度高,功能强大。它具备多目标实时检测、跟踪和计数功能,同时可以自由选择感兴趣的跟踪目标。

本博文提供了完整的Python程序代码和使用教程,适合新入门的朋友参考。您可以在文末的下载链接中获取完整的代码资源文件。以下是本博文的目录:

目录

  • 一、简介
  • 二、效果展示
  • 三、环境安装
  • 四、YOLOV5介绍
    • yolov5模型的训练步骤
  • 五、OCtrack介绍
  • 六、下载链接

二、效果展示

三、环境安装

以链接下均是博主个人的博客,总结的非常详细,按照此步骤安装,一定可以安装成功,相信自己欧。(后续会出详细的安装视频)

见 代码资源中 README.md 文件中 环境安装部分,以下是文档截图:
在这里插入图片描述

四、YOLOV5介绍

从实际效果来看YOLOv5已经相当优秀,是发展的比较完备、使用比较广泛的一个版本;而更重要的是YOLOv5的调用、训练和预测都十分方便。YOLOv5的另一个特点就是它为不同的设备需求和不同的应用场景提供了大小和参数数量不同的网络。

以下是YoloV5改进的部分(不完全):

(1)主干部分:使用了Focus网络结构,具体操作是在一张图片中每隔一个像素拿到一个值,这个时候获得了四个独立的特征层,然后将四个独立的特征层进行堆叠,此时宽高信息就集中到了通道信息,输入通道扩充了四倍。该结构在YoloV5第5版之前有所应用,最新版本中未使用。
(2)数据增强:Mosaic数据增强、Mosaic利用了四张图片进行拼接实现数据中增强,根据论文所说其拥有一个巨大的优点是丰富检测物体的背景!且在BN计算的时候一下子会计算四张图片的数据!
(3)多正样本匹配:在之前的Yolo系列里面,在训练时每一个真实框对应一个正样本,即在训练时,每一个真实框仅由一个先验框负责预测。YoloV5中为了加快模型的训练效率,增加了正样本的数量,在训练时,每一个真实框可以由多个先验框负责预测。

一、整体结构解析

在这里插入图片描述

和之前版本的Yolo类似,整个YoloV5可以依然可以分为三个部分,分别是Backbone,FPN以及Yolo Head

Backbone可以被称作YoloV5的主干特征提取网络,根据它的结构以及之前Yolo主干的叫法,我一般叫它CSPDarknet,输入的图片首先会在CSPDarknet里面进行特征提取,提取到的特征可以被称作特征层,是输入图片的特征集合。在主干部分,我们获取了三个特征层进行下一步网络的构建,这三个特征层我称它为有效特征层

FPN可以被称作YoloV5的加强特征提取网络,在主干部分获得的三个有效特征层会在这一部分进行特征融合,特征融合的目的是结合不同尺度的特征信息。在FPN部分,已经获得的有效特征层被用于继续提取特征。在YoloV5里依然使用到了Panet的结构,我们不仅会对特征进行上采样实现特征融合,还会对特征再次进行下采样实现特征融合。

Yolo Head是YoloV5的分类器与回归器,通过CSPDarknet和FPN,我们已经可以获得三个加强过的有效特征层。每一个特征层都有宽、高和通道数,此时我们可以将特征图看作一个又一个特征点的集合,每一个特征点都有通道数个特征。Yolo Head实际上所做的工作就是对特征点进行判断,判断特征点是否有物体与其对应。与以前版本的Yolo一样,YoloV5所用的解耦头是一起的,也就是分类和回归在一个1X1卷积里实现。

因此,整个YoloV5网络所作的工作就是 特征提取-特征加强-预测特征点对应的物体情况

yolov5模型的训练步骤

我们以 VOC 数据格式进行训练,如果自己的数据集是其他格式,可以通过https://blog.csdn.net/qq_28949847/article/details/130246098这篇博客进行格式转换。

1. 准备数据集(以VOC.yaml数据集为例)
2. 使用datasets文件夹下的voc2v5.py 将xml文件转为txt文件
只需修改下方红框位置处的参数, 具体含义见注释。
在这里插入图片描述

3. 参照datasets文件夹下创建的示例,创建文件夹结构,并将数据集放入在对应的文件夹下,截图如下:

在这里插入图片描述
4. 修改 data文件夹下的VOC.yaml

 # 数据集路径path: D:/lg/BaiduSyncdisk/project/person_code/project_self/Yolov5_OCtrack/datasets/airplanetrain: # train数据集- trainval: # val 数据集- traintest: # test 数据集- train# 修改为自己的类names:0: airplane

在这里插入图片描述

5. 根据自己的需求,修改 train.py文件夹下的参数,主要是以下画红框的这几个

在这里插入图片描述

6. 修改完参数后,点击运行即可训练
在这里插入图片描述

7. 训练结果(模型、PR曲线、数据分布情况、训练loss变化过程等)保存在 runs 文件夹下

在这里插入图片描述
在这里插入图片描述

五、OCtrack介绍

我们这里不做详细的原理介绍了,大部分的博客都已对其原理进行了详细介绍。我们主要对代码进行了详细的注释,有助于帮助我们更好的理解代码。其核心主要是两个类 OCSortKalmanBoxTracker, 下面代码对其进行了详细的注释:


class OCSort(object):def __init__(self, det_thresh, max_age=50, min_hits=-1,iou_threshold=0.3, delta_t=3, asso_func="diou", inertia=0.2, use_byte=False):"""初始化OCSort对象,并设置关键参数。参数:- det_thresh: float,目标检测结果的阈值- max_age: int,允许的跟踪器未更新的最大帧数,默认为30- min_hits: int,跟踪器更新所需的最小帧数,默认为3- iou_threshold: float,用于关联跟踪器和检测结果的IOU(交并比)阈值,默认为0.3- delta_t: int,时间步长,默认为3- asso_func: str,关联函数的名称,默认为"diou"- inertia: float,跟踪器的惯性权重,默认为0.2- use_byte: bool,是否使用字节级别的特征,默认为False"""self.max_age = max_ageself.min_hits = min_hitsself.iou_threshold = iou_threshold# 存储跟踪器的列表self.trackers = []self.frame_count = 0self.det_thresh = det_thresh# 时间步长self.delta_t = delta_tself.asso_func = ASSO_FUNCS[asso_func]# 跟踪器的惯性权重self.inertia = inertiaself.use_byte = use_byte# 跟踪器对象的计数器KalmanBoxTracker.count = 0self.label = {}# 存储label 对应关系self.idx_to_label = {}def update(self, results, img_size):"""更新跟踪器状态。参数:- results: numpy数组,表示检测结果,格式为[[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]- img_size: 元组,表示图像的尺寸 (height, width)要求:即使没有检测结果,每帧都必须调用此方法(对于没有检测结果的帧,请使用 np.empty((0, 5)))。返回一个类似的数组,其中最后一列是对象的ID。注意:返回的对象数量可能与提供的检测数量不同。"""# img 大小img_h, img_w = img_size[0], img_size[1]# 当前帧数self.frame_count += 1# 将 目标检测结果[[label, conf, [x1, y1, x2, y2],...] 转为 [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]格式output_results = []for r_i, res in enumerate(results):label, conf, bbox = res[:3]if label not in self.label:self.label[label] = 0 if len(self.label) == 0 else (max(self.label.values()) + 1)self.idx_to_label = {v: k for k, v in self.label.items()}cls = self.label[label]output_results.append([bbox[0], bbox[1], bbox[2], bbox[3], conf, cls])output_results = np.array(output_results) if len(output_results) else np.empty((0, 6))# x1y1x2y2bboxes = output_results[:, :4]scores = output_results[:, 4]classes = output_results[:, 5:6]dets = np.concatenate((bboxes, np.expand_dims(scores, axis=-1), classes), axis=1)# 选出self.det_thresh > score > 0.1阈值的数据,为低分数据, 设置 BYTE association 为 True 时会用到inds_low = scores > 0.1inds_high = scores < self.det_threshinds_second = np.logical_and(inds_low, inds_high)dets_second = dets[inds_second]# 选出 score > self.det_thresh 的数据remain_inds = scores > self.det_threshdets = dets[remain_inds]# 存储卡尔曼预测后的位置信息trks = np.zeros((len(self.trackers), 5))to_del = []ret = []# 对已经存在的self.trackers中的跟踪器进行卡尔曼位置预测for t, trk in enumerate(trks):# 根据trackers中的信息,进行卡尔曼位置预测pos = self.trackers[t].predict()[0]# 更新跟踪器的位置信息trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]# 如果预测的位置出现NaN值(无效值)if np.any(np.isnan(pos)):# 将该跟踪器的索引添加到待删除列表中to_del.append(t)# 移除包含NaN等无效值的行trks = np.ma.compress_rows(np.ma.masked_invalid(trks))# 从跟踪器列表中删除无效的跟踪器, 逆序删除元素, 防止索引发送变化for t in reversed(to_del):self.trackers.pop(t)# 获取跟踪器的速度velocities = np.array([trk.velocity if trk.velocity is not None else np.array((0, 0)) for trk in self.trackers])# 获取最后观察到的边界框last_boxes = np.array([trk.last_observation for trk in self.trackers])# 获取K个先前观察结果 ???k_observations = np.array([k_previous_obs(trk.observations, trk.age, self.delta_t) for trk in self.trackers])"""第一次:将目标检测得到的box同卡尔曼预测后的box进行级联匹配"""matched, unmatched_dets, unmatched_trks = associate(dets, trks, self.iou_threshold, velocities, k_observations,self.inertia)for m in matched:# 根据最新匹配的box进行卡尔曼更新self.trackers[m[1]].update(dets[m[0], :])"""# BYTE associationSecond round of associaton by OCR对未匹配的追踪器 同 低分的box进行2次匹配"""if self.use_byte and len(dets_second) > 0 and unmatched_trks.shape[0] > 0:# 提取未匹配的追踪器u_trks = trks[unmatched_trks]# 计算低分检测结果和未匹配追踪器之间的IOUiou_left = self.asso_func(dets_second, u_trks)  # iou between low score detections and unmatched tracksiou_left = np.array(iou_left)# 检查最大的IOU是否超过阈值if iou_left.max() > self.iou_threshold:"""注意:通过使用较低的阈值,例如 self.iou_threshold - 0.1,在MOT17/MOT20数据集上可能会获得更高的性能。但出于简单起见,我们在这里保持阈值的一致性。"""# 使用线性分配算法进行匹配matched_indices = linear_assignment(-iou_left)# 存储待删除的追踪器索引to_remove_trk_indices = []# 遍历匹配的索引对for m in matched_indices:det_ind, trk_ind = m[0], unmatched_trks[m[1]]# 如果IOU低于阈值,则跳过继续下一次迭代if iou_left[m[0], m[1]] < self.iou_threshold:continue# 更新对应的追踪器状态self.trackers[trk_ind].update(dets_second[det_ind, :])# 将待删除的追踪器索引添加到列表中to_remove_trk_indices.append(trk_ind)# 从未匹配的追踪器中移除已匹配的部分unmatched_trks = np.setdiff1d(unmatched_trks, np.array(to_remove_trk_indices))# 对未匹配的检测结果和追踪器 进行第2次重新匹配if unmatched_dets.shape[0] > 0 and unmatched_trks.shape[0] > 0:# 提取未匹配的检测结果和追踪器left_dets = dets[unmatched_dets]left_trks = last_boxes[unmatched_trks]# 计算未匹配的检测结果和追踪器之间的IOUiou_left = self.asso_func(left_dets, left_trks)iou_left = np.array(iou_left)# 检查最大的IOU是否超过阈值if iou_left.max() > self.iou_threshold:"""注意:通过使用较低的阈值,例如 self.iou_threshold - 0.1,在MOT17/MOT20数据集上可能会获得更高的准确度。但出于简单起见,我们在这里保持阈值的一致性。"""# 使用线性分配算法进行重新匹配rematched_indices = linear_assignment(-iou_left)# 存储待删除的检测结果和追踪器的索引to_remove_det_indices = []to_remove_trk_indices = []for m in rematched_indices:det_ind, trk_ind = unmatched_dets[m[0]], unmatched_trks[m[1]]# 如果IOU低于阈值,则跳过继续下一次迭代if iou_left[m[0], m[1]] < self.iou_threshold:continue# 更新对应的追踪器状态,索引是一直对应的,所以此处可以直接更新self.trackers[trk_ind].update(dets[det_ind, :])# 将待删除的检测结果和追踪器索引添加到列表中to_remove_det_indices.append(det_ind)to_remove_trk_indices.append(trk_ind)# 从未匹配的检测结果和追踪器中移除已匹配的部分unmatched_dets = np.setdiff1d(unmatched_dets, np.array(to_remove_det_indices))unmatched_trks = np.setdiff1d(unmatched_trks, np.array(to_remove_trk_indices))# 没有匹配上的tracker,也就是在没有新的测量值时,传入None,冻结滤波器状态并保持先前的预测结果,但是里面的后验状态 self.x_post、后验协方差 self.P_post、残差 self.y 进行了更新for m in unmatched_trks:self.trackers[m].update(None)# 对未匹配上的目标检测框,创建一个新的 KalmanBoxTracker 跟踪器对象for i in unmatched_dets:trk = KalmanBoxTracker(dets[i, :], delta_t=self.delta_t)self.trackers.append(trk)i = len(self.trackers)for trk in reversed(self.trackers):# 如果跟踪器的最近观测的总和小于0(即没有有效的观测)if trk.last_observation.sum() < 0:d = trk.get_state()[0]else:'''使用最近的观测box还是卡尔曼滤波器的预测box是可选的,这里没有注意到显著的差异。'''# 跟踪器有最近的观测(last_observation),则将变量 d 设置为该观测的前四个元素(即边界框的box)d = trk.last_observation[:4]if (trk.time_since_update <= 3) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):# +1 as MOT benchmark requires positiveret.append(np.concatenate((d, [trk.id, trk.cls, trk.conf])).reshape(1, -1))i -= 1# 当超过 max_age 没有更新时,删除此跟踪信息if trk.time_since_update > self.max_age:self.trackers.pop(i)outputs = []if len(ret) > 0:ret = np.concatenate(ret)for idx, t in enumerate(ret):x1, y1, x2, y2 = int(t[0]), int(t[1]), int(t[2]), int(t[3])track_id = int(t[4])cls = int(t[5])det_conf = t[6]class_name = self.idx_to_label[cls]x1 = min(img_w - 1, max(0, x1))x2 = min(img_w - 1, max(0, x2))y1 = min(img_h - 1, max(0, y1))y2 = min(img_h - 1, max(0, y2))res = [x1, y1, x2, y2, class_name, det_conf, track_id]outputs.append(res)return outputs
class KalmanBoxTracker(object):"""这个类表示以bbox形式观察到的单个被跟踪对象的内部状态。"""count = 0def __init__(self, bbox, delta_t=3, orig=False):"""使用目标检测框box初始化跟踪器。参数:- bbox:表示要跟踪的对象的初始边界框(目标检测框)。- delta_t:时间间隔,用于估计速度,计算速度是用 当前的box - 倒数第delta_t的box。- orig:一个布尔值,如果orig为False,则使用自定义的KalmanFilterNew作为卡尔曼滤波器模型,否则使用filterpy库中的KalmanFilter。"""# 定义Kalman滤波器模型if not orig:from .kalmanfilter import KalmanFilterNew as KalmanFilterself.kf = KalmanFilter(dim_x=7, dim_z=4)else:from filterpy.kalman import KalmanFilterself.kf = KalmanFilter(dim_x=7, dim_z=4)# 设置状态转移矩阵 7 * 7 矩阵,通过状态转移矩阵和当前状态的乘积,可以预测对象在下一个时间步的状态。# 7个位置分别代表:cx(中心位置)、cy(中心位置)、宽度、高度、速度(水平)、速度(垂直)、加速度# 当状态转移矩阵中的元素被设置为1时,表示在系统的状态转移过程中,对应状态之间的关系是线性的,并且该状态在时间推进时保持不变。self.kf.F = np.array([[1, 0, 0, 0, 1, 0, 0],[0, 1, 0, 0, 0, 1, 0],[0, 0, 1, 0, 0, 0, 1],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 0, 1, 0, 0],[0, 0, 0, 0, 0, 1, 0],[0, 0, 0, 0, 0, 0, 1]])# 设置观测矩阵self.kf.H = np.array([[1, 0, 0, 0, 0, 0, 0],[0, 1, 0, 0, 0, 0, 0],[0, 0, 1, 0, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0]])self.kf.R[2:, 2:] *= 10.# 对不可观测的初始速度给予较高的不确定性self.kf.P[4:, 4:] *= 1000.self.kf.P *= 10.self.kf.Q[-1, -1] *= 0.01self.kf.Q[4:, 4:] *= 0.01self.kf.x[:4] = convert_bbox_to_z(bbox)self.conf = bbox[4]self.time_since_update = 0self.id = KalmanBoxTracker.countKalmanBoxTracker.count += 1self.history = []self.hits = 0self.hit_streak = 0self.age = 0"""注意:[-1,-1,-1,-1,-1]是一个妥协的占位符,表示非观测状态,对于函数k_previous_obs的返回值也是如此。虽然不够美观,但为了以快速和统一的方式支持生成观测数组,如下所示:k_observations = np.array([k_previous_obs(...)]])。"""self.last_observation = np.array([-1, -1, -1, -1, -1])  # placeholderself.observations = dict()self.history_observations = []self.velocity = Noneself.delta_t = delta_tself.cls = int(bbox[5])def update(self, bbox):"""Args:bbox (array or None): 观测到的边界框,可以是数组或 None。Returns:NoneNotes:- self.velocity的计算:- 对于每个时间步 dt,从当前时间步 self.age 开始向前查找,找到与当前观测 bbox 相隔 delta_t 步的观测数据。- 如果找不到与当前观测相隔 delta_t 步的观测数据,使用最近的观测数据作为之前的边界框。"""if bbox is not None:if self.last_observation.sum() >= 0:  # no previous observationprevious_box = Nonefor i in range(self.delta_t):dt = self.delta_t - iif self.age - dt in self.observations:previous_box = self.observations[self.age - dt]breakif previous_box is None:previous_box = self.last_observation# 估计跟踪速度方向,使用与当前观测的bbox相隔 delta_t  步的观测数据self.velocity = speed_direction(previous_box, bbox)# 插入新的观测数据self.last_observation = bbox[:5]self.conf = bbox[4]self.observations[self.age] = bbox[:5]# 重置自上次更新以来的时间步数和历史记录self.time_since_update = 0self.history = []# 增加命中计数和命中连续次数self.hits += 1self.hit_streak += 1# 根据最新匹配的 bbox,进行卡尔曼更新self.kf.update(convert_bbox_to_z(bbox))else:self.kf.update(bbox)def predict(self):"""对跟踪器的速度和宽度之和进行判断,如果其小于等于零,则将跟踪器的水平加速度置零。具体解释如下:self.kf.x[6] 表示状态向量中的第 7 个元素,即水平加速度。self.kf.x[2] 表示状态向量中的第 3 个元素,即宽度。self.kf.x[6] + self.kf.x[2] 表示跟踪器的水平加速度和宽度之和。if self.kf.x[6] + self.kf.x[2] <= 0: 判断跟踪器的水平加速度和宽度之和是否小于等于零。如果条件成立,即跟踪器的速度和宽度之和小于等于零,那么 self.kf.x[6] *= 0.0 将跟踪器的水平加速度置零,相当于将其速度减小为零或停止水平运动。"""# 判断速度和宽度之和是否小于等于零if (self.kf.x[6] + self.kf.x[2]) <= 0:# 若小于等于零,将水平加速度置零self.kf.x[6] *= 0.0# 利用卡尔曼滤波器进行状态预测self.kf.predict()# 增加跟踪器的ageself.age += 1# 如果自上次更新以来经过的时间步数大于零if self.time_since_update > 0:# 将命中计数置零self.hit_streak = 0# 增加自上次更新以来的时间步数self.time_since_update += 1# 将当前状态向量 self.kf.x 转换为边界框信息,并添加到历史记录中self.history.append(convert_x_to_bbox(self.kf.x))# 返回历史记录中的最后一个边界框作为预测的边界框估计return self.history[-1]def get_state(self):"""Returns the current bounding box estimate."""return convert_x_to_bbox(self.kf.x)

目标跟踪效果:
在这里插入图片描述
至此视频中多目标检测跟踪的代码实现部分介绍完毕,后面的博文中将给出训练程序以及UI界面的详细介绍,至于程序如何使用、依赖包安装、pycharm软件的安装过程将通过博主的B站视频进行演示介绍,敬请关注!

六、下载链接

若您想获得博文中涉及的实现完整全部程序文件(包括模型权重,py, UI文件等,如下图),这里已打包上传至博主的面包多平台,已将所有涉及的文件同时打包到里面,点击即可运行,完整文件截图如下:
在这里插入图片描述
面包多:

相关文章:

实时车辆行人多目标检测与跟踪系统(含UI界面,Python代码)

算法架构&#xff1a; 目标检测&#xff1a;yolov5 目标跟踪&#xff1a;OCSort其中&#xff0c; Yolov5 带有详细的训练步骤&#xff0c;可以根据训练文档&#xff0c;训练自己的数据集&#xff0c;及其方便。 另外后续 目标检测会添加 yolov7 、yolox&#xff0c;目标跟踪会…...

谷歌AI机器人Bard发布强大更新,支持插件功能并增强事实核查;全面整理高质量的人工智能、机器学习、大数据等技术资料

&#x1f989; AI新闻 &#x1f680; 谷歌AI机器人Bard发布强大更新&#xff0c;支持插件功能并增强事实核查 摘要&#xff1a;谷歌的人工智能聊天机器人Bard发布了一项重大更新&#xff0c;增加了对谷歌应用的插件支持&#xff0c;包括 Gmail、Docs、Drive 等&#xff0c;并…...

NI SCXI-1125 数字量控制模块

NI SCXI-1125 是 NI&#xff08;National Instruments&#xff09;生产的数字量控制模块&#xff0c;通常用于工业自动化和控制系统中&#xff0c;以进行数字输入和输出控制。以下是该模块的一些主要产品特点&#xff1a; 数字量输入&#xff1a;SCXI-1125 模块通常具有多个数字…...

链表oj题1(Leetcode)——移除链表元素,反转链表,链表的中间节点,

链表OJ 一&#xff0c;移除链表元素1.1分析1.2代码 二&#xff0c;找到链表的中间节点2.1分析2.2代码 三&#xff0c;反转链表3.1分析3.2代码 四&#xff0c;找到链表中倒数第k个节点4.1分析4.2代码 一&#xff0c;移除链表元素 移除链表元素 1.1分析 这里的删除要分成两种…...

【libuv】与uvgrtrp的_SSIZE_T_定义不同

libuv的 #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) typedef intptr_t ssize_t;...

安卓ROM定制 修改必备常识-----初步了解system系统分区文件夹的基本含义 【二】

安卓修改rom 固件 修改GSI 移植rom 必备常识 lib--**so文件基本解析 一起来了解system目录相应文件的用途吧。&#xff08;rom版本不同里面的app也会不一样&#xff09; 简单打开img格式后缀文件 给大家说下最简单的方法提取img里面的文件&#xff0c;对于后缀img格式的文件可…...

GPT会统治人类吗

一 前言 花了大概两天时间看完《这就是ChatGPT》&#xff0c;触动还是挺大的&#xff0c;让我静下来&#xff0c;认真地想一想&#xff0c;是否真正理解了ChatGPT&#xff0c;又能给我们以什么样的启发。 二 思考 在工作和生活中&#xff0c;使用ChatGPT或文心一言&#xff0c;…...

win系统环境搭建(六)——Windows安装nginx

windows环境搭建专栏&#x1f517;点击跳转 win系统环境搭建&#xff08;六&#xff09;——Windows安装nginx 本系列windows环境搭建开始讲解如何给win系统搭建环境&#xff0c;本人所用系统是腾讯云服务器的Windows Server 2022&#xff0c;你可以理解成就是你用的windows10…...

Java中使用BigDecimal类相除保留两位小数

问题 遇到2个数相除&#xff0c;需要保留2位小数的结果。 解决 BigDecimal sum ...; BigDecimal yearValue ...;MathContext mathContext new MathContext(2, RoundingMode.DOWN); yearValue.divide(sum, mathContext);...

激光雷达在ADAS测试中的应用与方案

在科技高速发展的今天&#xff0c;汽车智能化已是必然的趋势&#xff0c;且自动驾驶汽车的研究也在世界范围内进行得如火如荼。而在ADAS测试与开发中&#xff0c;激光雷达以其高性能和高精度占据着非常重要的地位&#xff0c;它是ADAS测试与开发中不可缺少的组成。 一 激光雷达…...

malloc与free

目录 前提须知&#xff1a; malloc&#xff1a; 大意&#xff1a; 头文件&#xff1a; 申请空间&#xff1a; 判断是否申请成功&#xff1a; 使用空间&#xff1a; 结果&#xff1a; 整体代码&#xff1a; malloc申请的空间怎么回收呢? 注意事项&#xff1a; free:…...

计算周包材,日包材用来发送给外围系统

文章目录 1 Introduction2 code 1 Introduction In this example We get data from BOM and RESB . and calculate it . 2 code TYPES: BEGIN OF TY_ZPPT_0015_W,AUFNR TYPE ZPPT_0015-AUFNR,ZXH TYPE ZPPT_0015-ZXH,ZZJHID TYPE ZPPT_0015-ZZJHID,ZRJHID TYPE Z…...

R语言柱状图直方图 histogram

柱状图简介 柱状图也叫直方图&#xff0c;是展示连续性数值的分布状况。在x轴上将连续型数值分为一定数量的组&#xff0c;y轴显示对应值的频数。 R基本的柱状图 hist 我们用R自带的Orange数据来画图。 > head(Orange)Tree age circumference(圆周长) 1 1 118 …...

Linux磁盘管理:最佳实践

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

uni-app:通过三目运算动态增加样式效果(class)

效果 代码 第一条&#xff1a;当变量line的值等于abc时&#xff0c;class就等于yes,反之class等于no&#xff08;显然等于abc&#xff0c;执行yes,前景色为红色&#xff09; 第一条&#xff1a;当变量line1的值等于abc时&#xff0c;class就等于yes,反之class等于no&#xff…...

API安全

1 API的简介 API代表应用程序编程接口,它由一组允许软件组件进行通信的定义和协议组成。作为软件系统之间的中介,API使软件应用程序或服务能够共享数据和功能。但是API不仅仅提供连接基础,它还管理软件应用程序如何被允许进行通信和交互。API控制程序之间交换请求的类型、请…...

手写一个翻页功能

最近在对接海康摄像头&#xff0c;需要写一个翻页得功能&#xff0c;于是乎就想到了手写&#xff0c;然后就记录一下。在vue项目里写的 <img:src"require()"alt""click"onNext(delete)"/><img:src"require()"alt""…...

element show-overflow-tooltip 复制

el-table-column的show-overflow-tooltip弹出的提示无法复制&#xff0c;官方也暂时不准备解决&#xff0c;可以自己模拟一个 <el-table-column label"支付单号" width"100"><template #default"{ row }"><el-tooltip :content&…...

【C语言】指针的进阶(三)—— 模拟实现qsort函数以及指针和数组的笔试题解析

目录 1、模拟实现qsort函数 1.1、qsort函数的回顾 1.2、模拟实现qsort函数 2、指针和数组笔试题解析 2.1、一维数组 2.2、字符数组 1、模拟实现qsort函数 1.1、qsort函数的回顾 要模拟实现qsort函数&#xff0c;就要了解清楚qsort函数的参数以及使用方式。 我们先回顾一…...

Python 图像处理库PIL ImageOps笔记

# 返回一个指定大小的裁剪过的图像。该图像被裁剪到指定的宽高比和尺寸。 # 变量size是要求的输出尺寸&#xff0c;以像素为单位&#xff0c;是一个&#xff08;宽&#xff0c;高&#xff09;元组 # bleed&#xff1a;允许用户去掉图像的边界&#xff08;图像四个边界&#xff…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)

+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...

初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)

零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...

【Qt】控件 QWidget

控件 QWidget 一. 控件概述二. QWidget 的核心属性可用状态&#xff1a;enabled几何&#xff1a;geometrywindows frame 窗口框架的影响 窗口标题&#xff1a;windowTitle窗口图标&#xff1a;windowIconqrc 机制 窗口不透明度&#xff1a;windowOpacity光标&#xff1a;cursor…...

基于Python的气象数据分析及可视化研究

目录 一.&#x1f981;前言二.&#x1f981;开源代码与组件使用情况说明三.&#x1f981;核心功能1. ✅算法设计2. ✅PyEcharts库3. ✅Flask框架4. ✅爬虫5. ✅部署项目 四.&#x1f981;演示效果1. 管理员模块1.1 用户管理 2. 用户模块2.1 登录系统2.2 查看实时数据2.3 查看天…...