vue中使用wavesurfer.js绘制波形图和频谱图(支持.pcm)

在这里插入图片描述

新的实现方式:vue使用Canvas绘制频谱图

安装wavesurfer.js

npm install wavesurfer.js

第一版:

组件特点:

  • 一次性加载好所有的数据;
<template><div class="audio-visualizer-container"><div class="visualization-container"><div ref="waveform" class="waveform"></div><div ref="spectrogram" class="spectrogram"></div><div v-if="loading" class="loading-indicator">音频加载中...</div><div v-if="error" class="error-message">{{ error }}</div></div><div class="audio-controls"><audioref="audioPlayer"controls@play="startPlay"@pause="stopPlay"@seeked="handleSeek"controlsList="nodownload noplaybackrate"></audio></div></div>
</template><script>
import axios from 'axios'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import WaveSurfer from 'wavesurfer.js'
import Spectrogram from 'wavesurfer.js/dist/plugins/spectrogram.esm.js'
// https://juejin.cn/post/6979191645916889095export default {name: 'AudioWaveform',props: {audioUrl: {type: String,required: true,},},data() {return {wavesurfer: null,spectrogramPlugin: null,isPlaying: false,audioBlobUrl: null,loading: false,error: null,isUserInteraction: false, // 标记是否是用户交互}},watch: {audioUrl(newVal) {this.handleAudioUrl(newVal)},},mounted() {this.initWaveSurfer()this.handleAudioUrl(this.audioUrl)},beforeDestroy() {this.cleanup()},methods: {async initWaveSurfer() {try {this.wavesurfer = WaveSurfer.create({container: this.$refs.waveform,waveColor: '#48a1e0',progressColor: '#25ebd7',cursorColor: '#333',// cursorWidth: 1,// barWidth: 2,// barRadius: 3,height: 150,sampleRate: 8000, // 明确指定采样率// normalize: true,// backend: 'WebAudio',// renderFunction: (channels, ctx) => {//   console.log('Custom render function called!') // 确保执行//   // this.drawWaveform(ctx, channels[0]) // 使用第一个声道数据绘制波形//   const { width, height } = ctx.canvas//   const channelData = channels[0] // 使用左声道数据//   const dataLength = channelData.length//   const step = Math.max(1, Math.floor(dataLength / width)) // 确保步长≥1,避免除零//   ctx.beginPath()//   ctx.lineWidth = 1//   ctx.strokeStyle = '#48a1e0' // 波形颜色//   // 中心线位置(对称波形)//   const centerY = height / 2//   for (let i = 0; i < width; i++) {//     // 使用 step 控制数据采样间隔//     const dataIndex = Math.min(Math.floor(i * step), dataLength - 1) // 防止数组越界//     const value = channelData[dataIndex] // 获取振幅值(-1 到 1)//     // 映射振幅到 Canvas 高度//     const amplitude = value * centerY//     console.log(`绘制点: x=${i},value=${value} amplitude=${amplitude} realMv=${this.calcRealMv(value)}`) // 调试输出//     const x = i//     const y = centerY - amplitude // 向上为正,向下为负//     if (i === 0) {//       ctx.moveTo(x, y)//     } else {//       ctx.lineTo(x, y)//     }//   }//   ctx.stroke() // 绘制路径//   ctx.closePath()// },})// 初始化频谱图插件this.spectrogramPlugin = this.wavesurfer.registerPlugin(Spectrogram.create({container: this.$refs.spectrogram,// labels: true,// labelsBackground: 'rgba(0,0,0,0.1)', //频率标签的背景height: 150,fftSamples: 1024,frequencyMax: 8000, //最大显示频率frequencyMin: 0, //显示最小频率colorMap: 'roseus',windowFunc: 'hann', // 使用汉宁窗函数alpha: 1, // 完全不透明}))this.wavesurfer.on('ready', () => {console.log('WaveSurfer ready')//  this.$refs.spectrogram.style.height = '150px' // 强制设置高度if (this.wavesurfer && this.wavesurfer.backend) {this.wavesurfer.backend.setAudioElement(this.$refs.audioPlayer)}})this.wavesurfer.on('error', (err) => {console.error('WaveSurfer error:', err)this.error = '音频处理错误: ' + err})// 监听用户交互事件this.wavesurfer.on('interaction', () => {this.isUserInteraction = true})// 监听波形图进度变化this.wavesurfer.on('timeupdate', (currentTime) => {if (this.isUserInteraction) {this.$refs.audioPlayer.currentTime = currentTimethis.isUserInteraction = false // 重置标志}})} catch (err) {console.error('初始化失败:', err)this.error = '初始化失败: ' + err.message}},calcRealMv(point) {return (point * 3.3) / 32767},async handleAudioUrl(audioUrl) {if (!audioUrl) returntry {this.loading = truethis.error = nullthis.resetPlayer()const arrayBuffer = audioUrl.endsWith('.pcm')? await this.loadPcmAudio(audioUrl): await this.loadRegularAudio(audioUrl)await this.loadAudio(arrayBuffer)} catch (err) {console.error('加载音频失败:', err)this.error = '加载音频失败: ' + err.message} finally {this.loading = false}},async loadPcmAudio(url) {try {const response = await fetch(url, {headers: {'X-Mintti-Web-Token': Vue.ls.get(ACCESS_TOKEN),},})if (!response.ok) throw new Error('HTTP错误: ' + response.status)const pcmData = await response.arrayBuffer()return this.convertPcmToWav(pcmData)} catch (err) {console.error('PCM转换失败:', err)throw new Error('PCM音频处理失败')}},async loadRegularAudio(url) {try {const response = await axios({method: 'get',url,responseType: 'arraybuffer',timeout: 10000,})return response.data} catch (err) {console.error('音频下载失败:', err)throw new Error('音频下载失败')}},async loadAudio(arrayBuffer) {return new Promise((resolve, reject) => {try {if (this.audioBlobUrl) {URL.revokeObjectURL(this.audioBlobUrl)}const blob = new Blob([arrayBuffer], { type: 'audio/wav' })this.audioBlobUrl = URL.createObjectURL(blob)this.$refs.audioPlayer.src = this.audioBlobUrlthis.wavesurfer.loadBlob(blob).then(() => {console.log('音频加载完成')resolve()}).catch((err) => {console.error('WaveSurfer加载失败:', err)reject(new Error('音频解析失败'))})} catch (err) {reject(err)}})},resetPlayer() {if (this.isPlaying) {this.stopPlay()}if (this.$refs.audioPlayer) {this.$refs.audioPlayer.src = ''}},cleanup() {if (this.audioBlobUrl) {URL.revokeObjectURL(this.audioBlobUrl)}if (this.wavesurfer) {this.wavesurfer.destroy()}},startPlay() {if (this.wavesurfer) {this.isPlaying = truethis.wavesurfer.play()}},stopPlay() {if (this.wavesurfer) {this.isPlaying = falsethis.wavesurfer.pause()}},handleSeek() {if (this.wavesurfer && this.$refs.audioPlayer) {const currentTime = this.$refs.audioPlayer.currentTimeconst duration = this.$refs.audioPlayer.durationif (duration > 0) {this.wavesurfer.seekTo(currentTime / duration)}}},convertPcmToWav(pcmData) {const sampleRate = 8000 // 使用标准采样率const numChannels = 1const bitsPerSample = 16const byteRate = (sampleRate * numChannels * bitsPerSample) / 8const blockAlign = (numChannels * bitsPerSample) / 8const dataLength = pcmData.byteLengthconst buffer = new ArrayBuffer(44 + dataLength)const view = new DataView(buffer)// WAV头部this.writeString(view, 0, 'RIFF')view.setUint32(4, 36 + dataLength, true)this.writeString(view, 8, 'WAVE')this.writeString(view, 12, 'fmt ')view.setUint32(16, 16, true)view.setUint16(20, 1, true) // PCM格式view.setUint16(22, numChannels, true)view.setUint32(24, sampleRate, true)view.setUint32(28, byteRate, true)view.setUint16(32, blockAlign, true)view.setUint16(34, bitsPerSample, true)this.writeString(view, 36, 'data')view.setUint32(40, dataLength, true)// 填充PCM数据const pcmView = new Uint8Array(pcmData)const wavView = new Uint8Array(buffer, 44)wavView.set(pcmView)return buffer},writeString(view, offset, string) {for (let i = 0; i < string.length; i++) {view.setUint8(offset + i, string.charCodeAt(i))}},},
}
</script><style scoped>
.audio-visualizer-container {position: relative;width: 100%;height: 100%;display: flex;flex-direction: column;
}.visualization-container {position: relative;flex: 1;display: flex;flex-direction: column;background-color: #f5f5f5;border-radius: 4px;overflow: hidden;
}.waveform,
.spectrogram {width: 100%;height: 150px;background-color: #fff;border-radius: 4px;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}.loading-indicator,
.error-message {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);padding: 10px 20px;background-color: rgba(0, 0, 0, 0.7);color: white;border-radius: 4px;z-index: 10;font-size: 16px;text-align: center;
}.error-message {background-color: rgba(255, 0, 0, 0.7);
}.audio-controls {margin-top: 10px;
}audio {width: 100%;
}
</style>

改进版:

  • 显示加载进度;
  • 先加载pcm文件,然后绘制波形图,再绘制频谱图;
  • 代码更健壮,确保数据有效性;
<template><div class="audio-visualizer-container"><div class="visualization-container"><div ref="waveform" class="waveform"></div><div ref="spectrogram" class="spectrogram"></div><div v-if="loading" class="loading-indicator">音频加载中... {{ progress }}%<div class="progress-bar"><div class="progress-fill" :style="{ width: progress + '%' }"></div></div></div><div v-if="error" class="error-message">{{ error }}</div></div><div class="audio-controls"><audioref="audioPlayer"controls@play="startPlay"@pause="stopPlay"@seeked="handleSeek"controlsList="nodownload noplaybackrate"></audio></div></div>
</template><script>
import axios from 'axios'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import WaveSurfer from 'wavesurfer.js'
import Spectrogram from 'wavesurfer.js/dist/plugins/spectrogram.esm.js'export default {name: 'AudioWaveform',props: {audioUrl: {type: String,required: true,},},data() {return {wavesurfer: null,spectrogramPlugin: null,isPlaying: false,audioBlobUrl: null,loading: false,error: null,isUserInteraction: false,progress: 0, // 新增加载进度百分比// 新增:请求控制器currentRequestController: null,}},watch: {audioUrl(newVal) {this.handleAudioUrl(newVal)},},mounted() {this.initWaveSurfer()this.handleAudioUrl(this.audioUrl)},beforeDestroy() {this.cleanup()},methods: {async initWaveSurfer() {// 销毁旧实例if (this.wavesurfer) {this.wavesurfer.destroy()this.wavesurfer = null}// 创建新的 WaveSurfer 实例this.wavesurfer = WaveSurfer.create({container: this.$refs.waveform,waveColor: '#48a1e0',progressColor: '#25ebd7',cursorColor: '#333',height: 150,sampleRate: 8000,})// 创建并注册频谱图插件const spectrogramPlugin = Spectrogram.create({container: this.$refs.spectrogram,height: 150,fftSamples: 1024,frequencyMax: 8000,frequencyMin: 0,colorMap: 'roseus',windowFunc: 'hann',alpha: 1,})await this.wavesurfer.registerPlugin(spectrogramPlugin)// 绑定事件this.wavesurfer.on('ready', () => {console.log('WaveSurfer 和 Spectrogram 加载完成')})this.wavesurfer.on('error', (err) => {console.error('WaveSurfer error:', err)this.error = '音频处理错误: ' + err})// 监听用户交互事件this.wavesurfer.on('interaction', () => {this.isUserInteraction = true})// 监听波形图进度变化this.wavesurfer.on('timeupdate', (currentTime) => {if (this.isUserInteraction) {this.$refs.audioPlayer.currentTime = currentTimethis.isUserInteraction = false // 重置标志}})},calcRealMv(point) {return (point * 3.3) / 32767},async handleAudioUrl(audioUrl) {if (!audioUrl) returntry {// 1. 中止之前的请求if (this.currentRequestController) {this.currentRequestController.abort()}// 2. 创建新的控制器const controller = new AbortController()this.currentRequestController = controller// 3. 重置状态this.resetComponentState()// 4. 初始化 WaveSurferawait this.initWaveSurfer()this.loading = truethis.progress = 0this.error = null// 5. 加载音频const arrayBuffer = audioUrl.endsWith('.pcm')? await this.loadPcmAudio(audioUrl, controller): await this.loadRegularAudio(audioUrl, controller)await this.loadAudio(arrayBuffer)} catch (err) {if (err.name === 'AbortError') {console.log('请求已中止')return}console.error('加载音频失败:', err)this.error = '加载音频失败: ' + err.message} finally {this.loading = false}},async loadPcmAudio(url) {try {const response = await fetch(url, {headers: {'X-Mintti-Web-Token': Vue.ls.get(ACCESS_TOKEN),},})if (!response.ok) throw new Error('HTTP错误: ' + response.status)const pcmBlob = await response.blob()return new Promise((resolve, reject) => {const reader = new FileReader()reader.onload = () => resolve(reader.result)reader.onerror = () => reject(new Error('读取PCM失败'))reader.readAsArrayBuffer(pcmBlob)})} catch (err) {console.error('PCM转换失败:', err)throw new Error('PCM音频处理失败')}},async loadRegularAudio(url, controller) {try {const response = await axios({method: 'get',url,responseType: 'arraybuffer',timeout: 60000,signal: controller.signal,onDownloadProgress: (progressEvent) => {this.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)},})return response.data} catch (err) {if (err.name === 'AbortError') {throw err}console.error('音频下载失败:', err)throw new Error('音频下载失败')}},async loadPcmAudio(url, controller) {try {const response = await fetch(url, {headers: {'X-Mintti-Web-Token': Vue.ls.get(ACCESS_TOKEN),},signal: controller.signal,})if (!response.ok) throw new Error('HTTP错误: ' + response.status)const pcmBlob = await response.blob()return new Promise((resolve, reject) => {const reader = new FileReader()reader.onload = () => resolve(reader.result)reader.onerror = () => reject(new Error('读取PCM失败'))reader.readAsArrayBuffer(pcmBlob)})} catch (err) {if (err.name === 'AbortError') {throw err}console.error('PCM转换失败:', err)throw new Error('PCM音频处理失败')}},async loadAudio(arrayBuffer) {return new Promise((resolve, reject) => {try {if (this.audioBlobUrl) {URL.revokeObjectURL(this.audioBlobUrl)}const blob = new Blob([arrayBuffer], { type: 'audio/wav' })this.audioBlobUrl = URL.createObjectURL(blob)this.$refs.audioPlayer.src = this.audioBlobUrlthis.wavesurfer.loadBlob(blob).then(() => {console.log('音频加载完成')resolve()}).catch((err) => {console.error('WaveSurfer加载失败:', err)reject(new Error('音频解析失败'))})} catch (err) {reject(err)}})},resetComponentState() {// 停止播放if (this.isPlaying) {this.stopPlay()}// 清空音频源if (this.$refs.audioPlayer) {this.$refs.audioPlayer.src = ''}// 清空波形图if (this.wavesurfer) {this.wavesurfer.empty()}// 重置状态this.progress = 0this.error = nullthis.isUserInteraction = false// 如果你希望每次都重新初始化 WaveSurfer(可选)// this.cleanup()// this.initWaveSurfer()},resetPlayer() {if (this.isPlaying) {this.stopPlay()}if (this.$refs.audioPlayer) {this.$refs.audioPlayer.src = ''}},cleanup() {if (this.audioBlobUrl) {URL.revokeObjectURL(this.audioBlobUrl)}if (this.wavesurfer) {this.wavesurfer.destroy()}},startPlay() {if (this.wavesurfer) {this.isPlaying = truethis.wavesurfer.play()}},stopPlay() {if (this.wavesurfer) {this.isPlaying = falsethis.wavesurfer.pause()}},handleSeek() {if (this.wavesurfer && this.$refs.audioPlayer) {const currentTime = this.$refs.audioPlayer.currentTimeconst duration = this.$refs.audioPlayer.durationif (duration > 0) {this.wavesurfer.seekTo(currentTime / duration)}}},convertPcmToWav(pcmData) {const sampleRate = 8000const numChannels = 1const bitsPerSample = 16const byteRate = (sampleRate * numChannels * bitsPerSample) / 8const blockAlign = (numChannels * bitsPerSample) / 8const dataLength = pcmData.byteLengthconst buffer = new ArrayBuffer(44 + dataLength)const view = new DataView(buffer)this.writeString(view, 0, 'RIFF')view.setUint32(4, 36 + dataLength, true)this.writeString(view, 8, 'WAVE')this.writeString(view, 12, 'fmt ')view.setUint32(16, 16, true)view.setUint16(20, 1, true)view.setUint16(22, numChannels, true)view.setUint32(24, sampleRate, true)view.setUint32(28, byteRate, true)view.setUint16(32, blockAlign, true)view.setUint16(34, bitsPerSample, true)this.writeString(view, 36, 'data')view.setUint32(40, dataLength, true)const pcmView = new Uint8Array(pcmData)const wavView = new Uint8Array(buffer, 44)wavView.set(pcmView)return buffer},writeString(view, offset, string) {for (let i = 0; i < string.length; i++) {view.setUint8(offset + i, string.charCodeAt(i))}},},
}
</script><style>
.loading-indicator {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);color: #333;background: rgba(255, 255, 255, 0.9);padding: 10px 20px;border-radius: 8px;font-size: 14px;text-align: center;
}.progress-bar {width: 100%;height: 6px;background: #eee;margin-top: 8px;border-radius: 3px;overflow: hidden;
}.progress-fill {height: 100%;background: #48a1e0;transition: width 0.2s;
}.error-message {color: red;font-size: 14px;padding: 10px;background: #ffe5e5;border-radius: 4px;
}
</style>

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

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

相关文章

go mod教程、go module

什么是go mod go mod 是go语言的包管理工具&#xff0c;类似java 的maven&#xff0c;go mod的出现可以告别goPath&#xff0c;使用go module来管理项目&#xff0c;有了go mod账号就不需要非得把项目放到gopath/src目录下了&#xff0c;你可以在磁盘的任何位置新建一个项目 go…

150-SWT-MCNN-BiGRU-Attention分类预测模型等!

150-SWT-MCNN-BiGRU-Attention分类预测模型!基于多尺度卷积神经网络(MCNN)双向长短期记忆网络(BiGRU)注意力机制(Attention)的分类预测模型&#xff0c;matlab代码&#xff0c;直接运行使用&#xff01;1、模型介绍&#xff1a;针对传统方法在噪声环境下诊断精度低的问题&#…

MySQL数据一致性与主从延迟深度解析:从内核机制到生产实践

在高并发分布式系统中&#xff0c;数据一致性与复制延迟如同硬币的两面。本文深入剖析MySQL持久化机制与主从同步原理&#xff0c;并提供可落地的调优方案。一、数据持久化核心机制&#xff1a;双日志协同 1. Redo Log&#xff1a;崩溃恢复的生命线刷新策略&#xff08;innodb_…

【I】题目解析

目录 单选题 多选题 判断题 单选题 1.reg[7:0]A; A2hFF;则A&#xff08;&#xff09; A.8b11111110 B.8b03 C.8b00000011 D.8b11111111 C 2hFF实际上等效于2位二进制2b11&#xff0c;赋值给8位寄存器A之后&#xff0c;低位赋值&#xff0c;高位补0 A8b00000011 AMD FPG…

《Foundation 面板:设计、功能与最佳实践解析》

《Foundation 面板:设计、功能与最佳实践解析》 引言 在当今数字化时代,用户界面(UI)设计的重要性不言而喻。其中,Foundation 面板作为一种流行的前端框架,因其灵活性和高效性而被众多开发者所青睐。本文将深入解析 Foundation 面板的设计理念、功能特点以及最佳实践,…

React服务端渲染 Next 使用详解

1. Next.js 概述 Next.js 是一个基于 React 的开源框架&#xff0c;专注于服务器端渲染&#xff08;SSR&#xff09;和静态站点生成&#xff08;SSG&#xff09;&#xff0c;提供开箱即用的 SSR 功能&#xff0c;简化 React 应用的开发与部署。 2. Next.js 的核心特性 SSR 支…

Deforum Stable Diffusion,轻松实现AI视频生成自由!

摘要&#xff1a; 你是否曾被那些充满想象力、画面流畅的AI视频所震撼&#xff1f;你是否也想亲手创造出属于自己的AI动画&#xff1f;本文将为你提供一份“保姆级”的详尽教程&#xff0c;从环境配置到参数调整&#xff0c;一步步带你复现强大的Deforum Stable Diffusion模型&…

不同环境安装配置redis

不同环境安装配置redis windows 环境安装redis redis所有下载地址 windows版本redis下载&#xff08;GitHub&#xff09;&#xff1a; https://github.com/tporadowski/redis/releases &#xff08;推荐使用&#xff09;https://github.com/MicrosoftArchive/redis/releases]官…

汇川Easy系列PLC算法系列(回溯法ST语言实现)

Easy系列PLC 3次多项式轨迹插补算法 Easy系列PLC 3次多项式轨迹插补算法(完整ST代码)_plc连续插补算法-CSDN博客文章浏览阅读122次。INbExecuteBOOLOFFOFF不保持1INrStartPosREAL0.0000000.000000不保持起始位置unit2INrEndPosREAL0.0000000.000000不保持结束位置unit3INrStar…

Linux C:构造数据类型

目录 一、结构体&#xff08;struct&#xff09; 1.1类型定义 1.2 结构体变量定义 1.3 结构体元素初始化 1.4 结构体成员访问 1.5 结构体的存储&#xff08;内存对齐&#xff09; 1.6 结构体传参 本文主要记录了C语言中构造数据类型部分的内容&#xff0c;今天暂时只写了…

Python:self

在Python面向对象编程中&#xff0c;self是一个指向类实例自身的引用参数&#xff1a;‌1. 本质与作用‌‌身份标识‌&#xff1a;self是类实例化后对象的"身份证"&#xff0c;代表当前实例本身&#xff0c;用于区分不同实例的属性和方法‌‌自动传递‌&#xff1a;调…

【SpringMVC】SpringMVC的概念、创建及相关配置

什么是SpringMVC 概述 中文翻译版&#xff1a;Servlet 栈的 Web 应用 Spring MVC是Spring Framework的一部分&#xff0c;是基于Java实现MVC的轻量级Web框架。 查看官方文档&#xff1a;https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.h…

浅谈存储过程

问题引入 面试的时候有时候会问到知不知道存储过程&#xff0c;用没用过&#xff1f; 是什么 存储过程&#xff08;Stored Procedure&#xff09;是在大型数据库系统中&#xff0c;一组为了完成特定功能的SQL 语句集&#xff0c;它存储在数据库中&#xff0c;一次编译后永久…

maven optional 功能详解

前言 最近参与了一个项目,使用maven管理依赖.项目拆分了很多模块.然后交个多个团队各自开发.最后在一个项目骨架中,把各自的模块引入进来,一起启动. 后来随着项目的深入.引入的jar包变多.发现 jar包太多,编译太慢, 打包之后的war包非常大.这种情况就可以使用optional来优化什么…

Python基础--Day04--流程控制语句

流程控制语句是计算机编程中用于控制程序执行流程的语句。它们允许根据条件来控制代码的执行顺序和逻辑&#xff0c;从而使程序能够根据不同的情况做出不同的决策。流程控制实现了更复杂和灵活的编程逻辑。 顺序语句 顺序语句是按照编写的顺序依次执行程序中的代码。代码会按照…

【同济大学】双速率自动驾驶架构LeAD:端到端+LLM,CARLA实测93%路线完成率,性能SOTA!

近年来&#xff0c;随着端到端的技术快速发展将自动驾驶带到了一个新高度&#xff0c;并且取得了非常亮眼的成绩。由于感知限制和极端长尾场景下训练数据覆盖不足&#xff0c;模型在高密度复杂交通场景下和不规则交通情况下的处理能力不足&#xff0c;导致在开放道路上大规模部…

github与git新手教程(快速访问github)

0 序言 作为一个开发者&#xff0c;你必须知道github和git是什么&#xff0c;怎么使用。 github是一个存储代码等资源的远程仓库&#xff0c;一个大型项目往往需要很多人共同协作开发&#xff0c;而大家如何协同开发的进度与分工等要求需要有一个统一开放保存代码的平台。git…

Windows环境下安装Python和PyCharm

可以只安装PyCharm吗&#xff1f;不可以&#xff01;&#xff01;&#xff01; 开发Python应用程序需要同时安装Python和PyCharm。Python是一种编程语言&#xff0c;PyCharm是一个专门为Python开发设计的集成开发环境&#xff0c;提供丰富的功能以简化编码过程。 一、前期准备…

Qt 嵌入式系统资源管理

在嵌入式系统中&#xff0c;资源&#xff08;CPU、内存、存储、网络等&#xff09;通常非常有限&#xff0c;因此高效的资源管理对 Qt 应用的稳定性和性能至关重要。本文从内存优化、CPU 调度、存储管理到电源控制&#xff0c;全面解析 Qt 嵌入式系统资源管理的关键技术。 一、…

小杰数据结构(one day)——心若安,便是晴天;心若乱,便是阴天。

1.数据结构计算机存储、组织数据的方式&#xff1b;有特定关系的数据元素集合&#xff1b;研究数据的逻辑结构、物理结构&#xff08;真实存在&#xff09;和对应的算法&#xff1b;新结构仍保持原结构类型&#xff1b;选择更高的运行或存储效率的数据结构。逻辑结构——面向问…