,開源了)
電賽C題我用星閃UWB做了一個數字鑰匙門鎖系統(tǒng)開源了前言今年電賽C題要求做一套數字鑰匙系統(tǒng)——鑰匙能自動識別身份門鎖能判斷鑰匙距離在范圍內自動解鎖說實話這題不算難但要做得好不容易。身份識別用什么通信方案測距怎么保證精度怎么濾掉噪聲區(qū)域判斷的狀態(tài)機怎么設計我的方案是星閃SLE做身份識別 4錨點UWB做厘米級定位 卡爾曼濾波平滑數據 三級區(qū)域狀態(tài)機控制門鎖最終效果鑰匙靠近2米黃燈歡迎1米內綠燈解鎖離開自動上鎖全程OLED實時顯示距離和角度。距離誤差均為5cm內角度誤差7度內很遺憾今年電賽我們因為一些原因失利了但是還是決定將項目分享出來希望能夠幫助到大家代碼已開源https://gitcode.com/gcw_rVSV2mp6/diansai-C下面分享一下整體思路和踩過的坑系統(tǒng)架構先放一張整體架構圖星閃 SLE 通信 ┌────────────┐ ID 廣播 Notify ┌─────────────────────┐ │ 鑰匙 KEY │ ?──────────────────────? │ 門鎖 LOCK │ │ (H3863) │ UUID: 0x2828/0x2929 │ (H3863) │ │ │ │ SLE / UART×2 / OLED │ └────────────┘ │ LED×3 / 蜂鳴器 │ └─────────────────────┘ ▲ ▲ UART1 │ │ UART2 ▼ ▼ ┌──────────┐ ┌──────────┐ │ 錨點 E │ │ 錨點 G │ │ 錨點 F │ │ 錨點 H │ │ STM32 │ │ STM32 │ │ DW1000 │ │ DW1000 │ └──────────┘ └──────────┘整套系統(tǒng)分三部分鑰匙端一塊BearPi-Pico H3863通過星閃廣播身份ID一塊STM32最小系統(tǒng)板加DWM1000模塊當作標簽門鎖端一塊BearPi-Pico H3863接收ID 雙UART收測距數據 OLED顯示 LED/蜂鳴器UWB錨點4個STM32DWM1000模塊分布在不同位置做測距測完通過串口把距離發(fā)給門鎖為什么不用H3863直接驅動DWM1000一開始我也試過但H3863的SPI跟DW1000配合時序有問題我一直沒有移植成功。后來改成STM32專門負責UWB測距通過串口把結果發(fā)給H3863處理效果穩(wěn)定多了身份識別星閃SLE為什么選星閃題目要求身份識別傳統(tǒng)方案是BLE藍牙。但BearPi-Pico H3863原生支持星閃SLESparkLink Edge這是華為主推的新一代短距通信技術比BLE延遲更低、功耗更低而且SDK里自帶SSAP協(xié)議棧開發(fā)起來跟BLE GATT很像上手很快通信流程鑰匙端 (Server) 門鎖端 (Client) │ │ │ ?── 廣播 diansai2_key ────── │ 掃描 │ ── 連接成功 ─────────────────? │ │ ── 配對完成 ─────────────────? │ │ ── MTU 交換 (520) ────────────? │ │ ── 服務發(fā)現 ──────────────────? │ │ ── Property Handle ──────────? │ │ │ │ ── Notify ID (0x0A) ─────────? │ 每500ms │ ── Notify ID ────────────────? │鑰匙端做Server注冊一個ServiceUUID 0x2828和一個PropertyUUID 0x2929配對完成后每500ms通過Notify推送一次ID。門鎖端做Client掃描發(fā)現鑰匙后連接、配對、服務發(fā)現然后等著收NotifyID校驗鑰匙發(fā)送的ID是4-bit0~15門鎖端用撥碼開關設置期望ID收到后比對if (received_id expected_id): 驗證通過 - 允許解鎖 else: 驗證失敗 - 保持鎖定OLED上會實時顯示接收ID和期望ID以及PASS/FAIL結果UWB測距4錨點DS-TWRDS-TWR雙向測距原理UWB測距用的是DS-TWRDouble-Sided Two-Way Ranging算法分三步Tag (標簽) Anchor (錨點) │ │ │── Poll 包 ──────────────────────?│ T1 發(fā)送, T2 接收 │?── Response 包 ──────────────────│ T3 發(fā)送, T4 接收 │── Final 包 (含T1,T4,T5) ────────?│ T5 發(fā)送, T6 接收 │ │ │ 錨點根據6個時間戳計算TOF │飛行時間計算公式Ra T4 - T1 (Round 1) Rb T6 - T3 (Round 2) Da T5 - T4 (Reply 2) Db T3 - T2 (Reply 1) TOF (Ra×Rb - Da×Db) / (Ra Rb Da Db) 距離 TOF × 光速這個公式巧妙地消除了時鐘偏移誤差不需要雙方時鐘同步4錨點輪詢標簽端不是只跟一個錨點測距而是輪詢4個錨點E→F→G→H→E…每個錨點獨立測距通過串口輸出E D:0.56 F D:1.23 G D:0.89 H D:2.34門鎖端用兩個UART并行接收UART1 收錨點E、FUART2 收錨點G、H錨點布局60cm E ┌──────────┐ G │ │ │ │ 60cm │ │ F └──────────┘ H定位算法從測距到坐標卡爾曼濾波UWB測距有噪聲直接用會跳得很厲害。每個錨點獨立維護一個卡爾曼濾波器預測: p_pred p_est Q (Q 0.05) 更新: K p_pred / (p_pred R) (R 0.1) x_est x_est K × (測量值 - x_est) p_est (1 - K) × p_predQ越大越相信測量值R越大越相信預測值。0.05和0.1是我反復調試出來的參數野值剔除卡爾曼濾波能平滑小噪聲但對大跳變無能為力。所以加了一層野值剔除if |測量值 - 估計值| 50cm: 用估計值替代連續(xù)拒絕計數1 if 連續(xù)拒絕 5次: 接受新值強制重置防止卡死 else: 正??柭?0cm閾值和5次限制是經驗值太小會誤殺正常值太大會讓噪聲通過。坐標解算有了4個錨點的濾波后距離怎么算位置用的是差分平方法不需要解非線性方程計算量小x (d_G2 - d_E2) / (2L) (L60cm, 2L120) y (d_H2 - d_F2) / (2L)原理很簡單如果G和E距離相等x0如果離G更近x0。平方差正好消了距離的常數項徑向距離坐標x,y算出來后徑向距離avg (d_E d_F d_G d_H) / 4 r (avg - 61.0) / 1.10561.0和1.105是實測校準值——在已知距離點采樣擬合出來的零偏和縮放系數。不同硬件需要重新標定方位角angle -atan2(y, x) × (180/π) - 125°-125°是錨點布局的安裝偏移跟實際擺放方向有關。角度也做了指數平滑α0.3并且處理了360°環(huán)繞問題diff new_angle - old_angle if diff 180: diff - 360 if diff -180: diff 360 smooth 0.3 × diff區(qū)域狀態(tài)機距離算好了接下來是區(qū)域判斷。分三級距離 1m ┌──────────┐ ─────────────? ┌──────────┐ │ SENSING │ │ UNLOCK │ │ (紅燈) │ ?───────────── │ (綠燈) │ └──────────┘ 距離 1m └──────────┘ │ │ 距離 2m ▼ ┌──────────┐ │ WELCOME │ │ (黃燈) │ └──────────┘區(qū)域距離LED蜂鳴器解鎖區(qū) 1.0m綠燈短響100ms歡迎區(qū) 2.0m黃燈長響200ms感知區(qū) 2.0m紅燈-關鍵細節(jié)只有ID驗證通過才會解鎖。如果ID不匹配不管多近都是紅燈鎖定。事件驅動蜂鳴器蜂鳴器不是一直響而是區(qū)域變化時才響進入解鎖區(qū) → 短響離開解鎖區(qū) → 短響進入歡迎區(qū) → 長響離開歡迎區(qū) → 長響這樣不會太吵但每次狀態(tài)變化用戶都能感知到。OLED顯示門鎖端有一塊0.96寸SSD1306 OLED用軟件I2C驅動因為硬件I2C被占用了顯示內容掃描中SLE: Scanning... Exp: b0000 Key ID: ---- Zone: ----已連接Key:b1010 Exp:b0000 Ver: PASS Dist:1.23m A:4.5 Zone: WELCOME Event: Enter Welc Hrz:1.53m Status: WELCOME第一行同時顯示接收ID和期望ID的二進制第二行PASS/FAIL第三行距離和角度第四行區(qū)域第五行事件第六行水平距離第七行狀態(tài)。6x8字體8行顯示信息密度剛剛好字庫字庫是自己做的6x8點陣95個ASCII字符每個6字節(jié)一共570字節(jié)。直接寫在頭文件里編譯時寫死到FlashRTOS任務架構門鎖端跑LiteOS兩個并行任務┌─ Diansai2Lock (優(yōu)先級 17) ──────────┐ │ · SLE Client 掃描/連接/配對 │ │ · 接收 ID DIP 校驗 │ │ · OLED 刷新 (200ms) │ │ · LED/蜂鳴器控制 │ │ · 區(qū)域判斷 狀態(tài)機 │ └────────────────────────────────────┘ ┌─ Diansai2LockDist (優(yōu)先級 15) ──────┐ │ · UART1 接收錨點 E/F (115200) │ │ · UART2 接收錨點 G/H (115200) │ │ · 解析 X D:距離 協(xié)議 │ │ · 卡爾曼濾波 (每錨點獨立) │ │ · 野值剔除 │ │ · 徑向距離 方位角計算 │ └────────────────────────────────────┘Lock任務優(yōu)先級高一點17因為身份識別和狀態(tài)控制的實時性要求更高。Dist任務優(yōu)先級15測距數據有一定的容忍度晚幾十毫秒沒關系。兩個任務通過全局變量通信g_distance_cm、g_angle等簡單粗暴但有效踩過的坑1. H3863直接驅動DW1000時序不穩(wěn)一開始想用H3863的SPI直接驅動DW1000但是SPI程序一直沒有移植成功解決方案改用STM32專門驅動DW1000測距完成后串口把距離發(fā)給H38632. UWB測距跳變DW1000測距偶爾會跳一下比如實際1米突然報5米。卡爾曼濾波能平滑小波動但對這種大跳變沒用解決方案加了野值剔除層偏差超過50cm的測量值直接丟棄用上一次的估計值替代。連續(xù)丟棄5次后才強制接受新值防止濾波器卡死3. 角度跳變角度在±180°附近會突然跳變比如從179°跳到-179°普通的指數平滑會把這個跳變放大解決方案做平滑前先處理環(huán)繞diff new - old if diff 180: diff - 360 if diff -180: diff 360 smooth α × diff硬件清單器件數量角色BearPi-Pico H38632鑰匙 門鎖STM32F103 DW10005標簽UWB錨點 E/F/G/HSSD1306 0.96 OLED1門鎖顯示LED 紅/綠/黃3狀態(tài)指示有源蜂鳴器1事件提示4位撥碼開關2ID設置主控是Hi3863RISC-V 32bit240MHz集成Wi-Fi6/BLE/星閃SLE606KB SRAM4MB Flash。跑LiteOS實時操作系統(tǒng)代碼結構diansai2/ ├── README.md # 項目文檔 ├── LICENSE # CC BY-NC-SA 4.0 ├── diansai2.h # 公共定義 ├── diansai2_key.c # 鑰匙端SLE廣播 ID Notify ├── diansai2_lock.c # 門鎖端SLE接收 UART測距 卡爾曼 OLED 狀態(tài)機 ├── diansai2_oled.c/.h # SSD1306軟件I2C驅動 ├── diansai2_oled_font.h # 6x8點陣字庫 ├── CMakeLists.txt # 構建配置 ├── Kconfig # 引腳配置 └── stm32_uwb/ # STM32 UWB測距固件 ├── 標簽/ # Tag固件輪詢4錨點 └── 基座/ # Anchor固件測距UART輸出開源項目地址https://gitcode.com/gcw_rVSV2mp6/diansai-C硬件開源地址2026電賽C題-小熊派解決方案 - 立創(chuàng)開源硬件平臺包含H3863鑰匙端門鎖端完整固件STM32 UWB標簽錨點完整固件含DW1000驅動README文檔 項目介紹許可證CC BY-NC-SA 4.0署名-非商業(yè)性使用-相同方式共享可以學習、研究、修改、分享但不能商用衍生作品也要用同樣的許可證總結這個項目幾個關鍵設計決策星閃SLE替代BLE新技術嘗鮮延遲更低SDK好用STM32H3863架構分離UWB測距交給STM32H3863專注通信和邏輯多級濾波卡爾曼野值剔除兩層防護差分平方定位計算量小不需要解非線性方程事件驅動狀態(tài)機蜂鳴器只在區(qū)域變化時響不吵做電賽最大的收獲不是最后的結果而是踩坑的過程。UWB時序、卡爾曼調參、角度環(huán)繞、OLED花屏……每個坑都花了不少時間但解決之后對整個系統(tǒng)的理解會深很多希望這篇分享對大家有幫助代碼已經開源歡迎交流本文項目開源地址https://gitcode.com/gcw_rVSV2mp6/diansai-C許可證CC BY-NC-SA 4.0禁止商用