ByteTrack论文阅读笔记

目录

  • ByteTrack: Multi-Object Tracking by Associating Every Detection Box
    • 摘要
    • INTRODUCTION — 简介
    • BYTE算法
    • BYTE算法用Python代码实现
    • 实验
      • 评测指标
      • 轻量模型的跟踪性能
    • 总结
    • SORT算法简介
    • ByteTrack算法和SORT算法的区别

ByteTrack: Multi-Object Tracking by Associating Every Detection Box

论文链接:ByteTrack: Multi-Object Tracking by Associating Every Detection Box(ECCV2022)

摘要

(1)检测分数较低的目标,例如被遮挡的目标,会被简单丢弃。本文提出一种简单、有效且通用的关联方法,通过几乎关联每个检测框而不是仅关联高分检测框来进行跟踪。

(2)对于低分检测框,利用它们与轨迹的相似度来恢复真值目标,并过滤掉背景检测。

INTRODUCTION — 简介

image-20240703152721982

存在问题(如上图):

在帧t1中,初始化了三个不同的轨迹,因为它们的分数都高于0.5。然而,在帧t2和帧t3中发生遮挡时,红色轨迹(对应b)的对应检测分数变低,即0.8到0.4,再从0.4到0.1。这些检测框被阈值机制消除,红色轨迹相应的消失了,就会发生漏检

假如考虑了每个检测框,将立即引入更多的误检,例如,(a)帧t3中最右侧的框,这个框内并没有人,如果考虑每个框则会出现误检

解决方法:

本文发现轨迹相似度提供了一个强有力的线索,来区分低分数检测框中的目标和背景。如图中©所示,两个低分检测框通过运动模型的预测框与轨迹相匹配,匹配成功的恢复目标;没有匹配上运动轨迹的背景框将被删除。

具体细节:

  1. BYTE方法中,将每个检测框都视为轨迹的基本单元,就像计算机程序的byte;跟踪方法为每个细节检测框赋值。
  2. 首先基于运动相似度或外观相似度将高分检测框与轨迹匹配,本文采用卡尔曼滤波器来预测新帧中的轨迹位置,相似度可以由预测框和检测框的IoU或Re-ID特征距离来计算,图中(b)是第一次匹配后的结果。
  3. 然后使用相同的运动相似度在未匹配的轨迹(即红色框中的轨迹)和低分检测框之间执行第二次匹配,图中©显示了第二次匹配后的结果。
  4. 低检测分值的被遮挡的行人与先前的轨迹正确匹配,并且背景(在图像的右侧部分)被移除。

BYTE算法

如下为BYTE算法的伪代码:

image-20240703155139022

BYTE伪代码解析如下:

  1. 通过检测器获取检测框和对应的检测分数,对检测框进行分类,如果分数高于T_high,将检测框分类为高置信度组,分数低于T_high,高于T_low时,将检测框分类为低置信度组。
  2. 匹配过程使用到了检测框和卡尔曼滤波估计结果之间的相似度,这里可以采用IoU或Re-ID特征间距离来作为相似度度量。然后基于相似度采用匈牙利算法进行匹配,并保留那些未匹配到轨迹的高置信度检测框以及未匹配到检测框的轨迹。这部分对应伪代码中的17到19行(第一次关联)。
  3. 关联那些第一次关联剩下的轨迹以及低置信度检测框。之后保留那些第二次匹配过后仍然未匹配到边界框的轨迹,并删除那些低置信度边界框中在第二次匹配过后未找到对应轨迹的边界框,因为这些边界框被认定为是不包含任何物体的背景。对应伪代码中的第20到21行(第二次关联)。
  4. 将那些未匹配到对应轨迹的高置信度边界框作为新出现的轨迹进行保存,对应伪代码的23到27行。对两次匹配都没有匹配到的检测框,将它们初始化成新的轨迹。

注意:在第二次关联中仅使用IoU作为相似度非常重要。

原因:因为低分检测框通常包含严重的遮挡或运动模糊,外观特征不可靠。因此,当将BYTE应用于其他基于Re-ID的跟踪器时,不在第二次关联中采用外观相似度。

BYTE算法用Python代码实现

可能需要安装的依赖如: cython , lap , cython_bbox等。

如果 numpy 版本大于1.23可能需要修改 cython_bbox的源码,把代码里的 np.float 改成 np.float64。cython_bbox主要用于检测框的交叉 iou 的计算。

代码如下:

import numpy as np
from collections import deque
import os
import os.path as osp
import copyfrom kalman_filter import KalmanFilter
import matching
from basetrack import BaseTrack, TrackStateclass STrack(BaseTrack):shared_kalman = KalmanFilter()def __init__(self, tlwh, score):# wait activateself._tlwh = np.asarray(tlwh, dtype=np.float64)self.kalman_filter = Noneself.mean, self.covariance = None, Noneself.is_activated = Falseself.score = scoreself.tracklet_len = 0def predict(self):mean_state = self.mean.copy()if self.state != TrackState.Tracked:mean_state[7] = 0self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance)@staticmethoddef multi_predict(stracks):if len(stracks) > 0:multi_mean = np.asarray([st.mean.copy() for st in stracks])multi_covariance = np.asarray([st.covariance for st in stracks])for i, st in enumerate(stracks):if st.state != TrackState.Tracked:multi_mean[i][7] = 0multi_mean, multi_covariance = STrack.shared_kalman.multi_predict(multi_mean, multi_covariance)for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)):stracks[i].mean = meanstracks[i].covariance = covdef activate(self, kalman_filter, frame_id):"""Start a new tracklet"""self.kalman_filter = kalman_filterself.track_id = self.next_id()self.mean, self.covariance = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh))self.tracklet_len = 0self.state = TrackState.Trackedif frame_id == 1:self.is_activated = True# self.is_activated = Trueself.frame_id = frame_idself.start_frame = frame_iddef re_activate(self, new_track, frame_id, new_id=False):self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh))self.tracklet_len = 0self.state = TrackState.Trackedself.is_activated = Trueself.frame_id = frame_idif new_id:self.track_id = self.next_id()self.score = new_track.scoredef update(self, new_track, frame_id):"""Update a matched track:type new_track: STrack:type frame_id: int:type update_feature: bool:return:"""self.frame_id = frame_idself.tracklet_len += 1new_tlwh = new_track.tlwhself.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh))self.state = TrackState.Trackedself.is_activated = Trueself.score = new_track.score@property# @jit(nopython=True)def tlwh(self):"""Get current position in bounding box format `(top left x, top left y,width, height)`."""if self.mean is None:return self._tlwh.copy()ret = self.mean[:4].copy()ret[2] *= ret[3]ret[:2] -= ret[2:] / 2return ret@property# @jit(nopython=True)def tlbr(self):"""Convert bounding box to format `(min x, min y, max x, max y)`, i.e.,`(top left, bottom right)`."""ret = self.tlwh.copy()ret[2:] += ret[:2]return ret@staticmethod# @jit(nopython=True)def tlwh_to_xyah(tlwh):"""Convert bounding box to format `(center x, center y, aspect ratio,height)`, where the aspect ratio is `width / height`."""ret = np.asarray(tlwh).copy()ret[:2] += ret[2:] / 2ret[2] /= ret[3]return retdef to_xyah(self):return self.tlwh_to_xyah(self.tlwh)@staticmethod# @jit(nopython=True)def tlbr_to_tlwh(tlbr):ret = np.asarray(tlbr).copy()ret[2:] -= ret[:2]return ret@staticmethod# @jit(nopython=True)def tlwh_to_tlbr(tlwh):ret = np.asarray(tlwh).copy()ret[2:] += ret[:2]return retdef __repr__(self):return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)class BYTETracker(object):def __init__(self, frame_rate=20):self.tracked_stracks = []  # type: list[STrack]self.lost_stracks = []  # type: list[STrack]self.removed_stracks = []  # type: list[STrack]self.frame_id = 0self.det_thresh = 0.5 + 0.1self.buffer_size = int(frame_rate / 30.0 * 25)self.max_time_lost = self.buffer_sizeself.kalman_filter = KalmanFilter()def update(self, output_results):self.frame_id += 1activated_starcks = []refind_stracks = []lost_stracks = []removed_stracks = []if output_results.shape[1] == 5:scores = output_results[:, 4]bboxes = output_results[:, :4]else:output_results = output_results.cpu().numpy()scores = output_results[:, 4] * output_results[:, 5]bboxes = output_results[:, :4]  # x1y1x2y2remain_inds = scores > 0.5inds_low = scores > 0.1inds_high = scores < 0.5inds_second = np.logical_and(inds_low, inds_high)dets_second = bboxes[inds_second]dets = bboxes[remain_inds]scores_keep = scores[remain_inds]scores_second = scores[inds_second]if len(dets) > 0:'''Detections'''detections = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for(tlbr, s) in zip(dets, scores_keep)]else:detections = []''' Add newly detected tracklets to tracked_stracks'''unconfirmed = []tracked_stracks = []  # type: list[STrack]for track in self.tracked_stracks:if not track.is_activated:unconfirmed.append(track)else:tracked_stracks.append(track)''' Step 2: First association, with high score detection boxes'''strack_pool = joint_stracks(tracked_stracks, self.lost_stracks)# Predict the current location with KFSTrack.multi_predict(strack_pool)dists = matching.iou_distance(strack_pool, detections)dists = matching.fuse_score(dists, detections)matches, u_track, u_detection = matching.linear_assignment(dists, thresh=0.8)for itracked, idet in matches:track = strack_pool[itracked]det = detections[idet]if track.state == TrackState.Tracked:track.update(detections[idet], self.frame_id)activated_starcks.append(track)else:track.re_activate(det, self.frame_id, new_id=False)refind_stracks.append(track)''' Step 3: Second association, with low score detection boxes'''# association the untrack to the low score detectionsif len(dets_second) > 0:'''Detections'''detections_second = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for(tlbr, s) in zip(dets_second, scores_second)]else:detections_second = []r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked]dists = matching.iou_distance(r_tracked_stracks, detections_second)matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)for itracked, idet in matches:track = r_tracked_stracks[itracked]det = detections_second[idet]if track.state == TrackState.Tracked:track.update(det, self.frame_id)activated_starcks.append(track)else:track.re_activate(det, self.frame_id, new_id=False)refind_stracks.append(track)for it in u_track:track = r_tracked_stracks[it]if not track.state == TrackState.Lost:track.mark_lost()lost_stracks.append(track)'''Deal with unconfirmed tracks, usually tracks with only one beginning frame'''detections = [detections[i] for i in u_detection]dists = matching.iou_distance(unconfirmed, detections)dists = matching.fuse_score(dists, detections)matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)for itracked, idet in matches:unconfirmed[itracked].update(detections[idet], self.frame_id)activated_starcks.append(unconfirmed[itracked])for it in u_unconfirmed:track = unconfirmed[it]track.mark_removed()removed_stracks.append(track)""" Step 4: Init new stracks"""for inew in u_detection:track = detections[inew]if track.score < self.det_thresh:continuetrack.activate(self.kalman_filter, self.frame_id)activated_starcks.append(track)""" Step 5: Update state"""for track in self.lost_stracks:if self.frame_id - track.end_frame > self.max_time_lost:track.mark_removed()removed_stracks.append(track)# print('Ramained match {} s'.format(t4-t3))self.tracked_stracks = [t for t in self.tracked_stracks if t.state == TrackState.Tracked]self.tracked_stracks = joint_stracks(self.tracked_stracks, activated_starcks)self.tracked_stracks = joint_stracks(self.tracked_stracks, refind_stracks)self.lost_stracks = sub_stracks(self.lost_stracks, self.tracked_stracks)self.lost_stracks.extend(lost_stracks)self.lost_stracks = sub_stracks(self.lost_stracks, self.removed_stracks)self.removed_stracks.extend(removed_stracks)self.tracked_stracks, self.lost_stracks = remove_duplicate_stracks(self.tracked_stracks, self.lost_stracks)# get scores of lost tracksoutput_stracks = [track for track in self.tracked_stracks if track.is_activated]return output_stracksdef joint_stracks(tlista, tlistb):exists = {}res = []for t in tlista:exists[t.track_id] = 1res.append(t)for t in tlistb:tid = t.track_idif not exists.get(tid, 0):exists[tid] = 1res.append(t)return resdef sub_stracks(tlista, tlistb):stracks = {}for t in tlista:stracks[t.track_id] = tfor t in tlistb:tid = t.track_idif stracks.get(tid, 0):del stracks[tid]return list(stracks.values())def remove_duplicate_stracks(stracksa, stracksb):pdist = matching.iou_distance(stracksa, stracksb)pairs = np.where(pdist < 0.15)dupa, dupb = list(), list()for p, q in zip(*pairs):timep = stracksa[p].frame_id - stracksa[p].start_frametimeq = stracksb[q].frame_id - stracksb[q].start_frameif timep > timeq:dupb.append(q)else:dupa.append(p)resa = [t for i, t in enumerate(stracksa) if not i in dupa]resb = [t for i, t in enumerate(stracksb) if not i in dupb]return resa, resb

实验

评测指标

说明:IDTP可以看作是在整个视频中检测目标被正确分配的数量,IDFN在整个视频中检测目标被漏分配的数量,IDFP在整个视频中检测目标被错误分配的数量。

IDP:识别精确度

整体评价跟踪器的好坏,识别精确度IDP的分数如下进行计算:

image-20240703160520550

IDR:识别召回率

它是当IDF1-score最高时正确预测的目标数与真实目标数之比,识别召回率IDR的分数如下进行计算:

image-20240703160555355

IDF1:平均数比率

IDF1是指正确的目标检测数与真实数和计算检测数和的平均数比率,IDF1的分数如下进行计算:

image-20240703160639247

Re-Id:行人重识别

MOTA(Multiple Object Tracking Accuracy):MOTA主要考虑的是tracking中所有对象匹配错误,给出的是非常直观的衡量跟踪其在检测物体和保持轨迹时的性能,与目标检测精度无关,MOTA取值小于100,但是当跟踪器产生的错误超过了场景中的物体,MOTA可以变为负数。

MOTP(Multiple Object Tracking Precision) :是使用bonding box的overlap rate来进行度量(在这里MOTP是越大越好,但对于使用欧氏距离进行度量的就是MOTP越小越好,这主要取决于度量距离d的定义方式)MOTP主要量化检测器的定位精度,几乎不包含与跟踪器实际性能相关的信息。

HOTA(高阶跟踪精度)是一种用于评估多目标跟踪(MOT)性能的新指标。它旨在克服先前指标(如MOTA、IDF1和Track mAP)的许多限制。

轻量模型的跟踪性能

使用具有不同主干的YOLOX作为检测器。所有模型都在CrowdHuman和MOT17 half训练集上进行训练。在多尺度训练期间,输入图像尺寸是1088×608,最短边范围是384-832。

结果如表8所示。与DeepSORT相比,BYTE在MOTA和IDF1上都带来了稳定的改进,这表明BYTE对检测性能具有鲁棒性。值得注意的是,当使用YOLOX-Nano作为主干时,BYTE的MOTA比DeepSORT高3个点,这使其在实际应用中更具有吸引力。

image-20240703160906779

总结

ByteTrack算法的优点如下:

  1. ByteTrack算法是一种简单高效的数据关联方法,利用检测框和跟踪轨迹之间的相似性,在保留高分检测结果的同时,从低分检测结果中去除背景,挖掘出真正的前景目标。
  2. ByteTrack算法在处理大量目标时不会出现ID重复问题。
  3. ByteTrack算法在实时目标追踪方面表现优异。

SORT算法简介

SORT算法步骤:

  1. 对于每一个新的视频帧,运行一个目标检测器,得到一组目标的边界框。
  2. 对于每一个已经跟踪的目标,使用卡尔曼滤波对其状态进行预测,得到一个预测的边界框。
  3. 使用匈牙利算法将预测的边界框和检测的边界框进行匹配,根据边界框之间的重叠度(IOU)作为代价函数,求解最佳匹配方案。
  4. 对于匹配成功的目标,使用检测的边界框更新其卡尔曼滤波状态,并保留其标识。
  5. 对于未匹配的检测结果,创建新的目标,并初始化其卡尔曼滤波状态和标识。
  6. 对于未匹配的预测结果,删除过期的目标,并释放其标识。

ByteTrack算法和SORT算法的区别

  1. SORT算法是一种基于卡尔曼滤波的多目标跟踪算法;

    而ByteTrack算法是一种基于目标检测的追踪算法。

  2. SORT算法使用卡尔曼滤波预测边界框,然后使用匈牙利算法进行目标和轨迹间的匹配;

    而ByteTrack算法使用了卡尔曼滤波预测边界框,然后使用匈牙利算法进行目标和轨迹间的匹配,并且利用检测框和跟踪轨迹之间的相似性,在保留高分检测结果的同时,从低分检测结果中去除背景,挖掘出真正的前景目标。

  3. SORT算法在处理大量目标时会出现ID重复问题;

    而ByteTrack算法则不会出现这个问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/865640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT加载安装外围依赖库的翻译文件后翻译失败的现象分析:依赖库以饿汉式的形式暴露单例接口导致该现象的产生

1、前提说明 VS2019 QtClassLibaryDll是动态库,QtWidgetsApplication4是应用程序。 首先明确:动态库以饿汉式的形式进行单例接口暴露; 然后,应用程序加载动态库的翻译文件并进行全局安装; // ...QTranslator* trans = new QTranslator();//qDebug() << trans->…

暑期大数据人工智能学习-企业项目试岗实训开营

暑期企业项目-试岗实训活动全面开启啦 跟张良均老师学大数据人工智能 不仅可以提供实习证明&#xff0c;有需要话也可以提供实习鉴定报告 √54个热门案例拆解 √40项目实战课程 √27个项目可选 √4个项目方向

居然这么简单就能实现扫雷游戏!

目录 一.思路 1.成果展示 2.思路 二.具体操作 1.创建"棋盘" 2.初始化雷 3.布置雷 4.打印 5.排除雷 三.代码实现 1.test.c文件 2.thunder.h文件 3.thunder.c文件 Hello&#xff0c;大家好&#xff0c;今天我们来实现扫雷游戏&#xff0c;希望这一篇博客能给带给大家一…

使用LabVIEW报告生成工具包时报错97

问题详情&#xff1a; 在运行使用Excel/Word调用节点的程序时&#xff0c;收到错误97&#xff1a;LabVIEW&#xff1a;&#xff08;十六进制0x61&#xff09;输入中传递了一个空引用句柄或先前已删除的引用句柄。 当运行报告生成工具包中的一个示例程序时&#xff0c;收到错误…

速锐得解码汽车以太网技术特点接口定义数据传输及应用

在当前的汽车工业中&#xff0c;随着技术的飞速发展&#xff0c;车载网络技术也在不断进步与更新。其中&#xff0c;具备以太网的车型已成为一个新兴趋势&#xff0c;这主要归功于车载以太网技术在车内带宽需求较高的系统上的应用&#xff0c;如高级驾驶辅助系统&#xff08;AD…

1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到

今天刷面试题看到一个有意思的面试题&#xff0c; 1000T的文件怎么能以最快速度从南京传到北京&#xff1f; 网络传输 首先我们考虑通过网络传输&#xff0c;需要多长时间。 我特地咨询了在运营商工作的同学&#xff0c;目前带宽&#xff1a; 家庭宽带下行最大1Gbps&#…

Linux应用---内存映射

写在前面&#xff1a; 在进程间通信中&#xff0c;有一种方式内存映射。内存映射也是进程间通信的方式之一&#xff0c;其效率高&#xff0c;可以直接对内存进行操作。本节我们对内存映射进行学习&#xff0c;并结合案例进行实践。 1、基本理论 内存映射&#xff1a;是将磁盘文…

代码随想录-Day46

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

【JVM-05】Java内存区域(运行时数据区)、对象创建过程、内存布局

【JVM-05】Java内存区域即运行时数据区、对象创建过程、内存布局 1. 介绍下Java内存区域(运行时数据区)1.1 程序计数器(线程私有)1.2 虚拟机栈(线程私有)1.3 本地方法栈(线程私有)1.4 Java堆(线程共享)1.5 方法区(线程共享)1.5.1 方法区和永久代的关系1.5.2 常用参数1.5.3 为什…

Halcon支持向量机

一 支持向量机 1 支持向量机介绍&#xff1a; 支持向量机(Support Vector Machine&#xff0c;SVM)是Corinna Cortes和Vapnik于1995年首先提出的&#xff0c;它在解决小样本、非线性及高维模式识别表现出许多特有的优势。 2 支持向量机原理: 在n维空间中找到一个分类超平面…

node与npm安装教程

node与npm的下载安装教程&#xff1a; 文章目录 node与npm的下载安装教程&#xff1a;---Node.js 介绍NPM 介绍 一&#xff1a;下载&#xff08;node与npm的安装包是在一起的&#xff09;二&#xff1a;安装1&#xff1a;双击运行安装文件1-node-v14.15.0-x64.msi,点击下一步。…

常用渲染管线介绍

1、光栅化渲染管线&#xff08;Raster pipeline&#xff09; 1.1、光栅化概述 光栅化图形渲染管线是实时渲染的核心组件。渲染管线的功能是通过给定虚拟相机、3D场景物体以及光源等场景要素来产生或者渲染一副2D的图像。如上图所示&#xff0c;场景中的3D物体通过管线转变为屏…

动态渲染dom

最近在做类似论文生成的一个系统&#xff0c;比较复杂&#xff0c;简单来说就是文字、图表的展示&#xff0c;但是顺序不固定&#xff0c;所以有动态渲染dom的需求&#xff0c;以下是我写的小demo&#xff0c;以作记录。 <template><div id"app"><div…

Datadog Dash 2024 新功能解析

Datadog 2024 年的 Dash 刚刚落下帷幕&#xff0c;作为正在与 Datadog 开始竞争的观测云&#xff0c;我们认真仔细的分析了 Datadog 的每一个新功能&#xff0c;发现一些很有意思的事情&#xff0c;今天就给大家做一次全面的分析。&#xff08;所有 Datadog 的 Dash 的最新功能…

基于改进YOLOv5s的跌倒行为检测 | 引入SKAttention注意机制 + 引入空间金字塔池化结构SPPFCSPC + 结合ASFF自适应空间融合

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。为了实现电厂人员跌倒行为的实时检测&#xff0c;防止跌倒昏迷而无法及时发现并救援的事件发生&#xff0c;针对跌倒行为检测实时性以及特征提取能力不足的问题&#xff0c;提出了一种改进YOLOv5s的跌倒行为检测算法网络&a…

PD虚拟机和VirtualBox有什么区别?Parallels Desktop 19.1.1 破解版

随着计算机技术的不断发展&#xff0c;虚拟机软件在现代信息技术领域中扮演着越来越重要的角色。虚拟机不仅可以帮助用户在一台物理机器上运行多个操作系统&#xff0c;还能有效隔离不同环境&#xff0c;提升系统安全性。在众多的虚拟机软件中&#xff0c;PD虚拟机&#xff08;…

第一百三十八节 Java数据类型教程 - Java boolean数据类型和float数据类型

Java数据类型教程 - Java boolean数据类型 布尔数据类型只有两个有效值:true和false。 这两个值称为布尔值字面量。 我们可以使用布尔值字面量 boolean done; // Declares a boolean variable named done done true; // Assigns true to done布尔变量不能转换为任何其他…

ESP32-VScode环境设置

目录 前言 一、安装VSCode 二、安装ESP32环境 1.安装ESP-IDF 2.ESP-IDF设置 3:开始配置环境 4.打开example进行验证 5.烧录 6.调整波特率 总结 前言 环境&#xff1a;Visual Studio Code 芯片&#xff1a;ESP32 说实话&#xff0c;这是我装的时间最长的一个环境&…

汇聚荣拼多多电商哪些热词比较受关注?

汇聚荣拼多多电商哪些热词比较受关注?在探讨拼多多电商平台的热点关键词时&#xff0c;我们首先得明确&#xff0c;这个平台因其独特的商业模式和市场定位&#xff0c;吸引了大量消费者的目光。拼多多通过“拼团”购物的方式迅速崛起&#xff0c;成为电商行业的一个重要力量。…

2024年用scrapy爬取BOSS直聘的操作

SCrapy框架实现对BOSS直聘的爬取 文章目录 SCrapy框架实现对BOSS直聘的爬取对SCrapy框架的一个简单认识Scrapy 组件的作用Scrapy 数据流 1. 测试反爬2. 定义一个下载中间件类,截取spiders的请求&#xff08;中间件直接截取请求&#xff0c;并且返回给Spider进行数据解析&#x…