ArduPilot BatteryTag 电池信息标签:DroneCAN 节点与 Lua 脚本协同记录电池寿命数据
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
导读
本文围绕 ArduPilot 仓库中的电池信息标签(BatteryTag)功能展开,讲解如何通过一个挂在电池上的 AP_Periph 微型节点,把电池的循环次数、序列号、通电时长等寿命数据通过 DroneCAN 总线传递给飞控,并由 BatteryTag.lua 脚本完成日志记录、参数管理、GCS 上报与解锁安全检查。读完本文,你将掌握 BatteryTag 的完整工作链路:从 battery_tag.cpp 的节点端实现,到 BatteryTag.lua 的飞控端处理,以及BTAG_*参数、BTAG 日志报文、硬件目标与解锁闸门(arming gate)的配置方法。
BatteryTag 是什么:为每块电池建立"电子档案"
BatteryTag 的核心思路是:给每一块电池绑定一个独立的 AP_Periph 微型节点(基于低成本的 L431 系列 MCU)。这个节点在电池生命周期内始终跟随电池,持续记录三类关键信息:
- 循环次数(num_cycles):电池经历过多少次完整的使用循环;
- 通电时长(armed_hours):电池处于解锁通电状态的总小时数;
- 序列号(serial_num):电池的唯一身份标识。
节点将这些数据编码为 DroneCAN 的ardupilot.equipment.power.BatteryTag消息周期广播到 CAN 总线,飞控端的 Lua 脚本订阅并消费这些消息。正如 battery_tag.cpp 顶部注释所述:"该信息节点附着在特定电池上,记录循环次数与总通电时间;节点生成 BatteryTag 消息,由飞控上的 BatteryTag Lua 脚本消费。" 数据最终写入飞控日志中的BTAG报文,方便后期分析电池健康状况、估算老化程度。
端到端数据流:从电池到日志
完整的 BatteryTag 链路分为节点端与飞控端两段:
- 节点端(AP_Periph):
BatteryTag::update()每秒检查一次解锁状态,累计通电时间;每次解锁结束(armed变为 false)时,如果本次通电时长达到_CYCLE_MIN分钟门槛,就把循环次数加一,并把通电小时数累加保存。每 10 秒向 CAN 总线广播一次 BatteryTag 消息(见 battery_tag.cpp)。 - 飞控端(Lua 脚本):BatteryTag.lua 在总线 0 与总线 1 上各注册一个
DroneCAN_Handle订阅 BatteryTag 消息,通过check_batterytag()解析消息内容,更新最高循环次数、向 GCS 发送文本报告,并把数据写入BTAG日志。
这条链路中值得注意的两个细节:
- 时间同步:脚本通过
check_globaltime()把飞控 GPS 提供的 UTC 微秒时间戳以GlobalTime消息广播给节点,节点用它校准"首次使用时间"与"最后使用时间"(battery_tag.cpp)。因此飞控必须有 3D 定位(gps:status(0) >= 3)且 GPS 时间有效时,GlobalTime 才会发出(BatteryTag.lua)。 - 序列号自动生成:如果厂商没有预设序列号,节点在首次解锁时用系统 ID 的 CRC32(截取 31 位)自动生成并保存(battery_tag.cpp)。
飞控端脚本参数:BTAG_* 三件套
脚本通过param:add_table(49, "BTAG_", 6)注册参数表(BatteryTag.lua),共三个用户参数:
| 参数名 | 显示名 | 默认值 | 说明 |
|---|---|---|---|
BTAG_ENABLE | enable battery info support | 1 | 0 为禁用脚本,1 为启用(BatteryTag.lua) |
BTAG_MAX_CYCLES | max battery cycles | 400 | 允许解锁的最大电池循环次数,范围 0–10000,超出即阻止解锁 |
BTAG_CUR_CYCLES | current battery cycles | 0 | 当前所有在线 BatteryTag 节点中最高循环次数,脚本自动更新并set_and_save持久化 |
各参数的行为细节
BTAG_ENABLE脚本启动时立即检查该参数:若为 0,直接return,不再注册任何订阅(BatteryTag.lua)。这也意味着在禁用的状态下,飞控端不会对 BatteryTag 消息做任何处理。
BTAG_CUR_CYCLES每当收到任意节点的消息,脚本会比较num_cycles与当前highest_cycles,取所有在线节点中的最大值写入该参数(BatteryTag.lua)。文档明确指出,这个"当前最高循环次数"可以被其他脚本在启动时用于基于电池老化程度修正电量百分比估算——例如在任务开始前把电池剩余百分比按使用年限打折。
BTAG_MAX_CYCLES它充当解锁闸门的阈值。当highest_cycles > BTAG_MAX_CYCLES时,脚本通过arming:set_aux_auth_failed()使辅助解锁检查失败,从而禁止解锁;否则调用arming:set_aux_auth_passed()放行(BatteryTag.lua)。只有脚本在启动时通过arming:get_aux_auth_id()成功申请到辅助认证 ID(返回非 nil)时,这个安全检查才生效(BatteryTag.lua)。
无标签保护:15 秒超时闸门
脚本还有一个隐含的安全兜底:如果启动 15 秒后仍然没有收到任何 BatteryTag 消息(have_tag仍为 false),且存在辅助认证 ID,则强制解锁失败并提示 "Battery Tag not connected"(BatteryTag.lua)。这意味着一旦启用了该脚本,飞控在 CAN 总线上检测不到电池标签节点时同样无法解锁,防止"电池未登记"的情况下升空。
BTAG 日志报文格式
每条收到的 BatteryTag 消息都会通过logger:write写入一条 BTAG 日志(BatteryTag.lua),字段如下:
| 字段 | 格式码 | 含义 |
|---|---|---|
Node | B | 发送节点的 CAN 节点 ID |
Ser | I | 电池序列号(uint32) |
NCycle | I | 循环次数 |
ArmHr | f | 通电小时数(浮点) |
Cap | I | 电池容量(mAh) |
FirstUse | I | 首次使用时间(自 1970/1/1 起的分钟数) |
LastArm | I | 最后使用时间(自 1970/1/1 起的分钟数) |
日志解析后,即可按序列号跟踪每一块电池的寿命轨迹,例如观察某块电池循环次数增长速率、判断容量衰减趋势。
GCS 文本报告机制
脚本在收到每个新节点的第一条消息时,会立即用 INFO 级别文本上报该节点的循环次数(BatteryTag.lua)。此外,在首次检测到 GCS 连接 30 秒后(GCS_REPORT_TIME_S = 30),会把当前已知的所有节点循环次数汇总上报一次,且整个运行周期内只汇总一次(sent_report标志,BatteryTag.lua)。这样操作员在地面站上就能直接看到 "BatteryTag: Node 0:5, Cycles 12" 之类的提示,无需下载日志即可快速确认各电池状态。
节点端参数与循环计数规则
节点端的参数以BTAG_前缀注册在 AP_Periph 参数表中(Parameters.cpp),完整列表如下(见 battery_tag.cpp):
| 参数 | 默认值 | 说明 |
|---|---|---|
BTAG_NUM_CYCLES | 0 | 电池已循环次数 |
BTAG_ARM_HOURS | 0 | 电池累计通电小时数 |
BTAG_CAPACITY | 0 | 电池容量(mAh) |
BTAG_FIRST_USE | 0 | 首次使用时间(自 1970/1/1 起的分钟数) |
BTAG_LAST_USE | 0 | 最后使用时间(自 1970/1/1 起的分钟数) |
BTAG_SERIAL | 0 | 电池序列号 |
BTAG_CYCLE_MIN | 1 | 计为一个循环所需的最短通电时间(分钟) |
循环计数的精确规则:节点端每 1 秒调用一次BatteryTag::update()。当检测到从"非解锁"变为"解锁"时记录arm_start_ms;解锁结束后计算本次通电分钟数armed_minutes,先累加到armed_hours并保存,再判断armed_minutes >= cycle_min是否成立,成立则num_cycles加一(battery_tag.cpp)。默认BTAG_CYCLE_MIN = 1,即通电满 1 分钟即计为一个循环。值得注意的是,BatteryTag::update()每 1 秒检查一次,但消息广播每 10 秒一次,两者节奏独立(battery_tag.cpp)。
硬件目标与启用开关
BatteryTag 节点功能由编译期宏AP_PERIPH_BATTERY_TAG_ENABLED控制,当前仓库内置了三类硬件目标:
- MatekL431-BatteryTag:继承 MatekL431 板卡定义,禁用 ADC,启用 RTC 与 GlobalTime(MatekL431-BatteryTag/hwdef.dat);
- VM-L431-BatteryTag:基于 Vimdrones L431 硬件,CAN 节点名定义为
com.vimdrones.battery_tag,同样禁用 ADC、启用 RTC(VM-L431-BatteryTag/hwdef.dat,README.md); - sitl_periph_battery_tag:用于 SITL 仿真验证的节点目标,节点名
org.ardupilot.battery_tag,同样强制启用AP_PERIPH_BATTERY_TAG_ENABLED与 RTC 相关宏(sitl_periph_battery_tag/hwdef.dat)。
构建方式与普通 AP_Periph 固件一致,例如用 waf 指定上述板卡目标编译即可烧录到节点硬件。
端到端部署清单
要在实际飞行平台上启用完整的电池寿命管理,可按以下步骤操作:
- 烧录节点固件:为电池标签硬件编译并烧录 AP_Periph 固件(如 MatekL431-BatteryTag),把节点固定在电池上。
- 设置电池档案:通过 CAN 或串口在节点上配置
BTAG_SERIAL(或留 0 让其自动生成)、BTAG_CAPACITY、BTAG_CYCLE_MIN等参数。 - 飞控启用脚本:将 BatteryTag.lua 放入飞控的 SD 卡脚本目录,启用 Lua 脚本功能并让脚本随启动加载。
- 确认参数生效:在飞控上确认
BTAG_ENABLE=1、BTAG_MAX_CYCLES设为可接受的循环上限(默认 400)。 - 验证数据流:解锁观察 GCS 是否出现 "BatteryTag: Node ..." 文本;检查日志中是否有
BTAG报文,字段NCycle、ArmHr、Ser是否随解锁次数增长。 - 利用寿命数据:让其他 Lua 脚本读取
BTAG_CUR_CYCLES,在启动时根据电池老化程度修正剩余电量估算,实现更保守的飞行计划。
从源码看设计要点
- 解耦的两段式架构:节点端只负责"记录 + 广播",飞控端只负责"接收 + 决策",两者通过标准 DroneCAN 消息解耦。这种设计让电池标签节点可以被任意支持该消息的飞控识别,也让飞控端可以同时管理多个不同型号的电池标签。
- 持久化优先:节点端的
set_and_save与飞控端的set_and_save都保证了关键数据掉电不丢失;BTAG_CUR_CYCLES也因此成为跨启动周期的"记忆参数"。 - 安全兜底完备:15 秒无标签超时、循环次数超限、以及
BTAG_ENABLE显式开关三重机制,共同构成了解锁安全检查的完整闭环(docs.lua 中arming:set_aux_auth_failed/set_aux_auth_passed/get_aux_auth_id即脚本解锁闸门所依赖的 API)。 - 面向后续扩展:
BTAG_CUR_CYCLES作为公开参数,为第三方脚本读取电池老化程度预留了标准接口,这正是原文档所述"供其他脚本调整电池百分比估算"的具体实现锚点。
适用前提与限制
- BatteryTag 功能要求飞控具备 Lua 脚本支持(AP_Scripting),节点端要求 AP_Periph 固件包含
AP_PERIPH_BATTERY_TAG_ENABLED宏对应的板卡目标; - GlobalTime 时间同步依赖 GPS 3D 定位,若飞控长期无 GPS 信号,节点的首次/最后使用时间可能不完整;
- 本文涉及的参数默认值与取值范围均以当前仓库源码为准(如 battery_tag.cpp 与 BatteryTag.lua),实际使用前请以对应固件版本的实际参数说明为准。
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考