news 2026/9/12 2:47:33

ESP32蓝牙Beacon测距实战:RSSI校准与滤波工程化实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32蓝牙Beacon测距实战:RSSI校准与滤波工程化实现

1. 项目概述:为什么测距不是“读个RSSI就完事”?

你手上有一块ESP32,VSCode里搭好了ESP-IDF开发环境,也成功跑通了BLE广播和扫描——但当你把手机APP显示的“距离:2.3米”截图发到群里,老同事只回了一句:“这数字信得过?”
这就是本讲要直面的问题:蓝牙Beacon测距,本质上不是硬件功能,而是信号传播建模+环境校准+数据滤波的系统工程。它不依赖ESP32芯片本身有多强,而取决于你是否理解RSSI值背后隐藏的物理现实:信号在空气中衰减不是线性的,一堵墙、一杯水、甚至你手腕上戴的手表,都会让RSSI跳变5~10dBm。我去年调试一个仓库资产定位系统,同一块ESP32 Beacon在空旷走廊测距误差±0.8米,进到堆满金属货架的库房后,误差直接飙到±4.2米——不是代码写错了,是没做环境适配。

标题里的“第六讲”很关键:前五讲应该已覆盖ESP-IDF环境搭建、VSCode插件配置(特别是C/C++ Extension Pack和ESP-IDF Extension)、BLE广播帧构造(iBeacon/Eddystone格式)、主动扫描逻辑、以及GATT服务基础。本讲默认你已能用esp_ble_gap_start_advertising()发广播、用esp_ble_gap_set_scan_params()扫设备、并从ESP_GAP_BLE_SCAN_RESULT_EVT事件中提取adv_datarssi。现在我们要做的,是把那个原始RSSI数值,变成真正可用的距离参考值。核心关键词“ESP-IDF+VSCode+ESP32+蓝牙+beacon”不是简单罗列,而是指明技术栈边界:所有实现必须基于官方ESP-IDF BLE API(v5.1/v5.2/v5.3),不引入第三方SDK;VSCode仅作为编辑器和调试前端,不依赖任何图形化BLE工具;测距逻辑全部用C语言在ESP32端完成,不依赖手机APP或云端计算。

适合谁看?如果你正用ESP32做室内定位节点、资产追踪标签、或者需要粗略判断用户靠近/远离设备的交互场景(比如门禁感应、展台互动),本讲给你的不是“理论距离公式”,而是可直接编译烧录、在真实环境中调参验证的完整方案。新手能照着步骤跑通基础测距,有经验的开发者能深入理解每个参数的物理意义和调整逻辑。我们不讲蓝牙协议栈分层(那是Spec文档的事),只聚焦“怎么让ESP32输出一个靠谱的距离数字”。

2. 核心原理拆解:RSSI到距离,中间隔着三道墙

2.1 RSSI的本质:它根本不是“信号强度”,而是“接收机底噪之上的相对值”

很多初学者以为RSSI是像Wi-Fi信号格数那样直观的强度指标,其实完全相反。在ESP32的BLE控制器中,RSSI是基带处理器在特定时间窗口内,对ADC采样值做能量积分后,与内部参考电平比较得出的归一化数值。它的单位是dBm,但这个dBm不是绝对功率值,而是相对于芯片内部LNA(低噪声放大器)增益设定的相对量。这意味着:

  • 同一块ESP32,如果修改了CONFIG_BTDM_CTRL_BR_EDR_MAX_TX_POWER(经典蓝牙发射功率),RSSI读数会偏移;
  • 同一块板子,如果天线走线被铜箔覆盖(比如PCB设计时没做天线净空区),RSSI会系统性偏低3~5dBm;
  • 不同批次的ESP32-WROOM-32模块,因射频前端匹配电路微小差异,RSSI基准值可能相差±2dBm。

我实测过10片同型号模块,在无遮挡1米距离下扫描同一Beacon,RSSI读数分布范围是-58dBm ~ -63dBm。这不是Bug,是射频硬件的固有离散性。所以,任何不带校准的RSSI-to-distance转换都是空中楼阁。标题中“蓝牙beacon测距”的“测”字,核心动作就是校准——不是一次性的,而是针对具体部署环境的持续校准。

2.2 经典路径损耗模型:为什么Log-Distance模型是唯一可行选择

理论上,自由空间路径损耗公式为:
PL(d) = PL(d₀) + 10n·log₁₀(d/d₀)
其中PL(d₀)是参考距离d₀(通常取1米)处的路径损耗,n是路径损耗指数(自由空间n=2,室内走廊n≈2.2~2.8,金属仓库n≈4~6)。

这个公式看似完美,但问题在于:

  • PL(d₀)无法直接测量——你没法把手机贴在ESP32天线上测1米RSSI,因为近场耦合效应会让读数失真;
  • n值高度依赖环境,混凝土墙、玻璃幕墙、人体组织对2.4GHz信号的衰减机制完全不同;
  • ESP32的RSSI是整数(-127 ~ 0),精度只有1dBm,而公式中log₁₀(d)对小数点后两位敏感,微小的RSSI误差会导致距离计算成倍放大。

因此,工业级方案都采用经验校准法:在目标部署现场,用激光测距仪标定多个已知距离点(如0.5m, 1m, 2m, 5m),记录对应RSSI均值,拟合出该环境下的nPL(d₀)。本讲提供的代码框架内置了三段式校准模式:

  1. 快速校准:仅需测1米点,假设n=2.5(典型室内值),适合原型验证;
  2. 标准校准:测3个点(1m/3m/5m),用最小二乘法拟合直线,平衡精度与工作量;
  3. 高级校准:测5个点以上,支持分段线性插值,应对复杂多径环境。

提示:校准必须在设备最终安装位置进行。把Beacon放在桌上校准,再装到天花板上,结果会偏差30%以上——因为天线朝向改变导致辐射图畸变。

2.3 为什么不能只用单次RSSI?——噪声、多径、设备差异的三重干扰

单次RSSI读数波动极大,我抓取过连续100次扫描数据:

  • 空旷环境:标准差±3.2dBm
  • 有人员走动:标准差±5.8dBm
  • 靠近金属物体:标准差±8.1dBm

这种波动直接映射到距离计算上:按Log-Distance模型,RSSI变化10dBm对应距离变化10^(10/(10n))倍。当n=2.5时,±3dBm波动就导致距离估算偏差±40%!更糟的是,不同手机/笔记本的BLE接收灵敏度差异可达15dBm,同一Beacon被iPhone和小米手机扫描,RSSI可能相差8dBm——这说明Beacon测距必须在固定硬件平台上闭环验证,即ESP32扫描ESP32 Beacon,排除终端设备差异。

解决方案是时间域滤波+空间域滤波双保险

  • 时间域:对连续N次RSSI采样做滑动窗口中值滤波(Median Filter),比均值滤波更能抑制脉冲噪声;
  • 空间域:部署多个Beacon形成三角定位,用几何约束剔除异常RSSI(如三点距离和远大于实际边长);
    本讲聚焦单Beacon基础测距,所以重点实现时间域滤波。代码中RSSI_WINDOW_SIZE默认设为20,意味着每秒更新1次距离值(扫描间隔50ms),既保证实时性,又足够平滑噪声。

3. VSCode+ESP-IDF实操:从零构建可调参测距工程

3.1 工程创建与依赖配置:避开ESP-IDF版本陷阱

先确认你的ESP-IDF版本。标题中未指定,但网络热词提到v5.4.4v6.0,本讲以ESP-IDF v5.3.2为基准(这是当前最稳定的LTS版本,v5.4.x存在BLE扫描中断偶发bug)。在VSCode中打开命令面板(Ctrl+Shift+P),输入ESP-IDF: Create Project,填写:

  • Project name:ble_beacon_ranging
  • Board:ESP32 DevKitC(或其他你使用的板型)
  • ESP-IDF Path: 指向你的~/esp/esp-idf目录

关键一步:修改sdkconfig中的BLE配置。在VSCode中按Ctrl+Shift+PESP-IDF: SDK Configuration Editor,找到:

  • Component config → Bluetooth → Bluedroid Options → Enable Bluetooth controller→ ✅
  • Component config → Bluetooth → Bluedroid Options → Enable Bluetooth Host→ ✅
  • Component config → Bluetooth → Bluedroid Options → BLE maximum number of connections→ 设为1(测距只需扫描,不建链)
  • Component config → Bluetooth → Bluedroid Options → BLE scan duplicate elimination→ ❌(必须关闭!否则重复Beacon只上报一次,无法做连续RSSI统计)

注意:BLE scan duplicate elimination默认开启,这是初学者最大坑点。开启后,即使Beacon每100ms广播一次,ESP32也只触发一次ESP_GAP_BLE_SCAN_RESULT_EVT事件。关闭后,每次广播都能捕获,才能做时间序列滤波。

3.2 核心代码结构:四个文件的职责划分

整个测距逻辑拆分为四个文件,符合ESP-IDF模块化规范:

  • main/ranging_main.c:主循环和事件分发,只做初始化和状态机调度;
  • main/beacon_scanner.c:封装BLE扫描逻辑,包括参数设置、事件回调、RSSI缓存;
  • main/ranging_calculator.c:核心算法实现,含校准、滤波、距离转换;
  • main/ranging_config.h:所有可调参数集中管理,方便OTA升级时动态修改。

ranging_config.h内容示例:

#ifndef RANGING_CONFIG_H #define RANGING_CONFIG_H // 校准参数(出厂默认,现场需覆盖) #define CALIBRATION_REF_DISTANCE_M 1.0f #define CALIBRATION_PATH_LOSS_EXPONENT 2.5f // 滤波参数 #define RSSI_WINDOW_SIZE 20 #define RSSI_MEDIAN_FILTER_DEPTH 3 // 距离输出参数 #define DISTANCE_UPDATE_INTERVAL_MS 1000 #define DISTANCE_MIN_M 0.3f #define DISTANCE_MAX_M 10.0f // Beacon识别(按MAC地址白名单过滤) #define BEACON_MAC_ADDR "A1:B2:C3:D4:E5:F6" #endif

这种分离设计的好处是:算法工程师只改ranging_calculator.c,硬件工程师只调ranging_config.h,互不干扰。VSCode的IntelliSense能自动索引所有宏定义,修改参数后无需全局搜索。

3.3 Beacon扫描实现:如何稳定捕获RSSI序列

beacon_scanner.c的核心是gap_event_handler()回调函数。关键细节:

  • 扫描参数必须设为ESP_BLE_Scan_Type_PASSIVE:主动扫描(ACTIVE)会发送SCAN_REQ帧,可能干扰Beacon广播,且增加功耗;被动扫描只收不发,RSSI更稳定。
  • 扫描窗口和间隔要平衡:设scan_interval=0x0080(128 * 0.625ms = 80ms),scan_window=0x0050(80ms),即80ms扫描80ms,占空比100%。虽然功耗高,但确保不漏包。
  • Beacon识别逻辑:不要只靠adv_data中的UUID匹配,必须结合MAC地址。因为有些Beacon(如iBeacon)广播帧不包含UUID,或UUID被加密。代码中用memcmp(scan_rst->bda, target_mac, 6)精确比对。

实测发现:某些低成本Beacon广播间隔不稳(标称100ms,实测80~150ms随机),导致RSSI序列出现空洞。解决方案是在ranging_calculator.c中加入超时补全机制:若连续300ms无新RSSI,则用最近有效值填充,避免距离跳变。

3.4 测距算法实现:从RSSI到距离的完整转换链

ranging_calculator.c中的calculate_distance_from_rssi()函数是核心,分四步执行:

  1. RSSI滤波:将新RSSI加入环形缓冲区,调用median_filter()计算中值。中值滤波代码精简版:
int8_t median_filter(int8_t *window, int size) { int8_t temp[size]; memcpy(temp, window, size * sizeof(int8_t)); // 简单冒泡排序(size=20,开销可忽略) for (int i = 0; i < size; i++) { for (int j = 0; j < size - 1 - i; j++) { if (temp[j] > temp[j + 1]) { int8_t t = temp[j]; temp[j] = temp[j + 1]; temp[j + 1] = t; } } } return temp[size / 2]; // 中值 }
  1. 校准补偿:用当前校准参数计算理论路径损耗:
    float pl_d0 = rssi_filtered + 10 * n * log10(ref_distance);
    注意:log10()在ESP-IDF中需链接libm.a,在CMakeLists.txt中添加target_link_libraries(${COMPONENT_TARGET} m)
  2. 距离反解:由PL(d) = PL(d₀) + 10n·log₁₀(d/d₀)变形得:
    distance_m = ref_distance * pow(10, (pl_d0 - rssi_filtered) / (10 * n));
  3. 边界裁剪:限制输出在DISTANCE_MIN_MDISTANCE_MAX_M之间,避免数学溢出。

实操心得:pow(10, x)在嵌入式平台有精度损失,当x>3时误差增大。我改用查表法:预计算10^0.0, 10^0.1, ..., 10^3.0共31个值存入const数组,用线性插值求解,精度提升40%,CPU占用降低60%。

4. 环境校准实战:手把手教你做出±0.5米精度

4.1 校准前准备:三个必须检查的硬件条件

校准不是软件行为,而是物理实验。开始前务必确认:

  • 天线一致性:Beacon和Scanner必须使用相同天线类型。如果Beacon用PCB天线,Scanner就不能用外接IPEX天线——辐射方向图差异会导致角度敏感性。我曾用外接天线Scanner测PCB天线Beacon,在±30°偏角时RSSI变化达6dBm,等效距离误差±1.2米。
  • 供电稳定性:ESP32的RF性能对电压极敏感。用USB供电时,插入/拔出瞬间电压跌落可能导致BLE模块复位。校准时必须用稳压电源(3.3V±0.05V),或至少用优质USB-C线缆+带PD协议的充电头。
  • 环境静默:关闭附近所有Wi-Fi路由器、蓝牙音箱、微波炉。2.4GHz频段拥挤度直接影响RSSI底噪。用WiFi AnalyzerAPP扫描周围信道占用率,选择占用率<20%的时段校准。

4.2 三阶段校准操作流程

阶段一:快速校准(5分钟,精度±1.5米)
  1. 将Beacon固定在1米距离(用激光测距仪确认),Scanner置于同一水平面;
  2. 运行程序,等待RSSI稳定(约30秒),记录rssi_filtered值(如-62);
  3. 修改ranging_config.h
#define CALIBRATION_REF_DISTANCE_M 1.0f #define CALIBRATION_PATH_LOSS_EXPONENT 2.5f // 计算PL(d₀) = -62 + 10*2.5*log10(1) = -62 // 直接赋值,避免运行时计算 #define CALIBRATION_PL_D0_DBM -62
  1. 重新编译烧录,此时1米处距离输出应为0.95~1.05米。
阶段二:标准校准(20分钟,精度±0.7米)
  1. 在0.5m/1m/3m/5m四个点分别测量,每个点采集60秒RSSI序列;
  2. 对每组数据计算中值(非均值!中值抗脉冲噪声);
  3. 用Excel做线性拟合:横轴log10(distance),纵轴rssi_median,斜率即-10n,截距即PL(d₀)
  4. 将拟合结果填入ranging_config.h。例如拟合得rssi = -45.2 - 24.8*log10(d),则n=2.48PL(d₀)= -45.2(d₀=1m)。
阶段三:高级校准(1小时+,精度±0.5米)
  1. 增加2m/4m点,共6个距离点;
  2. 对每个点,沿垂直于连线的方向左右各偏移0.3m,测3组RSSI(模拟用户行走轨迹);
  3. 构建二维查找表:distance_table[6][3],行是距离,列是角度偏移;
  4. 运行时根据当前RSSI查表+线性插值。此方法在展厅人流密集场景下,将平均误差从±1.2米降至±0.4米。

4.3 校准结果验证:用“距离跳变率”代替绝对误差

现场部署后,不能只看某次测量是否准确,要看距离跳变率(Distance Jump Rate):

  • 定义:连续10次距离更新中,相邻两次差值>0.5米的次数占比;
  • 合格标准:空旷环境<5%,室内环境<15%;
  • 超标处理:若跳变率高但RSSI稳定,说明n值过小(模型欠拟合),需增大CALIBRATION_PATH_LOSS_EXPONENT;若RSSI本身抖动大,检查天线接地或电源纹波。

我用此方法诊断出一个案例:客户反馈测距忽远忽近。抓取RSSI发现标准差仅±1.8dBm,但跳变率高达40%。检查发现CALIBRATION_PATH_LOSS_EXPONENT被误设为1.8(应为2.5),导致模型过度敏感——RSSI微小变化被放大为距离大幅跳变。

5. 常见问题排查:那些让你熬夜到凌晨的“灵异现象”

5.1 RSSI始终为0或-127:硬件级故障定位

RSSI异常是测距失败的第一征兆。按优先级排查:

现象可能原因快速验证法解决方案
RSSI恒为0BLE扫描未启动esp_ble_gap_get_scan_state()返回ESP_BLE_SCAN_STATE_IDLE检查esp_ble_gap_start_scanning()返回值,确认无ESP_ERR_INVALID_ARG
RSSI恒为-127接收机未锁定用逻辑分析仪抓GPIO12(BLE RF enable)是否周期性拉高更换CONFIG_BTDM_CTRL_BR_EDR_MAX_TX_POWER0(最低功率),排除LNA饱和
RSSI随机跳变天线虚焊用手轻压天线馈点,观察RSSI是否突变重新焊接天线匹配电路,尤其注意Pi匹配网络的电容焊点

注意:RSSI=-127在ESP-IDF中表示“未检测到有效信号”,不是硬件损坏。常见于扫描窗口过短(scan_window < 10ms)或Beacon广播间隔过长(>2s)。

5.2 距离值卡死不动:事件循环阻塞分析

现象:程序运行后距离一直显示1.00m,不再更新。根源通常是事件队列溢出。ESP-IDF的BLE事件通过esp_event_loop分发,若回调函数执行时间过长(>5ms),后续事件会被丢弃。

  • 检查点:ranging_calculator.c中是否在calculate_distance_from_rssi()里调用了printf()vTaskDelay()
  • 解决方案:所有耗时操作(如浮点运算、查表)必须在FreeRTOS任务中异步执行。我在ranging_main.c中创建独立任务:
static void ranging_task(void *pvParameters) { while(1) { // 从队列获取RSSI,计算距离,发布到LED或UART calculate_and_publish_distance(); vTaskDelay(pdMS_TO_TICKS(DISTANCE_UPDATE_INTERVAL_MS)); } } // 在app_main()中:xTaskCreate(ranging_task, "ranging", 4096, NULL, 5, NULL);

5.3 多Beacon干扰:如何让Scanner“认准一个人”

当环境中存在多个Beacon时,gap_event_handler()会频繁触发,导致:

  • RSSI缓冲区被不同Beacon数据污染;
  • 距离计算逻辑混乱。

解决方案是MAC地址白名单+信号强度门限

// 在beacon_scanner.c中 if (memcmp(scan_rst->bda, target_mac, 6) != 0) { return; // 忽略非目标Beacon } if (scan_rst->rssi < -80) { // 门限值,排除远距离干扰 return; } // 此时才存入RSSI缓冲区

门限值-80需根据现场测试调整。空旷环境可设-75,金属环境设-85。实测表明,合理设置门限可将误触发率降低90%。

5.4 VSCode调试陷阱:为什么断点总在错误位置

VSCode的ESP-IDF调试器有时显示“断点未命中”,根本原因是:

  • 优化等级冲突sdkconfigCompiler options → Optimization level设为-O2-O3时,编译器会内联函数、重排指令,导致源码行号与机器码错位;
  • 解决方法:调试时强制设为-Og(专为调试优化),发布时再切回-O2
  • 额外技巧:在关键函数开头加__attribute__((used)),防止编译器优化掉调试符号。

我曾为一个median_filter()函数调试2小时,最后发现是-O3把整个函数内联了,VSCode找不到对应汇编地址。改成-Og后,断点立即命中。

6. 进阶扩展:从单点测距到实用定位系统

6.1 三角定位:用三块ESP32实现亚米级定位

单Beacon测距只能判断“远/近”,要获得坐标需三角定位。方案:

  • 固定三台ESP32 Scanner(A/B/C)在已知坐标点(如房间三顶点);
  • 移动Beacon发出广播,三台Scanner同步测量RSSI;
  • 每台Scanner将{rssi, timestamp}通过ESP-NOW发给中心节点;
  • 中心节点用TDOA(到达时间差)或RSSI加权质心法解算坐标。

关键优化:

  • 时间同步:用ESP-NOW的esp_now_send()自带时间戳,精度±10μs;
  • RSSI权重:距离越近权重越高,权重公式w_i = 1 / distance_i²
  • 坐标解算:不用复杂矩阵运算,用几何法:
    // 简化质心法(适合平面部署) float x = (w_a*x_a + w_b*x_b + w_c*x_c) / (w_a + w_b + w_c); float y = (w_a*y_a + w_b*y_b + w_c*y_c) / (w_a + w_b + w_c);

6.2 OTA动态校准:让设备自己学习环境

现场部署后,环境可能变化(如装修添置家具)。传统方案需工程师上门重校准,成本高。可行方案:

  • ranging_main.c中添加HTTP服务器,暴露/calibrate?rssi=-62&distance=1.0接口;
  • 手机APP扫码进入校准模式,用户输入已知距离,设备自动更新CALIBRATION_PL_D0_DBM
  • 新参数存入NVS(Non-Volatile Storage),重启生效。

安全考虑:校准接口必须加Token验证,且仅允许本地网络访问。我用SHA256哈希设备MAC生成Token,杜绝远程篡改。

6.3 功耗优化:电池供电Beacon的续航秘籍

若Beacon由CR2032电池供电,待机功耗决定寿命。关键措施:

  • 广播间隔最大化:设adv_int_min=0x0800(2048 * 0.625ms = 1.28s),比默认100ms省电12倍;
  • 关闭不必要的广播字段:只保留AD Type 0x01(Flags)和0xFF(Manufacturer Data),删掉0x09(Complete Local Name);
  • 深度睡眠唤醒:用RTC Timer定时唤醒,广播后立即进入esp_sleep_enable_timer_wakeup(1280000)(1.28s)。

实测:优化后CR2032电池续航从3个月提升至14个月,满足大部分资产标签需求。

最后分享一个小技巧:在VSCode中为ranging_config.h创建代码片段(Snippets),输入rconf自动展开所有参数模板。这样每次新建工程,5秒内就能完成参数初始化,把精力留给真正的环境适配。毕竟,再完美的算法,也抵不过一次扎实的现场校准——这才是“联网篇”第六讲想传递的终极心法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 2:47:18

MQL5直连CTP柜台:桥接DLL设计、行情订阅与下单实战

简介&#xff1a;基于MQL5语言的MT5客户端直连期货公司CTP柜台程序化交易源码&#xff0c;覆盖行情接入、交易下单、持仓查询、风险控制与数据中心等模块&#xff0c;适合有一定编程基础的期货量化交易者用于搭建或改造自动化交易客户端。压缩包共60个文件&#xff0c;约29.1MB…

作者头像 李华
网站建设 2026/9/12 2:47:15

Apple Silicon Mac 安装 MySQL 完全指南(dmg 与 Homebrew 双教程)

先把话说明白&#xff1a;这是 M 芯片系列教程的第四篇。前面几篇我陆续写了 M1/M2/M3 环境下 Homebrew、Python、Git 这些基础开发的安装折腾过程&#xff0c;这一篇终于轮到数据库了。这个系列默认你手上是一台 Apple Silicon 的 MacBook&#xff0c;也就是 M1、M2、M3 或者 …

作者头像 李华
网站建设 2026/9/12 2:46:08

IntelliJ IDEA社区版:免费轻量Java开发IDE完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 2:46:07

Java并发队列全解析:从BlockingQueue到线程池选型实战

聊到Java里的Queue&#xff0c;很多人第一反应是“这不就是个队列嘛&#xff0c;LinkedList也能用”。但真正到了并发场景&#xff0c;或者面试被问到“线程池的阻塞队列怎么选”时&#xff0c;才发现这里面水很深。我见过不少生产事故&#xff0c;明明线程池参数看着没问题&am…

作者头像 李华
网站建设 2026/9/12 2:45:46

从VSCode插件到Electron独立应用:打字游戏架构改造实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 2:45:39

MCP协议:AI工程化中的服务契约与工具治理标准

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华