基于Arduino与BLE的自行车骑行坡度模拟器DIY全解析
1. 项目概述与核心思路
几年前,当我在家养伤,对着骑行台日复一日地踩踏时,一个想法冒了出来:那些动辄大几千的智能骑行台,凭什么能模拟出爬坡的感觉?不就是把车头抬起来吗?这个看似简单的动作,背后是传感器、算法和机械的精密配合。市面上确实有成熟的商业产品,比如Wahoo的Kickr Climb,但它不仅价格昂贵,还要求你必须是特定品牌高端骑行台的用户。这让我这个喜欢折腾的硬件爱好者有点不服气,于是决定自己动手,用开源硬件和3D打印,花十分之一的预算,复刻一个能模拟-5%到+20%坡度的自行车骑行坡度模拟器。
这个项目的核心,我称之为OpenGradeSIM,它本质上是一个基于实时数据的反馈控制系统。简单来说,就是让机器“看懂”你在虚拟世界里的骑行状态(比如正在爬一个10%的坡),然后通过一个机械装置,实时地、按比例地把你的自行车前轮抬升到对应的高度。听起来简单,但拆解开来,需要解决三个核心问题:数据从哪来、坡度怎么算、动作如何执行。
数据来源是第一个坎。理想的状况是骑行台或训练软件(如Zwift)能直接通过蓝牙或ANT+广播当前的模拟坡度值。但现实很骨感,大多数协议为了节省带宽和电量,只传输最基础的速度、功率、踏频等数据,坡度信息被隐藏在软件内部的计算逻辑里,不对外公开。这就逼着我们得“曲线救国”,从已有的物理量里反推出坡度。
计算坡度是第二个技术难点。我们已知骑行者的输出功率,这部分功率主要消耗在两个方面:一是克服风阻、滚阻等行驶阻力;二是转化为爬坡做功。如果我们能建立一个模型,估算出在某个速度下,克服行驶阻力需要多少功率,那么用总功率减去这部分阻力功率,剩下的就是用于爬坡的功率。再结合骑行者的总重量和当前速度,就能反推出实时的坡度百分比。这就像解一道物理应用题,关键是要找到一个足够准确的阻力模型。
最后是执行部分。我们需要一个能承受自行车加骑行者部分重量(主要是前轮负载)、行程足够(模拟20%坡度需要约200mm抬升)、且响应速度跟得上骑行节奏的线性执行器。同时,还需要一套可靠的控制电路,能安全地驱动这个执行器正反转,并接收来自微控制器的指令。为了降低成本,我选择了没有内置位置反馈(电位器)的普通执行器,转而用Arduino板载的加速度计来测量倾角,实现闭环控制。
整个系统的流程可以概括为:Arduino通过BLE接收来自骑行台的实时速度与功率数据 -> 根据内置算法计算出模拟坡度 -> 将目标坡度转换为目标倾角(或执行器目标位置)-> 通过H桥驱动电路控制线性执行器伸缩 -> 执行器推动自行车前叉,改变整车倾角 -> 加速度计测量实际倾角并反馈给Arduino,形成闭环控制,直至实际倾角与目标倾角一致。
2. 核心硬件选型与设计解析
硬件是整个项目的骨架,选型和设计直接决定了系统的可靠性、精度和成本。我的核心思路是在满足功能和安全的前提下,尽可能采用高性价比、易获取的通用部件。
2.1 控制核心:Arduino Nano 33 IoT
选择Arduino Nano 33 IoT作为大脑,主要基于以下几点考量:
- 双模无线连接:板载的u-blox NINA-W102模块同时支持Wi-Fi和BLE。本项目最关键的就是通过BLE与骑行台或数据桥接器通信,获取速度与功率数据。原生支持BLE避免了外接模块的复杂性和额外成本。
- 内置传感器:板载的LSM6DS3TR-C IMU(惯性测量单元)包含了三轴加速度计和陀螺仪。我们可以直接使用加速度计来测量自行车的实时倾角,省去了额外购买角度传感器的费用和布线麻烦。
- 充足的IO与算力:基于ARM Cortex-M0+的SAMD21微控制器,48MHz主频,处理本项目的滤波算法和控制逻辑绰绰有余。其数字IO口也足够驱动显示屏、按键和电机驱动信号。
- 3.3V逻辑电平:这是一个需要特别注意的点。该板卡的所有IO口都是3.3V电平,而后面要用的L298N H桥驱动模块的控制信号通常是5V TTL电平。直接连接有损坏Arduino引脚的风险,因此必须加入逻辑电平转换模块。
注意:早期版本的ArduinoBLE库(如v1.1.2)在配对某些需要特定认证的商用BLE设备时可能存在兼容性问题。如果直接连接你的智能骑行台不成功,不要灰心,这不是你的代码问题。后文会介绍一个可靠的解决方案。
2.2 动力与执行:线性执行器与H桥驱动
线性执行器的选择是机械部分的重中之重。你需要关注几个关键参数:
- 推力:需要抬起的是自行车前轮以及部分骑行者重量。我的公路车轴距约1000mm,模拟20%坡度需要抬升前叉约200mm。粗略估算,前轮负载约占总重(假设车+人=100kg)的30%-40%,即30-40kg。换算成推力,需要至少300N-400N。我选择的是一款标称750N(约76公斤力)的200mm行程执行器,留有充足余量。
- 行程:根据你的自行车轴距和目标模拟坡度范围计算。公式很简单:
抬升高度 (mm) = 轴距 (mm) × 坡度 (%) / 100。例如,1000mm轴距模拟20%坡度,就需要200mm行程。 - 速度:执行器的伸缩速度决定了坡度变化的响应快慢。我用的这款速度约为10mm/s,从平路升到20%坡顶大约需要20秒,对于模拟长缓坡是足够的,但对于短陡坡变化可能稍显迟缓。有更高速度的型号,但价格也更高。
- 反馈类型:带内置电位器的执行器可以精确反馈当前位置,实现更直接的位置闭环控制,但价格通常是同规格无反馈型号的2-3倍。为了控制成本,本项目采用“无反馈执行器 + 加速度计测倾角”的方案,属于间接位置控制。
L298N H桥驱动模块是控制执行器正反转、调速(本项目为简单起见,通常全速运行)的核心。它本质上是一个双路电机驱动芯片,可以接受微控制器的信号,来控制连接在输出端的两根线(A+, A-)之间的电压方向和通断,从而让直流电机正转、反转或刹车。
- 供电:模块有一个12V输入口,用于给执行器供电。同时,它还有一个5V输出口,这个口可以给Arduino等逻辑电路供电。但是,对于Arduino Nano 33 IoT(3.3V系统),不能直接使用这个5V输出,以免损坏。
- 控制信号:IN1, IN2, IN3, IN4是控制信号输入引脚。控制一个执行器(一个电机)只需要其中一路(如IN1, IN2)。通过给这两个引脚输入不同的高低电平组合,来控制电机的状态。
- 逻辑电平转换:由于L298N模块期望5V控制信号,而Arduino Nano 33 IoT输出3.3V,因此必须在两者之间加入一个双向逻辑电平转换器(例如基于TXB0108芯片的模块)。将Arduino的3.3V信号端连接到转换器的低压侧,L298N的5V信号端连接到高压侧。
2.3 数据桥梁:NPE CABLE ANT+ to BLE Bridge
这是我踩过的第一个大坑,也是一个关键决策点。最初我试图让Arduino Nano 33 IoT直接连接我的Tacx Neo骑行台。虽然能搜索到设备,但始终无法成功配对并订阅数据特征值。排查后发现,许多商用健身设备为了安全和兼容性,在BLE连接时需要进行特定的配对或认证流程,而旧版的ArduinoBLE库并未完整实现这些流程。
解决方案就是引入一个“翻译官”——NPE CABLE。这个小设备非常巧妙:
- 一端(ANT+)连接你的骑行台。骑行台通常通过ANT+协议向码表、心率带等设备广播数据。
- 另一端(BLE)模拟成一个标准的BLE传感器(如功率计、速度传感器),将ANT+数据“翻译”成BLE数据包重新广播。
- 关键优势:CABLE广播的BLE信号是无需认证的,Arduino可以像连接一个普通的心率带一样轻松连接并读取数据。这样,你的训练软件(如Zwift)通过ANT+连接骑行台,而Arduino通过BLE连接CABLE,两者互不干扰,完美共存。
2.4 结构件:3D打印与机加工
为了让执行器能稳稳地顶起自行车,需要两个关键的结构件:
- 前叉转接座:需要把执行器顶杆的力传递到自行车的前叉上。对于现代公路车常用的12mm桶轴,我设计了一个转接件。中间是一段3/4英寸(约19mm外径)的10SWG铝管,内径刚好能紧密套在桶轴上。铝管两侧通过3D打印的PLA连接件,与一根6mm不锈钢杆连接,而不锈钢杆的另一端则与执行器的顶杆相连。铝管和PLA件主要承受压力,结构强度足够。
- 执行器底座(鞋):这个部件需要固定在骑行台的前轮垫块(或地面)上,为执行器提供一个稳固的支点。我设计了一个可以卡入Tacx Neo原装垫块凹槽的“鞋”形结构。如果你的骑行台不同,可能需要修改这个底座的设计。
所有3D打印文件均使用PLA材料,在普通的FDM 3D打印机上即可完成。对于受力较大的转接座部分,打印时可以设置较高的填充率(如50%以上)以增加强度。
3. 系统搭建与电路连接详解
有了所有零件,下一步就是像搭积木一样把它们正确地连接起来。这部分需要细心,特别是电源和信号线的连接,接错了可能烧毁元件。
3.1 电路连接原理与步骤
整个系统的供电与控制信号流如下图所示(注:此处为文字描述,实际搭建请参照接线表):
供电部分:
- 将12V/3A直流电源的正负极分别连接到L298N H桥驱动模块的“12V输入+”和“GND”。
- 不要使用L298N模块上的“5V输出”给Arduino供电。因为我们需要的是3.3V。
- 使用一个独立的5V USB电源(或稳压模块)为Arduino Nano 33 IoT供电。
控制信号部分:
- 逻辑电平转换器:将其“LV”(低电压)侧与Arduino的3.3V和GND相连;将其“HV”(高电压)侧与一个外部5V电源(可以从L298N的5V输出取,但最好独立)和GND相连。
- Arduino -> 电平转换器 -> L298N:
- Arduino的某个数字引脚(例如D5)连接至电平转换器LV侧的A1通道。
- 电平转换器HV侧的对应B1通道连接至L298N的IN1引脚。
- Arduino的另一个数字引脚(例如D6)连接至电平转换器LV侧的A2通道。
- 电平转换器HV侧的对应B2通道连接至L298N的IN2引脚。
- L298N -> 线性执行器:将线性执行器的两根电机线,分别连接到L298N模块上同一路的输出端,例如OUT1和OUT2。
- OLED显示屏与按键:OLED(I2C接口)的VCC接3.3V,GND接GND,SDA接Arduino的A4(或SDA),SCL接A5(或SCL)。2键薄膜按键的一端接公共地(GND),另外两端分别接Arduino的两个数字输入引脚(如D7, D8),并启用内部上拉电阻。
接线表示例:
| 元件 | 引脚/接口 | 连接到 | 元件 | 引脚/接口 | 说明 |
|---|---|---|---|---|---|
| 12V电源 | 正极(+) | -> | L298N模块 | 12V Input+ | 执行器主电源 |
| 12V电源 | 负极(-) | -> | L298N模块 | GND | 电源地 |
| 线性执行器 | 线1 | -> | L298N模块 | OUT1 | 电机输出A |
| 线性执行器 | 线2 | -> | L298N模块 | OUT2 | 电机输出A |
| 5V电源 | 正极(+) | -> | 电平转换器 | HV (VCC) | 为高压侧供电 |
| 5V电源 | 正极(+) | -> | L298N模块 | 5V Input (可选) | 为L298N逻辑供电 |
| 公共地 | GND | -> | 电平转换器 HV侧 GND | 所有GND最终需共地 | |
| 公共地 | GND | -> | L298N模块 GND | ||
| 公共地 | GND | -> | Arduino GND | ||
| Arduino Nano 33 IoT | 3.3V | -> | 电平转换器 | LV (VCC) | 为低压侧供电 |
| Arduino Nano 33 IoT | D5 | -> | 电平转换器 | A1 (LV侧) | 控制信号1 |
| 电平转换器 | B1 (HV侧) | -> | L298N模块 | IN1 | |
| Arduino Nano 33 IoT | D6 | -> | 电平转换器 | A2 (LV侧) | 控制信号2 |
| 电平转换器 | B2 (HV侧) | -> | L298N模块 | IN2 | |
| Arduino Nano 33 IoT | A4 (SDA) | -> | OLED显示屏 | SDA | I2C数据线 |
| Arduino Nano 33 IoT | A5 (SCL) | -> | OLED显示屏 | SCL | I2C时钟线 |
| OLED显示屏 | VCC | -> | Arduino | 3.3V | |
| OLED显示屏 | GND | -> | 公共地 | GND | |
| 薄膜按键 | 公共端 | -> | 公共地 | GND | |
| 薄膜按键 | 按键1 | -> | Arduino | D7 (INPUT_PULLUP) | 用于增加重量设定 |
| 薄膜按键 | 按键2 | -> | Arduino | D8 (INPUT_PULLUP) | 用于减少重量设定 |
重要警告:在早期的原型中,我曾为了图方便,剪断一根USB线,用它的电源线给Arduino供电,数据线传递3.3V信号。这是极其危险的做法!万一误将这根改装线插入电脑USB口,可能会将12V或5V电压引入电脑主板,造成永久性损坏。请务必使用标准的DC插头或接线端子进行电源连接,杜绝此类隐患。
3.2 机械组装要点
- 前叉转接座安装:将铝管套在自行车前轮的桶轴上,然后安装好两侧的3D打印连接件和6mm钢杆。确保所有螺丝紧固,但不要过度用力导致PLA件开裂。安装好后,手动上下晃动自行车,检查连接处是否有松动或异响。
- 执行器与底座固定:将执行器底部的安装孔用螺丝牢固地锁在3D打印的“鞋”形底座上。然后将这个底座放置并固定在骑行台前轮垫块的位置。对于Tacx Neo,其垫块有一个凹槽,我的设计正好可以卡进去。如果你的骑行台是平的,可能需要使用强力双面胶或打孔螺丝固定。
- 整体连接:将执行器的顶杆(已通过钢杆和转接座连接到前叉)与执行器本体连接。此时,执行器应处于完全收缩状态。打开电源,通过Arduino串口监视器或按键手动控制执行器小幅伸缩,观察整个传动机构是否运动顺畅,有无卡滞或干涉。
- 后轮轴处理:这是一个容易被忽略但至关重要的问题。当自行车前轮被抬升时,整辆车会以前轮接触点为轴心旋转。如果后轮轴在骑行台飞轮处是刚性固定的,那么车架和后轮轴之间会产生摩擦甚至扭力。我的解决方案是使用标准深沟球轴承(如61800-2RS)。将轴承套在骑行台快拆适配器上,然后再将自行车的后轮轴装入轴承内圈。这样,自行车相对于骑行台就可以自由地俯仰转动,避免了摩擦。安装后可能需要微调后拨的限位螺丝,因为轴承可能会使驱动侧有微小偏移。
4. 核心算法:从功率与速度到坡度
这是本项目的大脑,也是最体现工程思维的部分。既然无法直接获取坡度,我们就用物理定律把它算出来。
4.1 理论基础与公式推导
骑行者输出的总功率P_total(由骑行台功率计测量)主要用于克服两部分阻力:
- 行驶阻力功率
P_roll:包括空气阻力、轮胎滚阻、传动系统摩擦等。在室内无风环境下,空气阻力与速度的平方成正比,滚阻近��为常数。一个常用的简化模型是,行驶阻力功率与速度的2.8次方成正比。 - 爬坡功率
P_climb:用于对抗重力,使骑手和自行车向上提升。公式为:P_climb = m * g * v * sin(θ)。其中,m是总质量,g是重力加速度(9.8 m/s²),v是速度(m/s),θ是坡度角。对于较小的角度(自行车坡度通常小于20%),sin(θ) ≈ tan(θ) = grade / 100,其中grade是坡度百分比。
因此,核心计算公式为:P_total = P_roll + P_climbP_climb = P_total - P_rollgrade (%) = (P_climb / (m * g * v)) * 100
4.2 阻力模型的建立与校准
难点在于如何确定P_roll。我采用了数据拟合的方法:
- 获取基准数据:我使用了在线工具
bikecalculator.com。输入我的体重、自行车重量,在0%坡度下,计算不同速度(如10, 20, 30, 40 km/h)时对应的“功率(瓦特)”。这个功率值就是该速度下纯平路骑行需要克服的总阻力功率,即P_roll。 - 数据拟合:将得到的速度(km/h)和功率(W)数据点绘制在图表上。发现其关系是非线性的。通过对速度取2.8次方,我发现
P_roll与Speed_kmh^2.8呈现出良好的线性关系。 - 得出经验公式:使用线性回归,我得到了一个适用于我自身情况的经验公式:
P_roll = (0.0102 * (Speed_kmh ^ 2.8)) + 9.428这个公式的常数项(9.428W)可以理解为极低速下的基础滚阻,而系数0.0102则综合了空气密度、迎风面积、滚阻系数等因素。
实操心得:这个阻力模型是系统精度的关键。它因人、因车、因骑行台而异。最准确的方法是在你自己的骑行台上进行校准:在训练软件中设置0%坡度,以恒定速度骑行,记录骑行台报告的平均功率,这个功率就是该速度下的
P_roll。多测几个速度点,用你自己的数据拟合出专属公式,坡度模拟的准确性会大幅提升。
4.3 在Arduino中的实现
在代码中,我们需要实时进行以下计算:
- 获取原始数据:通过BLE读取骑行台广播的实时速度(km/h)和功率(W)。对功率应用一个移动平均滤波器(例如3秒平均),以平滑数据波动,模拟码表上显示的“3秒平均功率”。
- 计算阻力功率:将当前速度代入上述经验公式,计算出
P_roll。 - 计算爬坡功率:
P_climb = P_measured_avg - P_roll。如果结果为负,说明正在下坡。 - 计算坡度:将速度从km/h转换为m/s(除以3.6)。代入公式:
grade = (P_climb / (total_weight_kg * 9.8 * speed_ms)) * 100。 - 单位转换与限幅:计算出的坡度值可能波动很大,特别是低速或功率很低时。需要加入死区处理和限幅,例如,将绝对值小于0.5%的坡度视为0%,并将最终坡度限制在-5%到+20%的设计范围内。
- 坡度到目标位置的转换:根据公式
目标抬升高度 = 轴距 * 目标坡度 / 100,将坡度百分比转换为执行器需要达到的绝对长度。再结合加速度计读取的当前倾角,通过一个PID控制器或其他控制算法,计算出给执行器的动作指令(正转、反转、停止)。
5. 软件实现与数据解析
软件部分主要包括Arduino固件开发,核心任务是BLE通信、数据解析、坡度计算和电机控制。
5.1 BLE数据读取与解析
连接NPE CABLE后,Arduino需要扫描并连接其广播的特定服务。对于骑行数据,主要关注两个服务:
- Cycling Power Service (UUID: 0x1818):用于获取功率数据。功率值通常以16位无符号整数(单位:瓦特)的形式,存储在该服务下某个特征值(Characteristic)的数据包中。解析相对简单,直接读取特定字节并转换即可。
- Cycling Speed and Cadence Service (UUID: 0x1816):用于获取速度数据。这是解析的难点所在。该服务不直接提供速度值,而是提供两个关键信息:
Cumulative Wheel Revolutions:一个32位的无符号整数,表示从传感器启动以来总的车轮转数。Last Wheel Event Time:一个16位的无符号整数,表示上次车轮转动事件发生的时间,单位是1/1024秒。
速度计算步骤:
- 在
t1时刻,读取并记录当前的累计转数Rev1和事件时间Time1。 - 在
t2时刻,再次读取Rev2和Time2。 - 计算转数差:
Delta_Rev = Rev2 - Rev1。 - 计算时间差(秒):
Delta_Time_s = (Time2 - Time1) / 1024.0。 - 已知车轮周长
C(单位:米),则速度v = (Delta_Rev * C) / Delta_Time_s,单位是米/秒。再乘以3.6即得到km/h。
注意:车轮周长需要你手动测量并输入到代码中。一个简单的方法是,在轮胎上做一个标记,推动自行车让车轮在地面滚动一整圈,测量标记起点和终点之间的距离。对于常见的700x25c公路车轮胎,周长大约在2100-2200mm之间。
5.2 控制逻辑与状态机
主程序循环应该像一个状态机一样工作,逻辑清晰:
- 初始化:启动串口、初始化BLE、连接CABLE、订阅特征值、初始化显示屏、加速度计、设置电机控制引脚模式。
- 数据采集循环:
- 检查是否有新的BLE数据到达。
- 如果有,则解析速度与功率。
- 调用坡度计算函数,得到当前的目标坡度。
- 读取加速度计,通过反正切函数
atan2(accelY, accelZ)计算当前车架倾角(需要校准零位)。
- 控制决策:
- 将目标坡度转换为目标倾角(
目标倾角 = arctan(目标坡度/100))。 - 比较当前倾角与目标倾角。
- 如果当前倾角小于目标倾角超过一个阈值(如0.5度),则控制电机正转(抬升)。
- 如果当前倾角大于目标倾角超过阈值,则控制电机反转(下降)。
- 如果在阈值范围内,则停止电机。
- 将目标坡度转换为目标倾角(
- 用户交互:循环检测按键,用于手动校准零位或设置骑行者体重。将关键信息(实时速度、功率、计算坡度、当前倾角)刷新到OLED屏幕上。
5.3 关键代码片段示例
以下是坡度计算和电机控制的核心逻辑伪代码:
// 假设已从BLE获取平滑后的功率 power_avg (W) 和速度 speed_kmh (km/h) float calculateGrade(float power_avg, float speed_kmh, float total_weight_kg) { // 1. 计算阻力功率 float P_roll = (0.0102 * pow(speed_kmh, 2.8)) + 9.428; // 2. 计算爬坡功率 float P_climb = power_avg - P_roll; // 3. 处理低速或低功率下的异常值 if (speed_kmh < 1.0 || abs(P_climb) < 5.0) { // 阈值可调 return 0.0; } // 4. 单位转换并计算坡度 float speed_ms = speed_kmh / 3.6; float grade = (P_climb / (total_weight_kg * 9.8 * speed_ms)) * 100.0; // 5. 限幅 grade = constrain(grade, -5.0, 20.0); return grade; } void controlActuator(float target_grade, float current_angle) { float target_angle_rad = atan(target_grade / 100.0); float target_angle_deg = target_angle_rad * 180.0 / PI; float error = target_angle_deg - current_angle; float deadzone = 0.5; // 死区阈值,度 if (error > deadzone) { // 需要抬升 digitalWrite(MOTOR_IN1, HIGH); digitalWrite(MOTOR_IN2, LOW); // 也可以加入PWM进行速度控制 // analogWrite(MOTOR_ENA, 255); } else if (error < -deadzone) { // 需要下降 digitalWrite(MOTOR_IN1, LOW); digitalWrite(MOTOR_IN2, HIGH); } else { // 停止 digitalWrite(MOTOR_IN1, LOW); digitalWrite(MOTOR_IN2, LOW); } }6. 调试、校准与优化心得
系统搭建完成后,离稳定可靠运行还有一段调试的距离。这里分享几个关键的调试步骤和优化技巧。
6.1 系统校准流程
- 加速度计零位校准:将自行车和骑行台放置在绝对水平的平面上。在Arduino��码中,读取此时加速度计在Y轴和Z轴的值,计算出的倾角应为0度。将这个状态下的原始传感器值记录下来,作为“零位偏置”。在后续计算实际倾角时,先减去这个偏置。可以将此校准过程做成一个通过按键触发的功能。
- 执行器行程校准:让执行器运行到完全收缩和完全伸出的极限位置,记录这两个位置对应的加速度计倾角读数。这将是你系统的物理角度范围。将目标坡度百分比映射到这个角度范围内。
- 阻力模型验证:在训练软件中设置0%坡度,以几个不同的恒定速度(如20km/h, 30km/h)骑行几分钟,记录骑行台的平均功率和你的系统计算出的
P_roll。对比两者,调整经验公式中的系数,使计算值尽可能接近实测值。 - 坡度响应测试:在训练软件中设置一个固定的坡度(如5%),以恒定功率骑行。观察你的模拟器抬升是否平稳,最终稳定的倾角是否与理论值相符。反复调整PID控制参数(如果使用PID)或死区、响应速度等,使系统既响应迅速,又不会在目标值附近振荡。
6.2 常见问题与排查
在项目开发和网友复现过程中,遇到了一些典型问题:
BLE连接失败,无法订阅数据
- 现象:串口打印显示发现了功率和速度服务,但无法订阅特征值,或订阅后收不到数据。
- 排查:首先确认NPE CABLE已正确连接骑行台ANT+口,且指示灯正常。尝试用手机BLE扫描软件(如LightBlue)搜索CABLE广播的设备,查看其服务UUID是否与代码中一致。最常见的原因是ArduinoBLE库版本过旧。尝试更新到最新版本,或者使用社区维护的兼容性更好的BLE库。
- 解决:确保使用能正确处理连接参数的BLE库。如果问题依旧,检查CABLE的固件是否为最新。
坡度计算波动巨大,执行器频繁启停
- 现象:即使匀速骑行,计算出的坡度也在正负之间剧烈跳动,导致执行器像“打摆子”一样。
- 排查:根源在于原始功率和速度数据有噪声。功率计每秒钟可能发送十几次数据,单次读数波动很大。
- 解决:
- 加强滤波:对原始功率数据使用更长的移动平均窗口(如5秒或10秒)。对计算出的坡度值再进行一次低通滤波。
- 增加死区:扩大控制死区。例如,只有当目标倾角与实际倾角相差超过1度时,才启动电机。避免系统对微小波动过度反应。
- 输出限幅:限制坡度变化率。例如,规定每秒内坡度变化不超过2%。这能让抬升/下降动作更平滑,更接近真实爬坡体验。
执行器运动不顺畅或有异响
- 现象:电机转动,但顶杆运动卡顿,或者发出“咯咯”声。
- 排查:检查所有机械连接处是否紧固,有无松动。检查执行器顶杆与自行车转接件、转接件与前叉之间的连接是否同心,有无侧向力。手动推动执行器,感觉是否有内部卡滞。
- 解决:确保所有连接轴平行,避免产生弯矩。在螺纹连接处涂抹少量润滑脂。如果问题在执行器内部,可能是质量不佳,考虑更换品牌。
OLED显示屏不亮或显示乱码
- 现象:屏幕全黑,或显示白屏、乱码。
- 排查:首先确认接线正确,特别是VCC接的是3.3V而非5V(很多OLED屏是5V逻辑,但本项目用的这款是3.3V)。检查I2C地址是否正确(通常为0x3C)。用Arduino的I2C扫描示例程序检查是否能发现设备。
- 解决:确认供电电压。尝试在初始化显示库时,显式指定I2C地址和引脚。如果屏幕是好的,但初始化失败,可能是库不兼容,尝试换一个SSD1306或SH1106的驱动库。
后轮蹭刹车或变速不准
- 现象:安装轴承转接座后,后轮碟刹蹭碟,或变速器无法准确换到最大或最小飞轮。
- 排查:轴承的加入增加了驱动侧的宽度,导致后轮整体向非驱动侧偏移了一点。
- 解决:这是正常现象。需要重新调整后拨的限位螺丝(H螺丝和L螺丝),并可能需要微调后拨的张力。在安装模拟器之前,最好先记录下变速精准的状态,安装后再对比调整。
6.3 性能优化与扩展思路
基础版本完成后,你可以考虑以下优化和扩展,让体验更上一层楼:
- 引入PID控制:用简单的开关控制(如上述伪代码)容易产生振荡。实现一个比例-积分-微分控制器,能更平滑、更精准地将倾角维持在目标值。
- 增加手动模式:通过按键,可以手动控制执行器升降,方便在非骑行状态下调整位置,或者作为备份控制方式。
- 无线配置:利用Arduino Nano 33 IoT的Wi-Fi功能,创建一个简单的Web配置页面。通过手机浏览器连接到它,就可以无线设置体重、校准零位、调整PID参数等,无需连接电脑修改代码。
- 数据记录与回放:将实时坡度、功率、速度数据写入SD卡或通过Wi-Fi发送到服务器,用于后续分析你的骑行表现。
- 支持更多数据源:除了通过CABLE获取骑行台数据,还可以尝试直接解析Zwift等软件的网络数据包(如通过监听本地端口),或者使用屏幕识别技术(OCR)来获取软件界面显示的实时坡度,这能实现零延迟的坡度同步,但实现复杂度较高。
这个项目从构思到实现,充满了硬件DIY的乐趣和挑战。它不仅仅是一个省钱的替代品,更是一个深入了解实时控制系统、无线通信和机械设计的绝佳实践。当你第一次在骑行台上感受到系统根据虚拟世界的山坡自动抬升车头,那种力量反馈的变化,会让你觉得所有的调试和折腾都是值得的。最重要的是,整个系统完全在你的掌控之下,你可以随意修改、优化、扩展,这或许就是开源硬件和DIY最大的魅力所在。
