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
英文地址,請注明出處:http://en.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;選擇更高的運行或存儲效率的數據結構。邏輯結構——面向問…