这是一个为了 RoboMaster 比赛设计的电控功率控制模块,面向 RoboMaster 底盘电机的功率预测、功率限制与功率分配。该项目主要用于在裁判系统功率限制下尽量稳定地分配底盘功率,降低超功率风险,并提供了适配超级电容、舵轮底盘和全向轮小陀螺补偿的相关接口与思路。
本项目来自 304 战队电控组的实际调试与比赛经验。代码和参数仍可能存在不足,欢迎交流和指正。
- 支持单电机功率预测与功率限制。
- 支持四电机底盘的功率分配,如全向轮、麦轮等常见构型。
- 通过电机误差
error分配功率,并为每个电机预留基础功率。 - 预留超级电容剩余能量衰减接口。
- 提供舵轮结构中舵向电机与轮向电机的功率分配思路。
- 提供全向轮小陀螺行进偏移补偿思路。
- 提供
Fitting.py,可根据实测数据拟合电机功率模型参数。 old_version/中保留了旧版纯 C 实现,便于需要 C 版本的项目参考或迁移。
PowerManager/
├── Fitting.py # 电机功率模型参数拟合脚本
├── power_manager.cpp # 功率控制模块实现
├── power_manager.h # 功率控制模块接口与可调参数
├── README.md # 项目说明文档
└── old_version/ # 旧版纯 C 实现
├── powerctrl.cpp
├── powerctrl.h
└── README.md
-
将
power_manager.cpp和power_manager.h添加到你的工程中。例如使用 CMake 时,可以把
power_manager.cpp加入目标源码,并保证power_manager.h所在目录在 include path 中。 -
在底盘任务或电机控制任务中包含头文件。
#include "power_manager.h"
-
根据你的电机类型初始化
motor_power_init_t参数。可以先使用本文给出的 M3508 / GM6020 测试参数跑通流程,再根据自己的实测数据使用
Fitting.py重新拟合。 -
为每个底盘电机创建
MotorPower对象,并创建ChassisPowerManager。 -
在控制循环中先更新每个电机的误差,再调用
allocatePower()分配功率。 -
在 PID 输出已经计算完成、但尚未发送给电调之前,调用每个电机的
limiter()对输出电流进行限制。
以下参数来自作者的初步测试,仅供快速跑通流程使用。不同机器人、电机状态、供电结构、采样方法和负载条件都会影响模型参数,正式使用前建议自行拟合。
M3508 参数:
motor_power_init_t M3508_power_init_data(
0.65213,
-0.15659,
0.00041660,
0.00235415,
0.20022,
1.08e-7,
1000
);GM6020 参数:
motor_power_init_t GM6020_power_init_data(
0.7507578,
-0.0759636,
-0.00153397,
0.01225624,
0.19101805,
0.0000066450,
1000
);适用于常见四电机构型,如全向轮、麦轮等。
// 初始化
motor_power_init_t motor_3508_power_data(
0.65213,
-0.15659,
0.00041660,
0.00235415,
0.20022,
1.08e-7,
1000
);
MotorPower m3508_1_power(motor_3508_power_data);
MotorPower m3508_2_power(motor_3508_power_data);
MotorPower m3508_3_power(motor_3508_power_data);
MotorPower m3508_4_power(motor_3508_power_data);
ChassisPowerManager chassis(
&m3508_1_power,
&m3508_2_power,
&m3508_3_power,
&m3508_4_power
);
// 控制循环中更新每个电机的 error
// 这里的 error 为 PID 目标速度与当前反馈速度的差值
chassis.updateMotorError(0, m1_target - static_cast<float>(m3508_1.feedback_.speed));
chassis.updateMotorError(1, m2_target - static_cast<float>(m3508_2.feedback_.speed));
chassis.updateMotorError(2, m3_target - static_cast<float>(m3508_3.feedback_.speed));
chassis.updateMotorError(3, m4_target - static_cast<float>(m3508_4.feedback_.speed));
// target 填入希望限制的底盘总功率
chassis.allocatePower(target);
// 在 PID 输出已经算出、但还未发送给电调之前调用 limiter()
m3508_1_power.limiter(&m1_output, m3508_1.feedback_.speed, m3508_1_power.power_limit);
m3508_2_power.limiter(&m2_output, m3508_2.feedback_.speed, m3508_2_power.power_limit);
m3508_3_power.limiter(&m3_output, m3508_3.feedback_.speed, m3508_3_power.power_limit);
m3508_4_power.limiter(&m4_output, m3508_4.feedback_.speed, m3508_4_power.power_limit);ChassisPowerManager::allocatePower() 预留了 buffer_power_attenuation 参数,默认值为 1.0。你可以将超级电容剩余能量映射到 0.0 ~ 1.0 的衰减系数中,再传入该参数。
例如裁判系统限制功率为 100 W,但你希望结合超级电容能量动态调整可用功率,可以这样设计:
double super_cap_percent = SuperCapPercent; // 范围 0.0 ~ 1.0
double attenuation = 0.5 + 0.5 * super_cap_percent;
chassis.allocatePower(120.0, attenuation);也可以根据操作手模式自行设计,例如普通模式限制到 100 W,按下 Shift 后允许更高功率,但仍由超级电容剩余能量参与衰减。
Fitting.py 用于根据实测数据拟合电机功率模型参数。脚本中的数据格式为:
# 电流 A, 转速 rpm, 功率 W
data = [
(current, speed, power),
]需要注意的是,本文示例参数在拟合功率模型时使用的 current 不是电机原始反馈值,而是“电机反馈电流值 / 1000”之后的值。这个 1000 对应 motor_power_init_t 初始化时的最后一个参数 real_current_conversion。
当前脚本使用的模型形式为:
W = K0
+ K1 * current
+ K2 * speed
+ K3 * current * speed
+ K4 * current * current
+ K5 * speed * speed
拟合完成后,脚本会输出 K0 ~ K5、拟合优度、均方误差和可能的异常数据。你可以把输出的系数填入 motor_power_init_t。
运行方式:
python Fitting.py如果缺少依赖,需要先安装 numpy:
pip install numpy我于 2024 年夏入学,同时加入天津工业大学 304 战队,担任 25 赛季哨兵和步兵电控。由于 25 赛季队伍处于重组期,队内留存的知识体系较少,也没有成型的功率控制模块,比赛时经常出现机器人超功率后停在原地、被迅速击杀的情况。
即使队伍有超级电容模块,如果缺乏有效的功率控制,超级电容也可能以很快的速度被消耗完。因此在 26 赛季,我学习了多所学校的功率控制算法,并结合自己的实测数据,开发了这份较稳定、便于移植的功率控制算法供队内使用。
经测试,在 26 赛季 RMUL 中,部署该模块的机器人均未出现明显超功率问题,实际效果较好。
本项目主要包含三部分:
- 功率预测:根据电机电流、转速和拟合模型估算功率。
- 功率限制:根据目标功率上限限制单电机发送电流。
- 功率分配:根据底盘总功率限制,将功率分配给各个电机。
此外,代码中还包含适配超级电容、舵轮功率分配、全向轮小陀螺偏移补偿和常用滤波辅助函数等内容。
刚开始做功率控制时,我曾尝试直接通过电机反馈数据计算电机消耗功率,但由于误差较大,很难用简单公式直接算准。后来在观看中科大电控教程并参考其他开源项目后,才意识到更常见的做法是先拟合电机模型,再根据模型预测功率。
拟合模型的基本思路是:采样电机在不同转速、不同电流下的功率,得到多组数据,再拟合出一个以电流和转速为自变量、功率为因变量的函数。
一个简单的模型示例:
W = k1 * current * speed
+ k2 * current * current
+ k3 * speed * speed
+ k4
其中 k1、k2、k3、k4 是需要通过实验辨识或拟合得到的参数。模型形式可以有很多种,例如线性模型、二次模型,或者考虑铜损、铁损的经验模型。
本项目采用的模型形式为:
W = K0
+ K1 * current
+ K2 * speed
+ K3 * current * speed
+ K4 * current * current
+ K5 * speed * speed
理论上,如果采样数据足够多、覆盖范围足够广,拟合模型就相当于用一张比较细的表去查功率。
以 M3508 电机为例,可以给电机发送恒定速度目标,简单套一个纯 P 环,并尽量减小震荡。随后通过串口输出电机反馈电流与转速,同时在电机供电回路中串接功率计。
采样时需要注意:拟合用的供电支路最好只包含当前电机和电调,不要把开发板等其他负载也接入同一功率计,否则开发板功率也会被计入数据。
不带负载时,可以直接读取恒定速度下对应的功率、电流和转速。带负载数据也很重要,因为赛场上的电机通常处于带负载状态。作者曾采用过比较粗糙的方法:给 M3508 发送恒定电流,用手给电机施加负载,让功率计显示接近恒定功率时读取电流和转速。
这种方法能用来初步验证,但误差较大,尤其是高转速区间很难覆盖。更推荐使用更稳定、可重复的加载方式,例如正弦输入、机械加载装置或其他可控负载方案。
关于符号问题,建议拟合时先对速度和电流取绝对值。理想情况下,电机正转和反转的功率模型应当接近一致。但当转速和电流符号相反时,可能涉及发电功率与耗电功率的问题,使用时需要结合具体控制逻辑判断。
功率限制主要用于限制单个电机的功率。
M3508 的反馈电流范围为 -16384 ~ +16384。如果要转换为实际相电流,一般需要除以 819.2,但本项目代码中使用的是 1000。这里的换算比例并不要求绝对等于物理电流,因为最终还会再转换回发送值;它更主要的作用是缩放模型输入,避免数值过大。
因此,如果你想手动把数据代入单电机模型计算预测功率,需要保持和拟合时相同的输入尺度:例如示例参数中 real_current_conversion = 1000,则代入模型的电流应为 电机反馈电流值 / 1000,而不是原始反馈值本身。代码调用 MotorPower::update() 或 MotorPower::limiter() 时会自动完成这一步换算,手动计算时才需要特别注意。
对于 M3508 和 GM6020,反馈值范围和发送值范围具有较强对应关系。因此,可以使用反馈值拟合出的模型来限制即将发送给电机的电流值。
具体流程如下:
- 使用 PID 即将发送的电流值和电机当前反馈速度,通过模型预测即将消耗的功率。
- 如果预测功率小于目标限制功率,则不做限制。
- 如果预测功率大于目标限制功率,则计算一个
0.0 ~ 1.0的衰减系数。 - 将发送电流乘以衰减系数,使预测功率不超过限制值。
求衰减系数时会解一个二次方程,并根据判别式分为 > 0、= 0、< 0 三种情况。若无有效解,则将发送电流降为 0;若有多个有效解,则优先选择 0.0 ~ 1.0 范围内较大的解。
在代码中,单电机功率限制由 MotorPower 类实现。初始化时填入 K0 ~ K5 和电流换算比例,循环中根据当前速度和即将发送的电流预测功率,并在超出限制时通过指针修改发送电流。
功率分配的目标是将底盘总功率合理分配到每个电机。
本项目采用“根据 error 分配 + 每个电机预留基准功率”的方式:
- 根据每个电机 PID 的目标值和当前值误差大小分配功率,使每个电机用于减小误差的功率大致一致。
- 为每个电机预留一部分基础功率,因为电机和电调本身也会消耗少量功率,即使发送值为
0也不一定完全没有功耗。
实际测试中,这种方法的稳定性较好。
代码中使用 ChassisPowerManager 管理四个 MotorPower 对象,并按照上述方法将底盘总功率分配给各个电机。
26 赛季中,作者主要负责哨兵电控。哨兵底盘为舵轮结构,包含 4 个舵向电机和 4 个轮向电机,功率更加紧张,因此功率分配策略更重要。
作者采用的策略是:总功率的 50% 优先满足舵向电机的误差收敛需求,剩余功率以及舵向电机未使用完的功率再分配给轮向电机。
在代码设计上,可以建立两个 ChassisPowerManager,分别代表舵向部分和轮向部分,再按照上述策略分配功率。50% 只是作者实测时使用的比例,实际使用时应根据底盘结构和控制需求调整。
在全向轮底盘测试功率控制时,作者发现小陀螺状态下存在行进方向偏移。推测原因是功率控制会限制电机输出,使电机速度变化不再是瞬时的,从而导致行进方向变化也存在滞后。
偏移程度至少与当前平移速度、旋转速度、功率上限,以及平移速度和旋转速度的比值等因素有关。
作者当前的处理方式是在底盘解算时给 theta 角加入与旋转速度 rotate 相关的前馈补偿,从而在一定程度上抑制偏移。但这不是彻底方案,建议参考河北工业大学开源的全向轮小陀螺补偿方案进一步优化。
- 示例参数仅供参考,正式比赛或实车调试前请重新采样并拟合。
- 功率计采样时,请尽量保证被测支路只包含目标电机和电调。
- 带负载数据会显著影响模型质量,建议覆盖尽可能多的转速和负载区间。
- 代码中的宏定义参数集中在
power_manager.h,可根据稳定性、功率利用率和机器人实际表现进行调整。 old_version/中的旧版代码为纯 C 风格,可供需要 C 版本的项目参考。
本项目采用 MIT License 开源,详见 LICENSE。
开源这个项目,主要是希望把自己的思路和代码分享出来,给后面想研究功率控制的人提供一些帮助,就像曾经接受帮助的自己一样。部分实现可能仍不如强队方案成熟,欢迎大家交流。
由于学业原因,27 赛季我可能难以继续担任电控主力开发工作,更多会以培养电控新生为主,偶尔帮忙调车。
希望所有机甲大师都能不忘记自己参赛的初心,最终收获友谊与成长。
联系方式:
- QQ:1811410374
- 邮箱:同 QQ
304 战队 电控组 付晟君
2026.4.8
By mc3545dada