webrtc之子帶分割下——SplittingFilter源碼分析

文章目錄

  • 前言
  • 一、頻帶分割過程
    • 1.SplittingFilter的創建
    • 2.頻帶分割整體流程
      • 1)分割時機
      • 2)分割規則
      • 3)分割核心代碼
    • 3.頻帶合并
  • 二、算法實現
    • 1.實現原理介紹
    • 2.All pass QMF系統源碼
      • 1)提高精度
      • 2)經過串聯全通濾波器
      • 3)分離后的低頻高頻
    • 3.濾波算法實現
      • 1)濾波器系數
      • 2)全通濾波器串聯
  • 總結


前言

在之前的文章中,博主大致介紹了3A的作用、使用場景,并且以webrtc開源框架,說明了在webrtc中音頻3A開啟的方法和選項,以及對于3A模塊實例如何塞入近端信號和遠端信號的流程結合代碼做了詳細介紹。

webrtc中的SplittingFilter的主要作用是使用濾波器組進行子帶分割,好為后面做回聲消除做準備,本篇文章將會先介紹SplittingFilter代碼的整體流程,包括創建,分割,和合并的業務代碼。然后再進一步的介紹算法是如何實現以及實現細節。

|版本聲明:山河君,未經博主允許,禁止轉載

一、頻帶分割過程

1.SplittingFilter的創建

從最開始的地方來看一下SplittingFilter的創建時機,如果讀者對于webrtc有一定了解,那么應該知道對于音頻數據是存儲在AudioBuffer中的,AudioBuffer創建時同時會創建SplittingFilter

AudioBuffer::AudioBuffer(size_t input_rate,size_t input_num_channels,size_t buffer_rate,size_t buffer_num_channels,size_t output_rate,size_t output_num_channels): input_num_frames_(static_cast<int>(input_rate) / 100),......num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)), //判斷需要劃分的頻帶{.....if (num_bands_ > 1) { //判斷是否創建SplittingFiltersplit_data_.reset(new ChannelBuffer<float>(buffer_num_frames_, buffer_num_channels_, num_bands_));splitting_filter_.reset(new SplittingFilter(buffer_num_channels_, num_bands_, buffer_num_frames_));}
}

根據上文提到的,對于語音信號,人聲區間應該在0~8kHz,即:

  • 對于32k采樣率應該劃分為 0 ~ 8kHz, 8 ~ 16kHz
  • 對于48k采樣率劃分到 0 ~ 8kHz, 8 ~ 16kHz,16 ~ 24kHz。

所以對于16k采樣率即不用再劃分頻帶,而NumBandsFromFramesPerChannel(buffer_num_frames_)就是通過采樣率判斷需要劃分的頻帶區間。

2.頻帶分割整體流程

1)分割時機

AudioBuffer會在合適的時機進行頻帶分割,例如近端信號會在以下條件(如開啟高通濾波器,降噪等)滿足時進行分割:

bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingActive(bool ec_processing_active) const {return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ ||noise_suppressor_enabled_ || adaptive_gain_controller_enabled_ ||(echo_controller_enabled_ && ec_processing_active);
}if (submodule_states_.CaptureMultiBandSubModulesActive() &&SampleRateSupportsMultiBand(capture_nonlocked_.capture_processing_format.sample_rate_hz())) {capture_buffer->SplitIntoFrequencyBands();}

其中SplitIntoFrequencyBands接口就是開啟進行分割的入口

void AudioBuffer::SplitIntoFrequencyBands() {splitting_filter_->Analysis(data_.get(), split_data_.get());
}

2)分割規則

SplittingFilter根據頻帶選擇是分割成 0 ~ 8kHz, 8 ~ 16kHz還是分割成三份 0 ~ 8kHz, 8 ~ 16kHz,16 ~ 24kHz

void SplittingFilter::Analysis(const ChannelBuffer<float>* data,ChannelBuffer<float>* bands) {.....if (bands->num_bands() == 2) {TwoBandsAnalysis(data, bands); // 0 ~ 8kHz, 8 ~ 16kHz} else if (bands->num_bands() == 3) {ThreeBandsAnalysis(data, bands); // 0 ~ 8kHz, 8 ~ 16kHz,16 ~ 24kHz}
}

3)分割核心代碼

假如分割成兩份,那么核心的代碼就是如下:

void SplittingFilter::TwoBandsAnalysis(const ChannelBuffer<float>* data,ChannelBuffer<float>* bands) {RTC_DCHECK_EQ(two_bands_states_.size(), data->num_channels());RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame);for (size_t i = 0; i < two_bands_states_.size(); ++i) {std::array<std::array<int16_t, kSamplesPerBand>, 2> bands16;std::array<int16_t, kTwoBandFilterSamplesPerFrame> full_band16;FloatS16ToS16(data->channels(0)[i], full_band16.size(), full_band16.data());WebRtcSpl_AnalysisQMF(full_band16.data(), data->num_frames(),bands16[0].data(), bands16[1].data(),two_bands_states_[i].analysis_state1,two_bands_states_[i].analysis_state2);S16ToFloatS16(bands16[0].data(), bands16[0].size(), bands->channels(0)[i]);S16ToFloatS16(bands16[1].data(), bands16[1].size(), bands->channels(1)[i]);}
}
  • 核心代碼過程:
    • FloatS16ToS16對于多通道的情況,先取一個通道的數據,將其從FloatS16轉為int16
    • WebRtcSpl_AnalysisQMF進行頻帶分割,將兩個頻帶音頻數據分別放入bands16[0]bands16[1]two_bands_states_[i].analysis_state存儲不同通道,兩個頻帶的狀態
    • S16ToFloatS16再將兩個頻帶的數據恢復為float類型

3.頻帶合并

有頻帶分割,當然可以把分割的頻帶合并為原始音頻,過程和分割步驟相似

void SplittingFilter::TwoBandsSynthesis(const ChannelBuffer<float>* bands,ChannelBuffer<float>* data) {RTC_DCHECK_LE(data->num_channels(), two_bands_states_.size());RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame);for (size_t i = 0; i < data->num_channels(); ++i) {std::array<std::array<int16_t, kSamplesPerBand>, 2> bands16;std::array<int16_t, kTwoBandFilterSamplesPerFrame> full_band16;FloatS16ToS16(bands->channels(0)[i], bands16[0].size(), bands16[0].data());FloatS16ToS16(bands->channels(1)[i], bands16[1].size(), bands16[1].data());WebRtcSpl_SynthesisQMF(bands16[0].data(), bands16[1].data(),bands->num_frames_per_band(), full_band16.data(),two_bands_states_[i].synthesis_state1,two_bands_states_[i].synthesis_state2);S16ToFloatS16(full_band16.data(), full_band16.size(), data->channels(0)[i]);}
}

二、算法實現

1.實現原理介紹

webrtc的SplittingFilter源碼中主要應用到以下的原理:

  • QMF正交鏡像濾波器:語音信號處理二十九——QMF濾波器設計原理與實現
  • 多項分解和noble恒等式:語音信號處理三十——高效多相抽取器
  • 全通QMF濾波器:webrtc之子帶分割上——All-pass QMF濾波器

webrtc中是采用三個一階全通濾波器實現的all pass qmf,它的整體結構如下:

輸入: x[n]|↓Even x[2n] → A1(z) -→ A2(z) -→ A3(z) ──┐│      ↓ half_band_low[n]Odd  x[2n+1] → A1(z) -→ A2(z) -→ A3(z) ─┘↓ half_band_high[n][用于語音處理]合成:↑x?[2n]   = low + highx?[2n+1] = low - high

2.All pass QMF系統源碼


void WebRtcSpl_AnalysisQMF(const int16_t* in_data, size_t in_data_length,int16_t* low_band, int16_t* high_band,int32_t* filter_state1, int32_t* filter_state2)
{size_t i;int16_t k;int32_t tmp;int32_t half_in1[kMaxBandFrameLength];int32_t half_in2[kMaxBandFrameLength];int32_t filter1[kMaxBandFrameLength];int32_t filter2[kMaxBandFrameLength];const size_t band_length = in_data_length / 2;RTC_DCHECK_EQ(0, in_data_length % 2);RTC_DCHECK_LE(band_length, kMaxBandFrameLength);// Split even and odd samples. Also shift them to Q10.for (i = 0, k = 0; i < band_length; i++, k += 2){half_in2[i] = ((int32_t)in_data[k]) * (1 << 10);half_in1[i] = ((int32_t)in_data[k + 1]) * (1 << 10);}// All pass filter even and odd samples, independently.WebRtcSpl_AllPassQMF(half_in1, band_length, filter1,WebRtcSpl_kAllPassFilter1, filter_state1);WebRtcSpl_AllPassQMF(half_in2, band_length, filter2,WebRtcSpl_kAllPassFilter2, filter_state2);// Take the sum and difference of filtered version of odd and even// branches to get upper & lower band.for (i = 0; i < band_length; i++){tmp = (filter1[i] + filter2[i] + 1024) >> 11;low_band[i] = WebRtcSpl_SatW32ToW16(tmp);tmp = (filter1[i] - filter2[i] + 1024) >> 11;high_band[i] = WebRtcSpl_SatW32ToW16(tmp);}
}

1)提高精度

根據整體結構,首先是將信號分為奇偶信號,具體代碼如下:

 for (i = 0, k = 0; i < band_length; i++, k += 2){half_in2[i] = ((int32_t)in_data[k]) * (1 << 10);half_in1[i] = ((int32_t)in_data[k + 1]) * (1 << 10);}

值得注意的是* (1 << 10)是為了提高計算精度,webrtc中常用Q10 格式的定點數(左移 10 位),進行高精度的乘法和累加計算,避免精度損失。

2)經過串聯全通濾波器

WebRtcSpl_AllPassQMF(half_in1, band_length, filter1,WebRtcSpl_kAllPassFilter1, filter_state1);WebRtcSpl_AllPassQMF(half_in2, band_length, filter2,WebRtcSpl_kAllPassFilter2, filter_state2);

其中:

  • half_in1:偶信號
  • half_in2:奇信號
  • filter1,filter2:濾波后信號
  • WebRtcSpl_kAllPassFilter1:偶信號濾波器系數
  • WebRtcSpl_kAllPassFilter2:奇信號濾波器系數
  • filter_state1,filter_state2:保存濾波器狀態,實現保持多個數據塊(frame)之間連續

具體內部實現下文中介紹

3)分離后的低頻高頻

根據All pass QMF濾波器設計:
H0(z)=12[A(z)+A(?z)]H1(z)=12[A(z)?A(?z)]H_0(z)=\frac{1}{2}[A(z)+A(-z)] \\ H_1(z)=\frac{1}{2}[A(z)-A(-z)] H0?(z)=21?[A(z)+A(?z)]H1?(z)=21?[A(z)?A(?z)]
濾波后得到低頻高頻信號:

for (i = 0; i < band_length; i++){tmp = (filter1[i] + filter2[i] + 1024) >> 11;low_band[i] = WebRtcSpl_SatW32ToW16(tmp);tmp = (filter1[i] - filter2[i] + 1024) >> 11;high_band[i] = WebRtcSpl_SatW32ToW16(tmp);}

這里右移11位而不是10位,就是All pass QMF濾波器設計中要除以2

3.濾波算法實現

1)濾波器系數

從上文中,對于低頻子帶和高頻子帶中三個全通濾波器系數分別是:


// QMF filter coefficients in Q16.
static const uint16_t WebRtcSpl_kAllPassFilter1[3] = {6418, 36982, 57261};
static const uint16_t WebRtcSpl_kAllPassFilter2[3] = {21333, 49062, 63010};
項目內容
WebRtcSpl_kAllPassFilter1第一組三級一階全通濾波器的 Q16 系數,用于構造低子帶
WebRtcSpl_kAllPassFilter2第二組三級一階全通濾波器的 Q16 系數,用于構造高子帶
格式Q16,表示 value65536\frac{value}{65536}65536value?
來源來源于經典 QMF 結構設計,用于語音信號的二分頻濾波處理(Subband splitting)

通過matlab畫出來,其A(z)A(z)A(z)A(?z)A(-z)A(?z)的相頻響應是如下圖中所示:
在這里插入圖片描述

2)全通濾波器串聯


void WebRtcSpl_AllPassQMF(int32_t* in_data, size_t data_length,int32_t* out_data, const uint16_t* filter_coefficients,int32_t* filter_state)
{// The procedure is to filter the input with three first order all pass filters// (cascade operations).////         a_3 + q^-1    a_2 + q^-1    a_1 + q^-1// y[n] =  -----------   -----------   -----------   x[n]//         1 + a_3q^-1   1 + a_2q^-1   1 + a_1q^-1//// The input vector |filter_coefficients| includes these three filter coefficients.// The filter state contains the in_data state, in_data[-1], followed by// the out_data state, out_data[-1]. This is repeated for each cascade.// The first cascade filter will filter the |in_data| and store the output in// |out_data|. The second will the take the |out_data| as input and make an// intermediate storage in |in_data|, to save memory. The third, and final, cascade// filter operation takes the |in_data| (which is the output from the previous cascade// filter) and store the output in |out_data|.// Note that the input vector values are changed during the process.size_t k;int32_t diff;// First all-pass cascade; filter from in_data to out_data.// Let y_i[n] indicate the output of cascade filter i (with filter coefficient a_i) at// vector position n. Then the final output will be y[n] = y_3[n]// First loop, use the states stored in memory.// "diff" should be safe from wrap around since max values are 2^25// diff = (x[0] - y_1[-1])diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[1]);// y_1[0] =  x[-1] + a_1 * (x[0] - y_1[-1])out_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, filter_state[0]);// For the remaining loops, use previous values.for (k = 1; k < data_length; k++){// diff = (x[n] - y_1[n-1])diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]);// y_1[n] =  x[n-1] + a_1 * (x[n] - y_1[n-1])out_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, in_data[k - 1]);}// Update states.filter_state[0] = in_data[data_length - 1]; // x[N-1], becomes x[-1] next timefilter_state[1] = out_data[data_length - 1]; // y_1[N-1], becomes y_1[-1] next time// Second all-pass cascade; filter from out_data to in_data.// diff = (y_1[0] - y_2[-1])diff = WebRtcSpl_SubSatW32(out_data[0], filter_state[3]);// y_2[0] =  y_1[-1] + a_2 * (y_1[0] - y_2[-1])in_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, filter_state[2]);for (k = 1; k < data_length; k++){// diff = (y_1[n] - y_2[n-1])diff = WebRtcSpl_SubSatW32(out_data[k], in_data[k - 1]);// y_2[0] =  y_1[-1] + a_2 * (y_1[0] - y_2[-1])in_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[1], diff, out_data[k-1]);}filter_state[2] = out_data[data_length - 1]; // y_1[N-1], becomes y_1[-1] next timefilter_state[3] = in_data[data_length - 1]; // y_2[N-1], becomes y_2[-1] next time// Third all-pass cascade; filter from in_data to out_data.// diff = (y_2[0] - y[-1])diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[5]);// y[0] =  y_2[-1] + a_3 * (y_2[0] - y[-1])out_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, filter_state[4]);for (k = 1; k < data_length; k++){// diff = (y_2[n] - y[n-1])diff = WebRtcSpl_SubSatW32(in_data[k], out_data[k - 1]);// y[n] =  y_2[n-1] + a_3 * (y_2[n] - y[n-1])out_data[k] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[2], diff, in_data[k-1]);}filter_state[4] = in_data[data_length - 1]; // y_2[N-1], becomes y_2[-1] next timefilter_state[5] = out_data[data_length - 1]; // y[N-1], becomes y[-1] next time
}

其實算法實現已經在注釋里了,這里對于實現進行介紹,總共有三級,都是一階全通濾波器:

    //         a_3 + q^-1    a_2 + q^-1    a_1 + q^-1// y[n] =  -----------   -----------   -----------   x[n]//         1 + a_3q^-1   1 + a_2q^-1   1 + a_1q^-1//

根據一階全通濾波器的傳遞函數:
H(q)=a+q?11+aq?1?(1+aq?1)y[n]=(a+q?1)x[n]?y[n]+ay[n?1]=ax[n]+x[n?1]?y[n]=x[n?1]+a?(x[n]?y[n?1])H(q)=\frac{a+q^{-1}}{1+aq^{-1}} \\ \Longrightarrow (1+aq^{-1})y[n]=(a+q^{-1})x[n] \\ \Longrightarrow y[n] +ay[n-1]=ax[n]+x[n-1] \\ \Longrightarrow y[n]=x[n-1]+a\cdot(x[n]-y[n-1])H(q)=1+aq?1a+q?1??(1+aq?1)y[n]=(a+q?1)x[n]?y[n]+ay[n?1]=ax[n]+x[n?1]?y[n]=x[n?1]+a?(x[n]?y[n?1])
所以對應代碼實現:

  • 每一個全通濾波器的初始狀態使用的是上一次的frame對應全通濾波器的輸入輸出,例如:
    • filter_state[0]: x[?1]x[-1]x[?1]
    • filter_state[1]: y[?1]y[-1]y[?1]
  • 計算x[n]?y[n?1]x[n]-y[n-1]x[n]?y[n?1]:diff = WebRtcSpl_SubSatW32(in_data[0], filter_state[1]);
  • 計算y[n]y[n]y[n]:out_data[0] = WEBRTC_SPL_SCALEDIFF32(filter_coefficients[0], diff, filter_state[0]);

總結

本篇博客中介紹了SplittingFilter的作用和使用它的意義,通過對于webrtc源代碼的解讀對于頻帶分割的流程做了深入介紹,需要結合上一篇對于All pass QMF原理的文章一起看才能理解為何要如此設計。

如果對您有所幫助,請幫忙點個贊吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91436.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91436.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91436.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java運維之Tomcat升級

Tomcat升級準備工作 下述所有過程中,包含了兩種升級方式,一種是備份舊版本的 bin 和 lib,將新版本的 bin 和 lib 對舊版本進行覆蓋;另一種是直接備份舊版本的Tomcat包,運行新版本,將舊版本的配置文件(conf/ * )和應用(webapps/ * )等同步到新版本。 1. 到官網下載指…

MySQL的可重復讀隔離級別實現原理分析

MySQL 的 可重復讀&#xff08;Repeatable Read, RR&#xff09; 隔離級別主要通過 多版本并發控制&#xff08;Multi-Version Concurrency Control, MVCC&#xff09; 和 鎖機制&#xff08;特別是間隙鎖&#xff09; 來實現的。其核心目標是&#xff1a;在一個事務內&#xf…

利用Java自定義格式,循環導出數據、圖片到excel

利用Java自定義格式&#xff0c;循環導出數據、圖片到excel1、自定義格式循環導出數據1.1.設置格式1.1.1、居中樣式1.1.2、應用樣式到合并區域1.1.3、合并單元格1.1.4、設置列寬1.2、寫入數據1.2.1、創建標簽頭部1.2.2、寫入標簽內容2、自定義格式循環導出圖片2.1、設置格式并插…

SAP學習筆記 - 開發45 - RAP開發 Managed App New Service Definition,Metadata Extension

上一章講了在 Data Model View ( CDS View for BO Structure )基礎上創建 Projection View ( CDS View for BO Projection )。 SAP學習筆記 - 開發44 - RAP開發 Managed App 建 Projection View&#xff0c;Provider Contract&#xff0c;用 redirected to 設定父子關系-CSDN博…

React強大且靈活hooks庫——ahooks入門實踐之高級類hook(advanced)詳解

什么是 ahooks&#xff1f; ahooks 是一個 React Hooks 庫&#xff0c;提供了大量實用的自定義 hooks&#xff0c;幫助開發者更高效地構建 React 應用。其中高級類 hooks 是 ahooks 的一個重要分類&#xff0c;專門用于處理一些高級場景&#xff0c;如受控值、事件發射器、性能…

計算機網絡——數據鏈路層(25王道最新版)

數據鏈路層前言數據鏈路層的功能封裝成幀&#xff08;組幀&#xff09;字符計數法字節填充法零比特填充法違規編碼法小節差錯控制檢錯編碼奇偶校驗碼CRC校驗碼&#xff08;循環冗余校驗碼&#xff09;基本思想如何構造如何檢錯糾錯糾錯編碼海明校驗碼設計思路求解步驟&#xff…

【PTA數據結構 | C語言版】字符串替換算法

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 請編寫程序&#xff0c;將給定主串 s 中的子串 sub_s 替換成另一個給定字符串 t&#xff0c;再輸出替換后的主串 s。 輸入格式&#xff1a; 輸入給出 3 個非空字符串&#xff0c;依次為&#xff1a…

事物生效,訂單類內部更新訂單

代碼如下以下代碼1不生效&#xff0c;2生效解決方案1&#xff0c;外層方法加注解&#xff0c;內層不加2&#xff0c;不要拆分方法&#xff0c;把更新訂單操作放在帶事物的大方法中3&#xff0c;拆方法&#xff08;內部&#xff09;&#xff0c;注入自己&#xff0c;用代理對象調…

非對稱加密:RSA

文章目錄 非對稱加密:RSA 1、RSA 加解密 2、RSA 生成密鑰對(公鑰、私鑰)、加解密 參考資料 非對稱加密:RSA 1、RSA 加解密 <!-- RSA --><!-- 引入jsencrypt庫 --><script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.3.2/jsencrypt.min.js&q…

MongoDB 數據庫 啟用訪問控制

0. 最近服務器安裝了 MongoDB 被勒索了 測試服務器安裝了 MongoDB 等&#xff0c;開放了 27017 對所有 ip。 哈哈哈哈哈哈&#xff0c;問就是有點犯懶&#xff0c;之前都是只允許自己的 ip。 好家伙&#xff0c;然后沒過幾個小時&#xff0c;數據庫集合被清空&#xff0c;只留…

【Unity Sprite屬性拓展】

Unity Inspector 精靈圖預覽為 Unity 中的 Sprite 類型屬性提供了??增強版的 Inspector 顯示??&#xff0c;在保留標準精靈選擇功能的基礎上&#xff0c;添加了大型預覽圖和精靈名稱顯示功能代碼 using UnityEngine; using UnityEditor;// 1?? 告訴 Unity&#xff1a;所有…

細菌實驗入門:濃度測定與菌種鑒定技術詳解

在微生物實驗中&#xff0c;細菌濃度的精準測定和菌種的準確鑒定是兩項基礎且核心的操作。本文將詳細介紹相關技術的原理、操作步驟及注意事項&#xff0c;為新手提供系統性指導。一、細菌濃度測定方法1. 光密度法&#xff08;OD600&#xff09;&#xff1a;快速定量的首選原理…

GaussDB 數據庫架構師修煉(一)數據庫容量規劃

1、容量規劃的定義GaussDB容量規劃是指根據客戶業務系統的負載需求或歷史運行數據&#xff0c;進行合理規劃GaussDB的計算、存儲和網絡資源配置&#xff0c;以滿足業務系統正常使用和未來若干年負載增長訴求的過程。2、容量規劃活動主要步驟需求收集調研生產系統的業務特征&…

hashMap原理(一)

概念HashMap是java中一種非常常用的基于哈希表的數據結構&#xff0c;允許o(1)的時間復雜度進行元素插入&#xff0c;查找&#xff0c;和刪除。它通過”鍵-值“ 對的方式存儲數據。總的來說&#xff1a;HashMap的底層原理&#xff1a;數組鏈表紅黑樹&#xff08;jdk1.8之后還涉…

Ubuntu24 輔助系統-屏幕鍵盤的back按鍵在網頁文本框刪除不正常的問題解決方法

Ubuntu24 輔助系統-屏幕鍵盤的back按鍵異常 問題描述ubuntu24這個屏幕鍵盤&#xff0c;只有在網頁的搜索框或者文本框&#xff0c;比如百度首頁的搜索框&#xff0c;留言的文本框&#xff0c;才會出現點擊back按鈕的時候&#xff0c;出現了先選中當前這個字符&#xff0c;刪除此…

自然語言指令驅動的工業機器人協同學習系統:大語言模型如何重塑智能體協作范式

重磅推薦專欄: 《大模型AIGC》 《課程大綱》 《知識星球》 本專欄致力于探索和討論當今最前沿的技術趨勢和應用領域,包括但不限于ChatGPT和Stable Diffusion等。我們將深入研究大型模型的開發和應用,以及與之相關的人工智能生成內容(AIGC)技術。通過深入的技術解析和實踐經…

web:js的switch語句

在js中,switch語句是一種用于根據不同的條件執行不同代碼塊的控制流語句。它類似于多個if...else if...else語句,但結構更清晰,特別是在有多個條件分支的情況下。 基本語法 switch (expression) {case value1:// 當expression的值等于value1時執行這里的代碼break;case va…

為何說分布式 AI 推理已成為下一代計算方式

2024 年&#xff0c;我們見證了人工智能創新的空前爆發。AI 的快速發展令很多人驚嘆&#xff0c;為了訓練更先進的大語言模型&#xff08;LLM&#xff09;&#xff0c;科技巨頭爭相獲取強大的 GPU。如今&#xff0c;AI 正在無縫融入我們世界的每個角落。在眾多新興 AI 公司、模…

阿里云 RabbitMQ 可觀測性最佳實踐

阿里云 RabbitMQ 阿里云 RabbitMQ 是一款高性能、高可靠的消息中間件&#xff0c;支持多種消息協議和豐富的功能特性。它提供消息隊列功能&#xff0c;能夠實現應用間的消息解耦和異步通信&#xff0c;提升系統擴展性和穩定性。其支持多種消息持久化策略&#xff0c;確保消息不…

vue-router 導航式編程 參數的設置

主要是想記錄一下this.$router.push、replace、go等方法的參數如何設置。字符串路徑router.push(/home)直接使用字符串&#xff08;或模板字符串&#xff09;路徑&#xff0c;可跳轉到相應的URL路徑。對象式路徑路徑也可以是一個對象&#xff0c;對象里以key:value的形式表示UR…