Arduino BLE FTMS 改造室内自行车健身机:从协议到实战
简介这份资源面向嵌入式开发与室内骑行爱好者提供一套基于Arduino与ESP32的BLE FTMS室内自行车健身机完整实现方案用两块ESP32分别模拟功率计与智能骑行台解决商用设备价格高昂、协议封闭的问题。压缩包共30个文件约1.24MB包含ino主程序与h头文件、Python配置脚本、md说明文档以及dxf、stl、f3d等结构件图纸和brd、sch电路设计文件覆盖固件、电路与机械装配全链路。目前已有763人学习下载。读者可获取曲柄功率与踏频采集、FTMS数据转发、电阻螺钉位置控制、12864屏幕显示等核心代码并支持ERG模式调阻、模拟模式换挡以及从SD卡加载阻力曲线配套的图形化配置器可将模拟档位映射为阻力函数便于按需标定骑行手感。适合具备C与ESP32基础、希望深入理解健身设备通信协议的开发者参考。1. 从一台“哑巴”骑行台到能被 App 读懂的 FTMS 设备很多人家里都有一台室内自行车健身机骑起来阻力顺滑、坐垫也舒服但它就是个“哑巴”——码表上的速度、踏频、功率只能自己看手机上的骑行 App 一个都连不上。想让它变成 Zwift、Strava 这类应用能识别的智能骑行台核心就卡在一个协议上FTMSFitness Machine Service。而ble-ftms:Arduino BLE室内自行车健身机这个方向讲的就是用一块支持 BLE 的 Arduino 板子把普通健身机改造成符合 FTMS 规范的蓝牙外设让手机、平板、电脑上的骑行软件把它当成一台标准智能骑行台来读数据、发指令。这件事的价值不在于“炫技”而在于它把一台几千块的设备从封闭生态里解放出来。你不需要换整台车只需要在控制板层面做一次协议翻译把霍尔传感器或光电传感器的脉冲换算成踏频和速度把阻力旋钮或电磁刹车映射成 FTMS 的阻力控制点再通过 BLE 的 GATT 服务暴露出去。适合谁适合手里有 Arduino、会一点 C、愿意看蓝牙 GATT 文档的硬件爱好者也适合做健身设备固件的工程师想快速验证 FTMS 兼容性。下面我按“协议先立住、再动手复现”的顺序把这条路走一遍。2. FTMS 协议栈拆解从 GATT 服务到室内自行车数据特征2.1 为什么是 FTMS而不是自己定义一串私有 UUID蓝牙低功耗BLE里设备之间靠 GATT通用属性协议通信。GATT 把数据组织成“服务Service”和“特征Characteristic”每个服务、每个特征都有一个 UUID。如果你自己拍脑袋定一串 UUID手机 App 根本不认识除非你同时改 App 端代码。FTMS 是蓝牙技术联盟定义的标准服务UUID 固定为0x1826里面规定了健身机应该暴露哪些特征、每个特征的数据格式是什么。主流骑行 App 只要看到0x1826就会按标准去读踏频、速度、功率并往控制点写阻力指令。选 FTMS 的另一个理由是“互操作性”。你不需要为每个 App 写适配层只要你的设备符合规范任何支持 FTMS 的软件都能连。常见做法是先实现 Indoor Bike Data 特征UUID0x2AD2把瞬时速度、踏频、功率按位域打包再实现 Fitness Machine Control PointUUID0x2AD9用来接收 App 发来的“设置目标阻力”“开始训练”“停止”等指令最后加上 Fitness Machine FeatureUUID0x2ACC和 Fitness Machine StatusUUID0x2ADA告诉 App 你支持哪些能力、当前状态是什么。2.2 Indoor Bike Data 的位域怎么填一个字节都不能错Indoor Bike Data 是一个变长特征前两个字节是“标志位Flags”后面跟着实际数据。标志位决定了哪些字段存在、哪些字段省略。比如 bit 0 表示“瞬时速度是否有效”bit 1 表示“平均速度是否存在”bit 2 表示“瞬时踏频是否存在”bit 3 表示“平均踏频是否存在”bit 4 表示“总距离是否存在”bit 5 表示“阻力等级是否存在”bit 6 表示“瞬时功率是否存在”bit 7 表示“平均功率是否存在”等等。你填错一位App 解析出来的数据就会整体错位表现就是“速度显示成踏频”或者“功率一直是 0”。我一般会先确定要上报哪些字段瞬时速度、瞬时踏频、瞬时功率、总距离、阻力等级。对应的标志位就是 bit 0、bit 2、bit 4、bit 5、bit 6 置 1其余置 0。然后按顺序把数据塞进去速度是 uint16单位 0.01 km/h踏频是 uint16单位 0.5 rpm总距离是 uint24单位米阻力等级是 sint16功率是 sint16单位瓦特。注意字节序是小端Little EndianArduino 上可以用lowByte和highByte手动拼也可以直接操作结构体但结构体对齐要小心。// 构造 Indoor Bike Data 数据包 uint8_t indoorBikeData[16]; uint8_t index 0; // Flags: 瞬时速度(bit0) 瞬时踏频(bit2) 总距离(bit4) 阻力(bit5) 瞬时功率(bit6) uint16_t flags 0x0000; flags | (1 0); // Instantaneous Speed Present flags | (1 2); // Instantaneous Cadence Present flags | (1 4); // Total Distance Present flags | (1 5); // Resistance Level Present flags | (1 6); // Instantaneous Power Present indoorBikeData[index] lowByte(flags); indoorBikeData[index] highByte(flags); // 瞬时速度单位 0.01 km/h例如 25.00 km/h - 2500 uint16_t speed 2500; indoorBikeData[index] lowByte(speed); indoorBikeData[index] highByte(speed); // 瞬时踏频单位 0.5 rpm例如 90 rpm - 180 uint16_t cadence 180; indoorBikeData[index] lowByte(cadence); indoorBikeData[index] highByte(cadence); // 总距离uint24单位米例如 12345 米 uint32_t totalDistance 12345; indoorBikeData[index] totalDistance 0xFF; indoorBikeData[index] (totalDistance 8) 0xFF; indoorBikeData[index] (totalDistance 16) 0xFF; // 阻力等级sint16例如 5 int16_t resistance 5; indoorBikeData[index] lowByte(resistance); indoorBikeData[index] highByte(resistance); // 瞬时功率sint16单位瓦特例如 200W int16_t power 200; indoorBikeData[index] lowByte(power); indoorBikeData[index] highByte(power); // 最终通过 BLE 特征 notify 出去 // bleIndoorBikeData.setValue(indoorBikeData, index); // bleIndoorBikeData.notify();这段代码的关键在于标志位和数据字段必须一一对应。如果你把 bit 4 置 1 但没填总距离App 会往后多读三个字节把后面的阻力、功率全部读错。参数上速度单位是 0.01 km/h踏频单位是 0.5 rpm这两个换算最容易翻车踏频 90 rpm 要写成 180不是 90。功率和阻力是有符号 16 位负值在 FTMS 里通常表示“无效”或“不支持”但有些 App 会直接显示负数所以尽量别发负值。2.3 Control Point 的请求-响应模型App 发指令你得回执Fitness Machine Control Point 是一个“写-指示Write, Indicate”特征。App 往这个特征写一条指令比如0x00表示“请求控制权”0x01表示“重置”0x02表示“设置目标阻力”0x07表示“开始/恢复”0x08表示“停止/暂停”。你收到之后必须通过 Indicate 回一条响应格式是第一个字节是请求的操作码第二个字节是结果码0x01表示成功0x02表示不支持0x03表示参数无效0x04表示操作失败。如果你不回App 会一直等最后超时断开。常见做法是在 BLE 的写回调里解析操作码执行对应动作然后立刻 Indicate 响应。比如 App 写0x00请求控制权你就把控制权标志置 1回0x00 0x01。App 写0x02设置目标阻力后面跟一个 sint16 的阻力值你解析出来调整电磁刹车或阻尼器回0x02 0x01。注意 Indicate 和 Notify 的区别Indicate 需要客户端确认Notify 不需要。Control Point 必须用 Indicate否则 App 可能收不到响应。// Control Point 写回调示例 void onControlPointWrite(uint8_t* data, uint16_t len) { if (len 1) return; uint8_t opCode data[0]; uint8_t response[2]; response[0] opCode; switch (opCode) { case 0x00: // Request Control hasControl true; response[1] 0x01; // Success break; case 0x01: // Reset totalDistance 0; response[1] 0x01; break; case 0x02: // Set Target Resistance Level if (len 3) { int16_t targetResistance (int16_t)(data[1] | (data[2] 8)); setResistance(targetResistance); response[1] 0x01; } else { response[1] 0x03; // Invalid Parameter } break; case 0x07: // Start or Resume isRunning true; response[1] 0x01; break; case 0x08: // Stop or Pause isRunning false; response[1] 0x01; break; default: response[1] 0x02; // Not Supported break; } // 通过 Indicate 发送响应 // bleControlPoint.setValue(response, 2); // bleControlPoint.indicate(); }参数说明hasControl是个布尔标志只有拿到控制权后App 才会发后续指令setResistance是你自己实现的硬件控制函数可能是 PWM 调占空比也可能是步进电机调位置isRunning控制数据上报是否更新。注意结果码0x01是成功0x02是不支持0x03是参数无效0x04是操作失败。如果你收到不认识的 opCode回0x02比不回要好至少 App 知道你不支持不会一直等。3. 用 Arduino 把传感器数据变成 FTMS 可读的字节流3.1 硬件选型哪块 Arduino 能跑 BLE 和 FTMS不是所有 Arduino 都支持 BLE。经典 Uno、Nano 用的是 ATmega328P没有蓝牙硬件你得外挂 HC-05 之类的模块但那些模块通常只支持经典蓝牙串口不支持 BLE GATT做不了 FTMS。常见做法是选带原生 BLE 的板子Arduino Nano 33 BLE、Arduino Nano 33 IoT需要额外配置、ESP32 系列Arduino 核心支持 BLE。其中 ESP32 性价比最高双核 240MHzBLE 协议栈成熟社区例子多。Nano 33 BLE 用的是 nRF52840功耗低但价格稍高。我一般会选 ESP32-WROOM-32 模组搭配一个霍尔传感器测踏频、一个光电传感器测轮速如果你有飞轮再加一个电磁刹车或舵机控制阻力。供电方面ESP32 用 3.3V 逻辑传感器如果是 5V 输出要加电平转换或分压电阻。BLE 天线尽量远离电机和电源线否则丢包率会明显上升。3.2 踏频和速度的计算从脉冲间隔到 0.5 rpm 的整数霍尔传感器每转一圈输出一个或多个脉冲。假设你用的是单极霍尔飞轮每转一圈触发一次那么两次脉冲之间的时间间隔就是一圈的时间。踏频rpm 60 / 圈时间秒。但 FTMS 要求踏频单位是 0.5 rpm所以你要把 rpm 乘以 2 再取整。速度则取决于轮径或飞轮等效周长速度km/h 圈周长米 × 圈数 / 时间秒 × 3.6。FTMS 速度单位是 0.01 km/h所以再乘以 100。实际写代码时不要用delay去等脉冲要用中断。ESP32 的attachInterrupt可以挂在 GPIO 上上升沿触发。在中断里记录micros()时间戳主循环里算差值。如果超过 2 秒没有新脉冲就把踏频和速度置 0否则 App 会一直显示上一次的值。下面是一个简化的计算逻辑volatile unsigned long lastPulseTime 0; volatile unsigned long pulseInterval 0; void IRAM_ATTR onPulse() { unsigned long now micros(); pulseInterval now - lastPulseTime; lastPulseTime now; } void updateFtmsData() { unsigned long now micros(); if (now - lastPulseTime 2000000) { // 超过 2 秒无脉冲 currentCadence 0; currentSpeed 0; currentPower 0; return; } if (pulseInterval 0) { // 踏频60 秒 / (脉冲间隔微秒 / 1e6) 60e6 / pulseInterval float rpm 60000000.0 / pulseInterval; currentCadence (uint16_t)(rpm * 2); // 单位 0.5 rpm // 速度假设飞轮等效周长 2.0 米 float wheelCircumference 2.0; float speedKmh (wheelCircumference / (pulseInterval / 1000000.0)) * 3.6; currentSpeed (uint16_t)(speedKmh * 100); // 单位 0.01 km/h // 功率估算简化模型实际应用需要功率计或查表 currentPower (int16_t)(rpm * 2.5); // 粗略估算 } }参数说明pulseInterval是两次脉冲之间的微秒数60000000.0是 60 秒换算成微秒wheelCircumference要根据你的飞轮或轮子实际周长改常见室内骑行台飞轮等效周长在 1.5 到 2.5 米之间功率估算只是占位真实功率需要应变片或电磁刹车电流反馈。注意中断里不要做浮点运算或串口打印否则会丢脉冲。3.3 BLE 服务注册与广播让 App 能搜到你ESP32 的 Arduino BLE 库BLEDevice可以注册服务和特征。你需要先创建 FTMS 服务UUID0x1826然后添加 Indoor Bike Data 特征0x2AD2属性设为READ | NOTIFY添加 Control Point 特征0x2AD9属性设为WRITE | INDICATE添加 Feature 特征0x2ACC属性READ添加 Status 特征0x2ADA属性NOTIFY。广播时把设备名设成“Indoor Bike”之类App 扫描时就能看到。BLEService ftmsService(1826); BLECharacteristic indoorBikeData(2AD2, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); BLECharacteristic controlPoint(2AD9, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_INDICATE); BLECharacteristic feature(2ACC, BLECharacteristic::PROPERTY_READ); BLECharacteristic status(2ADA, BLECharacteristic::PROPERTY_NOTIFY); void setupBLE() { BLEDevice::init(Indoor Bike); BLEServer* server BLEDevice::createServer(); server-createService(ftmsService); ftmsService.addCharacteristic(indoorBikeData); ftmsService.addCharacteristic(controlPoint); ftmsService.addCharacteristic(feature); ftmsService.addCharacteristic(status); // 设置 Feature 特征的值告诉 App 支持哪些功能 uint8_t featureValue[8] {0x00}; featureValue[0] 0x02; // 支持 Cadence featureValue[1] 0x04; // 支持 Power feature.setValue(featureValue, 8); controlPoint.setCallbacks(new ControlPointCallbacks()); ftmsService.start(); BLEAdvertising* advertising BLEDevice::getAdvertising(); advertising-addServiceUUID(ftmsService.getUUID()); advertising-start(); }这段代码里featureValue的位域也要按 FTMS 规范填bit 1 表示支持踏频bit 2 表示支持功率bit 3 表示支持速度等等。如果你填错App 可能不显示某些数据。ControlPointCallbacks是自定义的回调类继承BLECharacteristicCallbacks在onWrite里处理 Control Point 的写入。广播时加上服务 UUIDApp 扫描过滤时能更快找到你。4. 避坑与排查FTMS 联调中最容易翻车的 5 个点4.1 现象App 能连上但速度、踏频全是 0原因通常是标志位和数据字段不匹配。比如你置了 bit 0瞬时速度但实际数据包里速度字段没填或者填了 0。另一种可能是字节序搞反了App 按小端解析你发的是大端。解决用手机上的 nRF Connect 或 LightBlue 读一下 Indoor Bike Data 的原始字节对照 FTMS 规范逐位检查。速度字段是 uint16小端单位 0.01 km/h如果你发的是 2500App 应该显示 25.00 km/h。4.2 现象Control Point 写进去没反应App 提示超时原因是你没有回 Indicate 响应或者响应格式不对。FTMS 要求每个写请求都要有对应的 Indicate第一个字节是请求的 opCode第二个字节是结果码。如果你只回一个字节或者用 Notify 代替 IndicateApp 可能收不到。解决确认特征属性包含INDICATE并且在onWrite里调用indicate()而不是notify()。另外Indicate 需要客户端确认如果你发得太快可能会丢包建议在回调里直接发不要延迟。4.3 现象踏频跳动很大有时突然跳到 200 以上原因是脉冲间隔测量被中断打断或者霍尔传感器抖动。机械开关式霍尔在临界点会多次触发导致pulseInterval变得极小算出来的 rpm 就很大。解决在中断里加去抖比如检测到脉冲后如果距离上次脉冲小于 50 毫秒就忽略。或者用硬件施密特触发器整形。另外micros()在中断里可能溢出虽然 70 分钟才溢出一次但长时间骑行要注意。4.4 现象BLE 连接几秒后自动断开原因可能是广播间隔太长、连接参数不匹配或者供电不足。ESP32 在 BLE 射频工作时电流会冲到 100mA 以上如果 USB 线质量差或电源纹波大电压跌落会导致重启。解决换一根短而粗的 USB 线或者在电源端并一个 470uF 电解电容。连接参数方面可以在BLEDevice::setMTU和updateConnParams里把连接间隔设成 15ms 到 30ms从机延迟设 0超时设 2 秒。4.5 现象功率显示负数或异常大原因是功率字段是有符号 16 位你发了一个超过 32767 的值或者负数没处理好。FTMS 里功率单位是瓦特sint16范围 -32768 到 32767。如果你用uint16_t存功率超过 32767 就会变成负数。解决用int16_t存功率并且在发送前做限幅比如限制在 0 到 2000 瓦之间。另外有些 App 会把负功率当成无效值忽略所以尽量不要发负值。5. 进阶用 FTMS 的 Training Status 和室内骑行数据做闭环验证5.1 把 Training Status 特征用起来让 App 知道你在“骑行中”FTMS 除了 Indoor Bike Data 和 Control Point还有一个 Training Status 特征UUID0x2AD3用来上报当前训练状态比如“空闲”“热身”“骑行中”“冷却”。很多 App 会根据这个状态决定是否开始记录。你可以用一个简单的状态机上电后是0x00Idle收到 Start 指令后切到0x01Warm Up检测到踏频大于 0 后切到0x02In Progress收到 Stop 后切到0x03Cool Down最后回到 Idle。Training Status 的数据格式是第一个字节是状态后面可以跟训练类型、目标等但最简单的做法是只发一个字节。void updateTrainingStatus(uint8_t status) { uint8_t data[1] {status}; bleTrainingStatus.setValue(data, 1); bleTrainingStatus.notify(); }参数说明status取值0x00空闲、0x01热身、0x02进行中、0x03冷却、0x04完成。注意这个特征是可选的但加上之后 App 的兼容性会更好。如果你发现某个 App 连上后不显示数据先检查它是否在等 Training Status。5.2 用 nRF Connect 做逐字节验证比盲猜快十倍联调阶段最怕“黑匣子”——App 说没数据你不知道是协议错了还是硬件坏了。我一般会先用 nRF Connect 连上设备展开 FTMS 服务逐个读特征值。Indoor Bike Data 如果是 Notify就点“订阅”然后看原始字节。对照 FTMS 规范手动解析前两个字节是 Flags后面按顺序排。如果速度字段是0x09 0x0C小端就是0x0C09 3081单位 0.01 km/h就是 30.81 km/h。如果读出来是0x0C 0x09那就是大端说明你发反了。Control Point 可以手动写0x00请求控制权看有没有 Indicate 回来。这个方法能快速定位是协议层还是应用层的问题。5.3 一个我踩过的坑别在 BLE 回调里做耗时操作早期我把阻力控制直接写在 Control Point 的onWrite回调里结果 App 发一条设置阻力的指令回调里驱动舵机转动 200 毫秒期间 BLE 协议栈被阻塞Indicate 响应发不出去App 直接超时断开。血泪经验BLE 回调里只做解析和标志位设置实际硬件动作放到主循环里用状态机去执行。比如回调里把targetResistance存下来主循环检测到变化再去驱动舵机。这样 BLE 响应能在几毫秒内发出App 不会超时。5.4 验证闭环用手机 App 骑 5 分钟看数据是否连续最后一步验证不是看单个字节而是实际骑 5 分钟观察 App 上的速度、踏频、功率曲线是否连续、有没有跳变。如果踏频在 60 到 90 之间波动速度在 20 到 30 km/h 之间功率在 100 到 200W 之间说明数据链路是通的。如果中间出现断崖式归零检查是不是脉冲丢失或 BLE 断连。我习惯在代码里加一个计数器每发一次 Indoor Bike Data 就加一串口打印出来看 5 分钟发了多少次。如果频率稳定在 1Hz 到 4Hz说明 Notify 正常。这个习惯帮我省了很多“后悔药”——与其事后猜不如一开始就把可观测性做进去。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

DSP28377D CAN发送与中断接收实战:从寄存器配置到避坑排查

DSP28377D CAN发送与中断接收实战:从寄存器配置到避坑排查

简介:这份资源面向嵌入式开发初学者与DSP工程师,聚焦TI DSP28377D(TMS320C28x系列)的CAN总线通信实现,解决发送与中断接收的代码落地问题,适用于工业自动化、汽车电子等实时通信场景。压缩包共217个文件&am…

2026/10/15 18:41:55 阅读更多 →
码匠教育:Python 的多任务,为什么和想象中的并行不一样

码匠教育:Python 的多任务,为什么和想象中的并行不一样

很多人刚开始接触Python多任务的时候,都会有很强的落差感:明明开了好几个线程,怎么运行速度不仅没翻倍,反而还变慢了,和想象里“多任务同时运行、效率成倍提升”完全不一样。这不是代码写错了,是Python的底…

2026/10/15 18:41:55 阅读更多 →
dcmtk-3.6.5-win64 已编译工具包使用指南:从命令行到 Visual Studio 集成

dcmtk-3.6.5-win64 已编译工具包使用指南:从命令行到 Visual Studio 集成

简介:dcmtk-3.6.5-win64已编译工具包面向医学影像处理方向的开发者与研究人员,解决在64位Windows平台上使用DCMTK时需自行编译源码的痛点。DCMTK是处理DICOM标准医学图像数据的开源C库,本包提供预编译二进制文件,解压即可调用&…

2026/10/15 18:41:55 阅读更多 →

最新新闻

30天从零开始学AI应用开发(Day 26):项目三完工:调试 Agent 的血泪经验

30天从零开始学AI应用开发(Day 26):项目三完工:调试 Agent 的血泪经验

这是系列的第 26 篇。整个系列写给零基础、想入行 AI 的朋友,每天一篇,30 天后你会做出 3 个能写进简历的项目。这篇解决什么问题 昨天项目三跑通了,有几个朋友说规划步骤挺像样,也有人报告了各种奇怪的状况:跑着跑着不…

2026/10/15 19:24:19 阅读更多 →
中国汽车工业软件的卡脖子清单:从PLM到MES的国产替代路线分析

中国汽车工业软件的卡脖子清单:从PLM到MES的国产替代路线分析

文章目录引言:一辆车背后的软件战争一、汽车工业软件的全景图:五大品类与三条工具链二、卡脖子清单逐项拆解2.1 三维CAD:有替代,但成熟度差距明显2.2 CAE仿真:国产空白最多的重灾区2.3 EDA电子设计自动化:差…

2026/10/15 19:24:19 阅读更多 →
全球大厂的“去英伟达”实验

全球大厂的“去英伟达”实验

头图由AI生成文 | 田渝编 | 沈校10月12日晚间,微软董事长兼首席执行官萨提亚纳德拉在社交媒体上公布了微软自研芯片的最新计划。2027年,微软计划大规模部署Azure Maia 300。当日,微软股价当天上涨1.14%至541.15美元,市值重回4万亿…

2026/10/15 19:24:19 阅读更多 →
被停牌的茶百道,更惨的是加盟商?

被停牌的茶百道,更惨的是加盟商?

多米诺骨牌效应才刚开始。文|段泽钰编|郭梦仪1848年,加州淘金热兴起。商人布兰南做了一件事:他先囤积了大量采矿工具,然后跑到街上大喊“金子!金子!”人群蜂拥而至,工具价格翻倍&…

2026/10/15 19:24:19 阅读更多 →
3Dmol.js 分子可视化实战:浏览器端渲染与交互分析

3Dmol.js 分子可视化实战:浏览器端渲染与交互分析

简介:这份前端项目资源以3Dmol.js为核心,面向具备一定JavaScript基础、希望在网页中实现分子结构可视化的开发者与化学教育工作者。它解决的是在浏览器端解析并渲染PDB、MOL、SDF等分子数据、构建可交互三维模型的问题,适合用于化学教学演示、…

2026/10/15 19:24:19 阅读更多 →
北航何静:Codex调用SketchUp实操,常见智能体均适用

北航何静:Codex调用SketchUp实操,常见智能体均适用

北京航空航天大学副教授何静实测 AI 智能体驱动 SketchUp 草图大师自动建模,覆盖 ChatGPT、Claude Code、Workbuddy、OpenClaw 多款智能体,对比 4 种建模实现路径,完成多类型建筑场景建模与迭代修改。实测场景:现代住宅、10 层玻璃…

2026/10/15 19:23:17 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →