小智源碼分析——音頻部分(二)

一、利用創建好的對象來調用音頻服務?

上周從上圖的getaudiocode()方法進去感受了一下底層小智的構造如何實現。所以用一個codec來接收我們所構造的音頻對象。

下來是用構造好的音頻對象來調用音頻初始化服務Initialize,因為啟動函數Application函數的類中有audio_servicez_所以可以進行調用。

這段初始化代碼的核心作用是:

1綁定并啟動音頻編解碼器

2配置音頻數據流的格式和處理流程

3按需初始化音頻處理器和喚醒詞檢測模塊

4設置好各類回調,保證音頻事件能及時通知到主程序

5創建定時器,自動管理音頻硬件電源

void AudioService::Initialize(AudioCodec* codec) {// 保存傳入的音頻編解碼器指針codec_ = codec;// 啟動音頻編解碼器,準備采集和播放codec_->Start();/* 初始化 Opus 解碼器和編碼器 */// 創建 Opus 解碼器,采樣率與輸出一致,單聲道,幀長為 OPUS_FRAME_DURATION_MSopus_decoder_ = std::make_unique<OpusDecoderWrapper>(codec->output_sample_rate(), 1, OPUS_FRAME_DURATION_MS);// 創建 Opus 編碼器,采樣率固定為 16kHz,單聲道,幀長為 OPUS_FRAME_DURATION_MSopus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);// 設置編碼復雜度為最低,節省算力opus_encoder_->SetComplexity(0);// 如果輸入采樣率不是 16kHz,則配置重采樣器,將輸入音頻轉換為 16kHzif (codec->input_sample_rate() != 16000) {input_resampler_.Configure(codec->input_sample_rate(), 16000);reference_resampler_.Configure(codec->input_sample_rate(), 16000);}// 根據編譯配置選擇不同的音頻處理器(如帶有回聲消除的AFE,或無處理的空實現)
#if CONFIG_USE_AUDIO_PROCESSORaudio_processor_ = std::make_unique<AfeAudioProcessor>();
#elseaudio_processor_ = std::make_unique<NoAudioProcessor>();
#endif// 根據編譯配置選擇不同的喚醒詞檢測算法
#if CONFIG_USE_AFE_WAKE_WORDwake_word_ = std::make_unique<AfeWakeWord>();
#elif CONFIG_USE_ESP_WAKE_WORDwake_word_ = std::make_unique<EspWakeWord>();
#elif CONFIG_USE_CUSTOM_WAKE_WORDwake_word_ = std::make_unique<CustomWakeWord>();
#elsewake_word_ = nullptr;
#endif// 設置音頻處理器的輸出回調,當有處理好的音頻輸出時,推入編碼隊列audio_processor_->OnOutput([this](std::vector<int16_t>&& data) {PushTaskToEncodeQueue(kAudioTaskTypeEncodeToSendQueue, std::move(data));});// 設置語音活動檢測(VAD)回調,檢測到說話狀態變化時,更新狀態并通知外部audio_processor_->OnVadStateChange([this](bool speaking) {voice_detected_ = speaking;if (callbacks_.on_vad_change) {callbacks_.on_vad_change(speaking);}});// 如果啟用了喚醒詞檢測,設置喚醒詞檢測回調,檢測到喚醒詞時通知外部if (wake_word_) {wake_word_->OnWakeWordDetected([this](const std::string& wake_word) {if (callbacks_.on_wake_word_detected) {callbacks_.on_wake_word_detected(wake_word);}});}// 創建音頻電源管理定時器,定期檢查音頻輸入/輸出是否需要關閉以省電esp_timer_create_args_t audio_power_timer_args = {.callback = [](void* arg) {AudioService* audio_service = (AudioService*)arg;audio_service->CheckAndUpdateAudioPowerState();},.arg = this,.dispatch_method = ESP_TIMER_TASK,.name = "audio_power_timer",.skip_unhandled_events = true,};esp_timer_create(&audio_power_timer_args, &audio_power_timer_);
}

二、啟動音頻服務

經過上部分的初始化,配置好了音頻的編解碼器,以及處理時對于音頻的要求(不符合要求的要重新采樣為符合要求的格式),還包括喚醒詞的檢測、提取和回調。

啟動流程(Start)

1標記服務未停止

service_stopped_ = false;

讓各任務知道服務正在運行。

2清除音頻相關事件位

xEventGroupClearBits(...)

確保音頻輸入、喚醒詞、音頻處理等任務可以正常啟動。

3啟動音頻電源管理定時器

esp_timer_start_periodic(...)

每秒檢查一次音頻硬件的電源狀態,自動省電。

4啟動音頻輸入任務

xTaskCreatePinnedToCore?或?xTaskCreate

創建音頻采集任務,負責從麥克風采集音頻數據。

5啟動音頻輸出任務

xTaskCreate

創建音頻播放任務,負責將音頻數據輸出到揚聲器。

6啟動 Opus?編解碼任務

xTaskCreate

創建音頻編解碼任務,負責音頻數據的編碼(發送)和解碼(播放)。

void AudioService::Start() {// 標記服務未停止service_stopped_ = false;// 清除音頻相關的事件位,確保任務可以正常啟動xEventGroupClearBits(event_group_, AS_EVENT_AUDIO_TESTING_RUNNING | AS_EVENT_WAKE_WORD_RUNNING | AS_EVENT_AUDIO_PROCESSOR_RUNNING);// 啟動音頻電源管理定時器,每秒檢查一次音頻硬件電源狀態esp_timer_start_periodic(audio_power_timer_, 1000000);/* 啟動音頻輸入任務 */
#if CONFIG_USE_AUDIO_PROCESSOR// 如果使用音頻處理器,任務綁定到指定內核xTaskCreatePinnedToCore([](void* arg) {AudioService* audio_service = (AudioService*)arg;audio_service->AudioInputTask();vTaskDelete(NULL);}, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_, 1);
#else// 不使用音頻處理器,普通方式創建任務xTaskCreate([](void* arg) {AudioService* audio_service = (AudioService*)arg;audio_service->AudioInputTask();vTaskDelete(NULL);}, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_);
#endif/* 啟動音頻輸出任務 */xTaskCreate([](void* arg) {AudioService* audio_service = (AudioService*)arg;audio_service->AudioOutputTask();vTaskDelete(NULL);}, "audio_output", 4096, this, 3, &audio_output_task_handle_);/* 啟動 Opus 編解碼任務 */xTaskCreate([](void* arg) {AudioService* audio_service = (AudioService*)arg;audio_service->OpusCodecTask();vTaskDelete(NULL);}, "opus_codec", 4096 * 7, this, 2, &opus_codec_task_handle_);
}

三、音頻回調服務

下來回到Application函數內,下一步執行下圖這一模塊:?

首先定義一個callbacks對象,他的類型如下:

?AudioServiceCallbacks?是一個回調函數集合,用于讓外部(比如主應用 Application)能夠“訂閱”音頻服務(AudioService)中的各種事件。當音頻服務內部發生特定事件時,會自動調用這些回調,通知外部進行相應處理。

struct AudioServiceCallbacks {std::function<void(void)> on_send_queue_available;std::function<void(const std::string&)> on_wake_word_detected;std::function<void(bool)> on_vad_change;std::function<void(void)> on_audio_testing_queue_full;
};

讓主程序通過事件組機制,能夠及時響應音頻服務中的關鍵事件,實現音頻事件的異步通知和處理。?

?

每個成員的含義

  • on_send_queue_available

類型:std::function<void(void)>

說明:當音頻發送隊列有可用數據時觸發。比如可以通知主程序“可以發送音頻數據到服務器了”。

  • on_wake_word_detected

類型:std::function<void(const std::string&)>

說明:當檢測到喚醒詞(如“小智”)時觸發。參數是檢測到的喚醒詞內容。

  • on_vad_change

類型:std::function<void(bool)>

說明:當語音活動檢測(VAD)狀態發生變化時觸發。參數?bool?表示當前是否有人在說話(true=正在說話,false=靜音)。

  • on_audio_testing_queue_full

類型:std::function<void(void)>

說明:當音頻測試隊列已滿時觸發。一般用于調試或測試場景。

異步和函數回調的區別?

方面異步執行自動回調
是否并發是,任務后臺運行不一定,回調是響應機制
主體是誰程序發起的異步任務異步任務完成后執行的函數
控制權主程序不阻塞,控制權立即返回控制權在回調被觸發時才回到你手里
是否依賴異步異步通常搭配回調使用回調常用在異步任務,但也可用于同步場景
舉個例子setTimeout()不會阻塞主線程setTimeout(fn, 1000) 中的 fn 是回調

四、音頻服務具體功能

分別了解下列三個核心任務函數:

  • AudioInputTask():音頻采集
  • AudioOutputTask():音頻播放
  • OpusCodecTask():音頻編解碼

// 音頻輸入任務,運行在一個 FreeRTOS 任務中
void AudioService::AudioInputTask() {while (true) {// 等待音頻相關事件觸發:測試模式、喚醒詞檢測、通用音頻處理EventBits_t bits = xEventGroupWaitBits(event_group_,AS_EVENT_AUDIO_TESTING_RUNNING |AS_EVENT_WAKE_WORD_RUNNING |AS_EVENT_AUDIO_PROCESSOR_RUNNING,pdFALSE,      // 不清除標志位pdFALSE,      // 任意一個事件即可返回portMAX_DELAY // 無限等待);// 如果服務已經停止,則退出任務if (service_stopped_) {break;}// 若麥克風需要預熱,延遲一段時間后繼續下一輪循環if (audio_input_need_warmup_) {audio_input_need_warmup_ = false;vTaskDelay(pdMS_TO_TICKS(120)); // 延遲 120mscontinue;}/** ==========================*  音頻測試處理邏輯(如按下 BOOT 錄音)*  ========================== */if (bits & AS_EVENT_AUDIO_TESTING_RUNNING) {// 判斷測試隊列是否已滿(按最大時長判斷)if (audio_testing_queue_.size() >= AUDIO_TESTING_MAX_DURATION_MS / OPUS_FRAME_DURATION_MS) {ESP_LOGW(TAG, "Audio testing queue is full, stopping audio testing");EnableAudioTesting(false); // 自動關閉測試continue;}// 準備讀取一幀音頻數據(例如 20ms × 16000Hz)std::vector<int16_t> data;int samples = OPUS_FRAME_DURATION_MS * 16000 / 1000;// 如果成功讀取音頻數據if (ReadAudioData(data, 16000, samples)) {// 若為雙聲道,僅保留左聲道數據(變為單聲道)if (codec_->input_channels() == 2) {auto mono_data = std::vector<int16_t>(data.size() / 2);for (size_t i = 0, j = 0; i < mono_data.size(); ++i, j += 2) {mono_data[i] = data[j];}data = std::move(mono_data);}// 推送數據到測試編碼隊列PushTaskToEncodeQueue(kAudioTaskTypeEncodeToTestingQueue, std::move(data));continue; // 當前處理完畢,回到等待下一次事件}}/** ==========================*  喚醒詞檢測處理邏輯*  ========================== */if (bits & AS_EVENT_WAKE_WORD_RUNNING) {std::vector<int16_t> data;int samples = wake_word_->GetFeedSize(); // 獲取所需幀長度// 若幀長度有效且成功讀取數據if (samples > 0 && ReadAudioData(data, 16000, samples)) {wake_word_->Feed(data); // 投喂喚醒詞檢測器continue;}}/** ==========================*  通用音頻處理邏輯*  ========================== */if (bits & AS_EVENT_AUDIO_PROCESSOR_RUNNING) {std::vector<int16_t> data;int samples = audio_processor_->GetFeedSize(); // 獲取處理器需要的數據大小// 若幀有效且數據讀取成功if (samples > 0 && ReadAudioData(data, 16000, samples)) {audio_processor_->Feed(std::move(data)); // 投喂音頻處理器continue;}}// 如果沒有任何已知事件被處理到,這通常是邏輯錯誤ESP_LOGE(TAG, "Should not be here, bits: %lx", bits);break; // 退出任務}// 最后,任務退出時打印警告日志ESP_LOGW(TAG, "Audio input task stopped");
}
void AudioService::AudioInputTask() {while (true) {EventBits_t bits = xEventGroupWaitBits(event_group_, AS_EVENT_AUDIO_TESTING_RUNNING |AS_EVENT_WAKE_WORD_RUNNING | AS_EVENT_AUDIO_PROCESSOR_RUNNING,pdFALSE, pdFALSE, portMAX_DELAY);if (service_stopped_) {break;}if (audio_input_need_warmup_) {audio_input_need_warmup_ = false;vTaskDelay(pdMS_TO_TICKS(120));continue;}/* Used for audio testing in NetworkConfiguring mode by clicking the BOOT button */if (bits & AS_EVENT_AUDIO_TESTING_RUNNING) {if (audio_testing_queue_.size() >= AUDIO_TESTING_MAX_DURATION_MS / OPUS_FRAME_DURATION_MS) {ESP_LOGW(TAG, "Audio testing queue is full, stopping audio testing");EnableAudioTesting(false);continue;}std::vector<int16_t> data;int samples = OPUS_FRAME_DURATION_MS * 16000 / 1000;if (ReadAudioData(data, 16000, samples)) {// If input channels is 2, we need to fetch the left channel dataif (codec_->input_channels() == 2) {auto mono_data = std::vector<int16_t>(data.size() / 2);for (size_t i = 0, j = 0; i < mono_data.size(); ++i, j += 2) {mono_data[i] = data[j];}data = std::move(mono_data);}PushTaskToEncodeQueue(kAudioTaskTypeEncodeToTestingQueue, std::move(data));continue;}}/* Feed the wake word */if (bits & AS_EVENT_WAKE_WORD_RUNNING) {std::vector<int16_t> data;int samples = wake_word_->GetFeedSize();if (samples > 0) {if (ReadAudioData(data, 16000, samples)) {wake_word_->Feed(data);continue;}}}/* Feed the audio processor */if (bits & AS_EVENT_AUDIO_PROCESSOR_RUNNING) {std::vector<int16_t> data;int samples = audio_processor_->GetFeedSize();if (samples > 0) {if (ReadAudioData(data, 16000, samples)) {audio_processor_->Feed(std::move(data));continue;}}}ESP_LOGE(TAG, "Should not be here, bits: %lx", bits);break;}ESP_LOGW(TAG, "Audio input task stopped");
}

?

void AudioService::AudioOutputTask() {while (true) {// 加鎖等待播放隊列非空或服務停止信號std::unique_lock<std::mutex> lock(audio_queue_mutex_);// 如果隊列為空且服務未停止,則阻塞等待條件變量觸發audio_queue_cv_.wait(lock, [this]() { return !audio_playback_queue_.empty() || service_stopped_; });// 如果檢測到服務已經停止,則退出任務if (service_stopped_) {break;}// 從播放隊列取出一個音頻任務(前移出隊)auto task = std::move(audio_playback_queue_.front());audio_playback_queue_.pop_front();// 通知等待的線程隊列已發生變化(喚醒可能的生產者)audio_queue_cv_.notify_all();// 解鎖互斥量,開始進行播放處理lock.unlock();// 如果音頻輸出尚未啟用,則啟用輸出并啟動功耗監測定時器if (!codec_->output_enabled()) {codec_->EnableOutput(true);esp_timer_start_periodic(audio_power_timer_, AUDIO_POWER_CHECK_INTERVAL_MS * 1000);}// 將 PCM 數據輸出到音頻設備codec_->OutputData(task->pcm);// 更新時間戳記錄為最近一次輸出時間last_output_time_ = std::chrono::steady_clock::now();// 播放計數器 +1,用于調試/統計debug_statistics_.playback_count++;#if CONFIG_USE_SERVER_AEC// 若啟用了服務器端 AEC,并且任務中包含有效時間戳,則記錄該時間戳if (task->timestamp > 0) {lock.lock(); // 重新加鎖以保護 timestamp_queue_timestamp_queue_.push_back(task->timestamp);}#endif}// 最后,任務退出時打印日志ESP_LOGW(TAG, "Audio output task stopped");
}
void AudioService::OpusCodecTask() {while (true) {// 加鎖并等待條件滿足:// - 服務已停止// - 編碼隊列非空 且 發送隊列未滿// - 解碼隊列非空 且 播放隊列未滿std::unique_lock<std::mutex> lock(audio_queue_mutex_);audio_queue_cv_.wait(lock, [this]() {return service_stopped_ ||(!audio_encode_queue_.empty() && audio_send_queue_.size() < MAX_SEND_PACKETS_IN_QUEUE) ||(!audio_decode_queue_.empty() && audio_playback_queue_.size() < MAX_PLAYBACK_TASKS_IN_QUEUE);});// 若服務已停止,則退出任務if (service_stopped_) {break;}/** ========================* 解碼邏輯* ======================== */if (!audio_decode_queue_.empty() && audio_playback_queue_.size() < MAX_PLAYBACK_TASKS_IN_QUEUE) {// 取出一個待解碼數據包auto packet = std::move(audio_decode_queue_.front());audio_decode_queue_.pop_front();audio_queue_cv_.notify_all();lock.unlock();  // 解鎖以便其他線程訪問隊列// 構造新的播放任務auto task = std::make_unique<AudioTask>();task->type = kAudioTaskTypeDecodeToPlaybackQueue;task->timestamp = packet->timestamp;// 設置解碼參數SetDecodeSampleRate(packet->sample_rate, packet->frame_duration);// 解碼數據if (opus_decoder_->Decode(std::move(packet->payload), task->pcm)) {// 如果解碼后的采樣率不一致,則重采樣if (opus_decoder_->sample_rate() != codec_->output_sample_rate()) {int target_size = output_resampler_.GetOutputSamples(task->pcm.size());std::vector<int16_t> resampled(target_size);output_resampler_.Process(task->pcm.data(), task->pcm.size(), resampled.data());task->pcm = std::move(resampled);}// 加鎖并推送到播放隊列lock.lock();audio_playback_queue_.push_back(std::move(task));audio_queue_cv_.notify_all();} else {// 解碼失敗ESP_LOGE(TAG, "Failed to decode audio");lock.lock();}debug_statistics_.decode_count++;}/** ========================* 編碼邏輯* ======================== */if (!audio_encode_queue_.empty() && audio_send_queue_.size() < MAX_SEND_PACKETS_IN_QUEUE) {auto task = std::move(audio_encode_queue_.front());audio_encode_queue_.pop_front();audio_queue_cv_.notify_all();lock.unlock(); // 解鎖以進行編碼// 構建音頻流數據包auto packet = std::make_unique<AudioStreamPacket>();packet->frame_duration = OPUS_FRAME_DURATION_MS;packet->sample_rate = 16000;packet->timestamp = task->timestamp;// 編碼 PCM 數據if (!opus_encoder_->Encode(std::move(task->pcm), packet->payload)) {ESP_LOGE(TAG, "Failed to encode audio");continue;}// 根據任務類型,推送到不同隊列if (task->type == kAudioTaskTypeEncodeToSendQueue) {{std::lock_guard<std::mutex> lock(audio_queue_mutex_);audio_send_queue_.push_back(std::move(packet));}// 通知有新的可發送數據if (callbacks_.on_send_queue_available) {callbacks_.on_send_queue_available();}} else if (task->type == kAudioTaskTypeEncodeToTestingQueue) {std::lock_guard<std::mutex> lock(audio_queue_mutex_);audio_testing_queue_.push_back(std::move(packet));}debug_statistics_.encode_count++;lock.lock(); // 重新加鎖以進入下一輪循環}}// 任務退出時記錄日志ESP_LOGW(TAG, "Opus codec task stopped");
}

?

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

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

相關文章

菜鳥的C#學習(四)

文章目錄一、格式說明符1.1、數字格式說明符&#xff08;適用于數值類型&#xff1a;int, double, decimal 等&#xff09;1. 標準數字格式2. 自定義數字格式1.2、日期時間格式說明符&#xff08;適用于 DateTime, DateTimeOffset&#xff09;1. 標準日期時間格式2. 自定義日期…

基于黑馬教程——微服務架構解析(二)

本篇文章基于黑馬程序員的微服務課程內容&#xff0c;結合個人學習過程中的理解與思考進行整理。本節將圍繞以下幾個問題展開&#xff1a;什么是網關和配置管理前面那篇文章&#xff0c;我們了解如何把一個單體的項目拆成分布式微服務項目&#xff0c;并且講解一下各個服務之間…

Text2SQL智能問答系統開發(一)

開發一個面向企業的chatBI工作流 已完成 基礎 Text2SQL 功能實現 實現用戶輸入自然語言問題后&#xff0c;系統能夠自動生成 SQL 并執行返回結果。用戶交互優化 支持用戶通過補充信息對查詢進行調整&#xff0c;提升易用性。模糊時間處理機制 對“最近”“近期”等模糊時間關…

Python HTML模塊詳解:從基礎到實戰

一、模塊體系全景圖 Python生態中處理HTML的工具可分為三大層級&#xff1a; 標準庫基礎層&#xff1a;html模塊 html.parser第三方增強層&#xff1a;BeautifulSoup&#xff08;搭配解析器&#xff09;專業級工具層&#xff1a;lxml requests-html 二、標準庫核心模塊詳解…

PyTorch常用Tensor形狀變換函數詳解

PyTorch常用Tensor形狀變換函數詳解 在PyTorch中&#xff0c;對張量&#xff08;Tensor&#xff09;進行形狀變換是深度學習模型構建中不可或缺的一環。無論是為了匹配網絡層的輸入要求&#xff0c;還是為了進行數據預處理和維度調整&#xff0c;都需要靈活運用各種形狀變換函數…

自主智能Agent如何重塑工作流自動化:技術、經濟與未來展望

自主智能Agent的崛起與工作流自動化的范式革命2025年7月&#xff0c;當OpenAI向付費用戶推出具備網頁瀏覽和代碼執行能力的ChatGPT Agent時&#xff0c;工作流自動化領域迎來了一場靜默但徹底的革命。這款不再滿足于簡單問答的智能體&#xff0c;在一個安全的虛擬計算機環境中運…

技術架構、行業應用、工具鏈整合、挑戰應對及未來趨勢五大模塊,引用多個權威來源數據與開源項目實現細節。

以下是一份關于AI技術落地的實戰經驗總結報告&#xff0c;結合代碼示例、可視化圖表與行業案例&#xff0c;內容分為技術架構、行業應用、工具鏈整合、挑戰應對及未來趨勢五大模塊&#xff0c;引用多個權威來源數據與開源項目實現細節。AI技術落地實戰指南&#xff1a;從架構設…

第 9 篇:神經網絡初探——當AI擁有了“大腦”,世界從此不同

《人工智能AI之機器學習基石》系列⑨ 專欄核心理念: 用通俗語言講清楚機器學習的核心原理,強調“洞察 + 技術理解 + 應用連接”,構建一個完整的、富有啟發性的知識體系。

音頻焦點 Android Audio Focus 進階

舊焦點處理 示例調用鏈: requestAudioFocus() → propagateFocusLossFromGain_syncAf() → handleFocusLossFromGain()。 系統事件(如來電)→ 強制焦點變化 → handleFocusLossFromGain()。 函數 propagateFocusLossFromGain_syncAf 焦點持有者發生的焦點丟失通知 主要功能…

MFC UI對話框

文章目錄對話框模態對話框創建銷毀關閉CDialog::OnCancel()EndDialog()CDialog::DestroyWindow()非模態對話框創建銷毀關閉delete this對話框 模態對話框 ??阻塞父窗口??&#xff0c;強制用戶先處理對話框。關閉前父窗口無法響應事件。 創建 推薦&#xff1a;非指針方式…

RabbitMQ--@RabbitListener及@RabbitHandle

兩者區別 在 Spring AMQP 中&#xff0c;RabbitListener 和 RabbitHandler 是處理 RabbitMQ 消息的核心注解&#xff0c;但職責和使用場景完全不同。以下從 定義、區別、場景、示例 逐層解析&#xff1a;一、核心定義1. RabbitListener作用&#xff1a;標記 方法或類 為 Ra…

【基于CKF的IMM】MATLAB例程,CV和CT兩個模型下的IMM,二維,濾波使用CKF(容積卡爾曼濾波),附下載鏈接

本程序實現了基于交互多模型&#xff08;IMM&#xff09;容積卡爾曼濾波&#xff08;CKF&#xff09;的多模型融合定位方法&#xff0c;并與純CV?CKFCV-CKFCV?CKF&#xff08;勻速模型&#xff09;和CT?CKFCT-CKFCT?CKF&#xff08;勻角速度轉彎模型&#xff09;方法對比。…

AI資訊日報 - 2025年07月28日

AI資訊日報 | 2025年07月28日 周一 今日核心要點 精華提煉 技術突破 ? 騰訊混元&#xff1a;開源發布、3D技術 ? 書生Intern&#xff1a;開源發布、多模態 企業動態 ? AI工具集&#xff1a;協同創作、視頻生成 數據概覽分類數量重點關注技術突破2 條開源發布、3D技術企業動態…

大語言模型 LLM 通過 Excel 知識庫 增強日志分析,根因分析能力的技術方案(1):總體介紹

文章大綱 1. 核心目標 2. 系統總體架構 3. Google Cloud 端到端方案(含無 RAG & RAG 雙模式) 3.1 無 RAG:Function-Calling 查表模式 3.2 RAG:托管式向量檢索 4. 開源輕量級方案 5. 數字孿生聯合驗證(實驗性) 6. 知識圖譜增強(Neo4j) 7. 監控與持續優化(CometLLM)…

Deepseek + browser-use 輕松實現瀏覽器自動化

在數字化時代&#xff0c;瀏覽器應用廣泛&#xff0c;瀏覽器自動化可大幅提升效率。Deepseek 是強大的智能語言模型&#xff0c;能精準解析復雜指令&#xff0c;browser - use 是專注瀏覽器操作的工具&#xff0c;提供豐富 API 接口&#xff0c;支持主流瀏覽器的各類自動化操作…

開疆智能ModbusTCP轉Profient網關連接西門子PLC與川崎機器人配置案例

本案例是西門子PLC與川崎機器人通過Profient轉ModbusTCP網關進行通訊轉換的配置案例&#xff0c;西門子作為profinet主站&#xff0c;機器人作為ModbusTCP服務器。配置過程&#xff1a;機器人配置川崎機器人控制器提供了RS232、以太網的通信接口&#xff0c;同時也可通過加裝選…

Docker多主機網絡連接:實現跨主機通信

Docker 是一種流行的容器化平臺&#xff0c;它可以幫助開發人員更方便地構建、發布和管理應用程序。在 Docker 中&#xff0c;容器是獨立運行的應用程序包裝&#xff0c;包含了運行所需的所有文件、庫和環境變量。Docker 提供了多種網絡連接方式&#xff0c;使得容器之間可以進…

OSPF筆記

一、OSPF基礎1、技術背景&#xff08;RIP中存在的問題&#xff09;RIP中存在最大跳數為15的限制&#xff0c;不能適應大規模組網周期性發送全部路由信息&#xff0c;占用大量的帶寬資源路由收斂速度慢以跳數作為度量值存在路由環路可能性每隔30秒更新2、OSPF協議特點沒有跳數限…

kotlin基礎【3】

Kotlin Playground: Edit, Run, Share Kotlin Code Online 資料&#xff1a;kotlin實戰 第一章 data class Person(val name: String,val age:Int?null)//允許接受以age為空&#xff0c;當為空將它賦值為null,如果不這么寫直接寫age:Int?是否可以fun main(args:Array<St…

Java-數構二叉樹

1.樹 1.1概念 樹是一種非線性的數據結構&#xff0c;它是由n個有限節點組成一個具有層次關系。這種結構有以下特點&#xff1a; 一個特殊的結點&#xff0c;稱為根節點&#xff0c;根節點沒有前驅節點除根節點以外&#xff0c;其余節點分成M個互不相交的集合。每個集合又是一…