[PSOC™] 【英飞凌 CY8CKIT-062S2-AI评测】基于MicroPython实现雷达存在检测

[复制链接]
266|2
傅沈骁 发表于 2025-11-14 22:28 | 显示全部楼层 |阅读模式
, ,
本帖最后由 傅沈骁 于 2025-11-14 22:48 编辑

开发板上的雷达芯片为XENSIV 60GHz radar sensor,相关介绍可以参见https://www.infineon.com/part/BGT60TR13C
为了便于快速开发,本文尝试基于MicroPython实现板载雷达的人体存在检测
首先在英飞凌的MicroPython固件包中选择对应开发板固件

接着在ModusToolbox Programmer中烧录对应的MicroPython固件

打开Thonny,选择解释器为MicroPython(通用)

可以看到芯片已经识别成功



接着开始编写代码根据User Guide,可以看到雷达相关引脚如下


定义引脚,定义雷达帧长度、ADC分频、起始频率、带宽、检测最小/最大距离
  1. ========= 引脚 =========
  2. PIN_RSPI_MOSI = 'P12_0'
  3. PIN_RSPI_MISO = 'P12_1'
  4. PIN_RSPI_SCK  = 'P12_2'
  5. PIN_RSPI_CS   = 'P12_3'
  6. PIN_RSPI_IRQ  = 'P11_0'
  7. PIN_RXRES_L   = 'P11_1'
  8. PIN_LED_MACRO = 'P5_3'
  9. PIN_LED_MICRO = 'P5_4'

  10. # ========= 参数(现场可调) =========
  11. WORD_SIZE        = 512
  12. ADC_DIV          = 16
  13. START_FREQ_KHZ   = 61_000_000
  14. BANDWIDTH_KHZ    = 1_000_000

  15. MIN_DIST_M       = 0.30
  16. MAX_DIST_M       = 6.00

定义宏存在判定(SNR阈值+滞后+保持时间),定义微存在判定(短窗能量的变异系数CV),定义基线(背景能量)更新与冻结,以及其他一些参数定义
  1. # 宏存在:用 SNR(dB) 滞后
  2. PRESENCE_ON_DB   = 5.5    # <-- 下调
  3. PRESENCE_OFF_DB  = 3.5
  4. HOLD_MACRO_SEC   = 8.0

  5. # 微存在:用短窗变异系数 CV = std/mean
  6. MICRO_ON_CV      = 0.10   # 进入微存在
  7. MICRO_OFF_CV     = 0.04   # 退出微存在
  8. HOLD_MICRO_SEC   = 10.0

  9. # 基线学习率(可冻结)
  10. ALPHA_ABSENT     = 0.02
  11. ALPHA_PRESENT    = 0.002
  12. FREEZE_BASELINE_WHEN_PRESENT = True

  13. # 短窗/循环
  14. SHORT_N          = 10
  15. LOG_INTERVAL_MS  = 80
  16. USE_FFT          = True

  17. # 周期性 FIFO 清理
  18. FIFO_RESET_PERIOD = 200
  19. # 汉宁窗(抑制旁瓣)
  20. USE_HANN_WINDOW   = True
  21. # ROI 跟随(峰值 ±bins)
  22. USE_ROI_FOLLOW    = True
  23. ROI_HALF_BINS     = 4     # ≈ ±0.6 m @ 0.151 m/bin

  24. # 峰值距离滑动平均
  25. PEAK_MA_N         = 5

定义一些基础工具函数,用于计算复数幅度以及构建SPI、构建雷达对象以及做硬复位脉冲
  1. def mag(re, im):
  2.     return (re*re + im*im) ** 0.5

  3. def build_spi():
  4.     return SPI(
  5.         baudrate=50_000_000, polarity=0, phase=0, bits=8,
  6.         sck=Pin(PIN_RSPI_SCK), mosi=Pin(PIN_RSPI_MOSI), miso=Pin(PIN_RSPI_MISO)
  7.     )

  8. def build_radar(spi):
  9.     return BGT60TRxxModule(
  10.         word_size=WORD_SIZE, spi_interface=spi, chip_select=PIN_RSPI_CS,
  11.         reset=PIN_RXRES_L, interrupt_pin=PIN_RSPI_IRQ, interrupt_handler=None
  12.     )

  13. def hw_reset_pulse(radar):
  14.     radar.reset_radar.value(0); time.sleep_ms(2)
  15.     radar.reset_radar.value(1); time.sleep_ms(10)

写位的控制操作
  1. def start_frame_direct(radar):
  2.     radar.write_reg(BGTCONST.MAIN_ADDR, BGTCONST.START_FRAME)
  3.     time.sleep_ms(1)

  4. def fifo_reset_direct(radar):
  5.     radar.write_reg(BGTCONST.MAIN_ADDR, BGTCONST.FIFO_RESET)
  6.     time.sleep_ms(1)
  7.     radar.write_reg(BGTCONST.MAIN_ADDR, 0x00)

雷达初始化,其中需要设定ADC分频/chirp长度/频率参数,调节前端增益,初始化寄存器表,然后复位FIFO并启动第一帧
  1. def init_radar_full(radar, n_fsu, n_rtu, n_rsu):
  2.     hw_reset_pulse(radar)
  3.     radar.reset(); time.sleep_ms(5)  # 软复位(直接写)

  4.     radar.set_adc_div(ADC_DIV)
  5.     radar.set_chirp_len(WORD_SIZE)
  6.     radar.configure_chirp(n_fsu, n_rtu, n_rsu)
  7.     radar.set_vga_gain_ch1(3)
  8.     radar.set_vga_gain_ch2(3)
  9.     radar.set_vga_gain_ch3(3)
  10.     radar.setCompareValue(75)

  11.     radar.initSensor(); time.sleep_ms(5)  # 整表写
  12.     fifo_reset_direct(radar)
  13.     start_frame_direct(radar)

计算能量与峰值

  1. def compute_metrics_fft(radar, i0, i1):
主检测循环与状态机
  1. def run_detection(radar, rng_res, i0, i1, n_fsu, n_rtu, n_rsu, use_fft=True):
  2.     led_macro = Pin(PIN_LED_MACRO, Pin.OUT, value=0)
  3.     led_micro = Pin(PIN_LED_MICRO, Pin.OUT, value=0)

  4.     STATE_ABSENT, STATE_PRESENT_MACRO, STATE_PRESENT_MICRO = 0, 1, 2
  5.     state = STATE_ABSENT

  6.     baseline = 0.0
  7.     # ROI 能量短窗缓冲
  8.     short_buf = [0.0] * SHORT_N; short_i = 0
  9.     hold_macro_sec = 0.0; hold_micro_sec = 0.0
  10.     prev_ms = time.ticks_ms()
  11.     frame_count = 0

  12.     # 峰值距离滑动平均
  13.     peak_ma_buf = [0.0] * PEAK_MA_N; peak_ma_i = 0

预热阶段(假设无人),这一步用于快速建立背景基线
  1.     # 预热(无人)
  2.     for _ in range(30):
  3.         try:
  4.             start_frame_direct(radar)
  5.             e_full, e_roi, _, _ = (compute_metrics_fft(radar, i0, i1) if use_fft
  6.                                    else compute_metrics_nfft(radar, i0, i1))
  7.         except Exception as e2:
  8.             safe_recover(radar, e2, n_fsu, n_rtu, n_rsu); e_roi = 0.0
  9.         baseline = (1 - ALPHA_ABSENT) * baseline + ALPHA_ABSENT * e_roi
  10.         time.sleep_ms(10)
每帧的处理中,需要定期清FIFO,启动新帧,计算能量与峰值,并计算峰值距离与滑动平均
  1.         # 峰值距离与滑动平均
  2.         peak_dist = peak_idx * rng_res
  3.         peak_ma_buf[peak_ma_i] = peak_dist
  4.         peak_ma_i = (peak_ma_i + 1) % PEAK_MA_N
  5.         peak_ma = sum(peak_ma_buf) / PEAK_MA_N
计算SNR(dB)(对ROI能量相对基线)
  1.         # SNR(dB) = 10*log10(E_roi/baseline)
  2.         eps = 1e-9
  3.         snr_db = 10.0 * math.log(e_roi / max(baseline, eps)) / math.log(10.0) if baseline > 0 else 99.0

短窗CV(用于微存在)
  1.         # ---- 宏存在:SNR(dB) 滞后 + 边沿计时 ----
  2.         macro_enter = (snr_db > PRESENCE_ON_DB)
  3.         macro_exit  = (snr_db < PRESENCE_OFF_DB)

  4.         if macro_enter:
  5.             if state != STATE_PRESENT_MACRO:
  6.                 state = STATE_PRESENT_MACRO; hold_macro_sec = 0.0
  7.             else:
  8.                 hold_macro_sec += dt_sec
  9.         elif state == STATE_PRESENT_MACRO:
  10.             hold_macro_sec += dt_sec
  11.             if macro_exit and hold_macro_sec > HOLD_MACRO_SEC:
  12.                 state = STATE_ABSENT; hold_macro_sec = 0.0
微存在状态机(CV 滞后 + 保持时间)
  1.         # ---- 微存在:CV 滞后 + 边沿计时 ----
  2.         micro_enter = (cv > MICRO_ON_CV)
  3.         micro_exit  = (cv < MICRO_OFF_CV)

  4.         if state == STATE_ABSENT and micro_enter:
  5.             state = STATE_PRESENT_MICRO; hold_micro_sec = 0.0
  6.         elif state == STATE_PRESENT_MICRO:
  7.             hold_micro_sec += dt_sec
  8.             if micro_exit and hold_micro_sec > HOLD_MICRO_SEC:
  9.                 state = STATE_ABSENT; hold_micro_sec = 0.0

最后更新LED和打印日志
  1.         # LED
  2.         led_macro.value(1 if state == STATE_PRESENT_MACRO else 0)
  3.         led_micro.value(1 if state == STATE_PRESENT_MICRO else 0)

  4.         # 日志
  5.         print("res={:.3f}m/bin, idx=[{}..{}], E_full={:.2f}, E_roi={:.2f}, base={:.2f}, "
  6.               "SNR={:.2f}dB, short_mean={:.2f}, CV={:.3f}, state={}, holdM={:.1f}s, holdm={:.1f}s, "
  7.               "peak={:.2f}m(ma:{:.2f}m, val:{:.2f})".format(
  8.             rng_res, i0, i1, e_full, e_roi, baseline,
  9.             snr_db, short_mean, cv, state, hold_macro_sec, hold_micro_sec,
  10.             peak_dist, peak_ma, peak_val
  11.         ))

其中各个字段的含义如下:

res:距离分辨率

idx[i0..i1]:参与能量/峰值计算的bin索引范围

E_full:在[i0..i1]全串口上加窗后的平均能量(单位位归一化幅度的平均)

E_roi:在ROI以当前峰值为中心、半宽ROI_HALF_BINS)的平均能量

base:基线能量

SNR(dB):ROI能量相对基线的信噪比

short_mean:短窗中ROI能量的均值

CV:短窗ROI能量的变异系数,用于微存在判定

status:当前存在状态,0为无人,1为宏存在,2为微存在

holdM:宏存在状态下累计的保持时间

holdm:微存在状态下累计的保持时间

peak:当前帧的峰值距离

ma:峰值距离的滑动平均

val:峰值bin的幅度/能量值(加窗后),反映该距离处的反射强度

最终main函数如下
  1. def main():
  2.     spi   = build_spi()
  3.     radar = build_radar(spi)

  4.     n_fsu = calculateFSU(START_FREQ_KHZ)
  5.     n_rtu = calculateRTU(ADC_DIV, WORD_SIZE)
  6.     n_rsu = calculateRSU(BANDWIDTH_KHZ, n_rtu)

  7.     init_radar_full(radar, n_fsu, n_rtu, n_rsu)

  8.     rng_res = radar.get_range_resolution()   # m/bin
  9.     i0 = max(0, int(MIN_DIST_M / rng_res))
  10.     i1 = min(WORD_SIZE - 1, int(MAX_DIST_M / rng_res))

  11.     run_detection(radar, rng_res, i0, i1, n_fsu, n_rtu, n_rsu, use_fft=USE_FFT)

最终输出的效果如下,有些数据处理得不太好,导致这些值都不怎么变化



完整代码文件如下




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
yangxiaor520 发表于 2025-11-15 08:53 来自手机 | 显示全部楼层
用Python编程,编译出来的文件是不是更大

评论

解释语言,不需要编译,但是跑起来肯定比编译的C语言慢不少  发表于 2025-11-15 12:00
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

22

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部