ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

STM32F407二轮平衡车:MPU6050姿态解算与串级PID调参实战

STM32F407二轮平衡车:MPU6050姿态解算与串级PID调参实战 简介基于意法半导体 STM32F407ZGT6 芯片与 MPU6050 姿态传感器利用速度环加位置环串级 PID 控制算法实现两轮自平衡车的技术资料包内容覆盖完整工程源码与 Keil 项目配置适合电子信息、自动化、计算机等专业学生作为课程设计、期末大作业或毕业设计参考资料也适合具备 C 语言与 PID 基础、希望深入理解平衡车控制原理的嵌入式开发者自学与二次开发。压缩包共有 188 个文件大小约 5.33MB主要构成为 59 个头文件、49 个 C 源文件同时包含工程配置文件、编译生成的固件与映射文件以及说明文档和辅助脚本目录结构清晰便于按模块阅读和重新编译目前已有 357 人学习或下载。资料价值体现在源码完整可直接烧录验证速度环与位置环两级 PID 以及 MPU6050 姿态解算实现思路明确算法模块相对独立附带工程配置与清理脚本可减少环境处理时间便于后续进行参数调试、算法对比或功能扩展。1. 从串级 PID 理解两轮车平衡为什么内环不倒、外环不跑每次有同行来问“为什么我的两轮车装了 MPU6050 还是倒”我第一个问题不是问 PID 参数而是问他速度环是不是没加限幅。标题里这三个词——速度环、位置环、串级 PID——描述的其实是同一个控制骨架最内层用角度环把车身拉回竖直外层用速度环和位置环把整车拉回原地。很多资料把“速度环和位置环串级”挂在标题上但真正让车不倒的不是外环而是被外环驱动的那个内环角度反馈。用 STM32F407ZGT6 跑这套系统168MHz 主频和 FPU 算三个回路绰绰有余MPU6050 也只提供加速度和角速度两组原始数据。适合刚入手自平衡车的嵌入式工程师也适合手里有资料包但对“内外环怎么嵌套、参数怎么一步一步调出来”还含糊的人。下面按姿态解算、回路搭建、硬件接线、调参、定点停车这个顺序往下讲。2. MPU6050 姿态解算先让 STM32F407ZGT6 拿到能用的角度2.1 配置 MPU6050 的要点量程、采样率、数字低通MPU6050 在平衡车里的角色不是“角度传感器”而是“角速度传感器 加速度传感器”。陀螺仪输出的是绕三个轴的角速度加速度计输出的是三个轴上的比力。平衡车只需要绕轮轴方向的车身倾角也就是 pitch 角但对角速度的实时性要求很高因为内环微分项直接使用陀螺仪瞬时值。先看量程配置。陀螺仪量程选 ±2000dps灵敏度 16.4 LSB/(°/s)这样车身快速倾倒时不会削顶加速度计量程选 ±16g 或 ±8g对应灵敏度 2048 或 4096 LSB/g。量程不是越大越好大了分辨率低静止时加速度计的噪声会直接反映到角度里所以加速度计不建议选 ±16g 以下的选项。陀螺仪量程灵敏度 LSB/(°/s)加速度量程灵敏度 LSB/g±250131±2g16384±50065.5±4g8192±100032.8±8g4096±200016.4±16g2048寄存器配置顺序一般是先设电源管理寄存器 0x6B 唤醒芯片再写 0x1B 陀螺仪量程、0x1C 加速度量程、0x1A 数字低通滤波器 DLPF最后通过 0x19 设置采样率分频。DLPF 带宽对平衡车非常关键带宽太低角度数据滞后内环容易振荡带宽太高噪声直接进 PID电机发烫。常见选择是 DLPF 配置为 0x03对应陀螺仪带宽约 44Hz加速度计带宽约 44Hz采样率用 1kHz控制周期取 2ms也就是 500Hz 读取一次。void MPU6050_Init(void) { uint8_t reg; reg 0x00; // 清除 SLEEP 位唤醒传感器 HAL_I2C_Mem_Write(hi2c1, 0xD0, 0x6B, 1, reg, 1, 50); reg 0x03; // 陀螺仪 ±2000dps HAL_I2C_Mem_Write(hi2c1, 0xD0, 0x1B, 1, reg, 1, 50); reg 0x18; // 加速度计 ±16g HAL_I2C_Mem_Write(hi2c1, 0xD0, 0x1C, 1, reg, 1, 50); reg 0x03; // DLPF 约 44Hz HAL_I2C_Mem_Write(hi2c1, 0xD0, 0x1A, 1, reg, 1, 50); reg 0x04; // 采样率 1kHz / (1 4) 200Hz HAL_I2C_Mem_Write(hi2c1, 0xD0, 0x19, 1, reg, 1, 50); }上面这段 HAL 库代码里的 0xD0 是 MPU6050 的 7 位地址 0x68 左移一位后的写地址HAL_I2C_Mem_Write最后一个参数是超时时间。需注意配置写完后要有 10ms 以上延时等传感器内部首次数据稳定再开始读取否则前几十个原始值全是 0。2.2 从加速度计计算倾角与陀螺仪积分互补纯加速度计算倾角公式是angle_acc atan2(-acc_x, acc_z)前提是加速度计按常见方式安装芯片平放X 轴指向车身前进方向Z 轴竖直向上。安装方向不同轴对应关系要重算。经验做法是上电后把车身人工扶正串口打印三个轴的原始值判断哪个轴朝下再确定公式里的符号。全角速度积分也能得到角度但陀螺仪零偏会随时间累积漂移几秒钟后角度就偏了。所以工程上最常用互补滤波高频段信任角速度积分低频段信任加速度计角度。由于平衡车振动大纯加速度计的倾角高频噪声很严重滤波系数通常在 0.95 到 0.98 之间但系数调到 0.99 以上会让角度响应极慢外环速度调节还没反应车已经倒了。另一个不可忽视的点陀螺仪零漂。每次上电后静止采样 500 组陀螺仪原始值取平均作为本次运行的零漂补偿。温度变化大时零漂会变所以启动阶段就标定一次的方案在空调房里没问题在户外温差大的环境最好每隔几分钟自动补一次。2.3 互补滤波代码与零漂处理控制周期内做一次姿态更新读取原始数据、转物理单位、做互补滤波。#define RAD_TO_DEG 57.29578f #define GYRO_SCALE 16.4f // ±2000dps 灵敏度 #define ACC_SCALE 2048.0f // ±16g 灵敏度 #define DT 0.002f // 2ms 控制周期 float angle 0.0f, gyro_rate 0.0f, gyro_offset 0.0f; void MPU6050_Read_Raw(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) { uint8_t buf[6]; HAL_I2C_Mem_Read(hi2c1, 0xD0, 0x3B, 1, buf, 6, 50); *ax (buf[0] 8) | buf[1]; *ay (buf[2] 8) | buf[3]; *az (buf[4] 8) | buf[5]; HAL_I2C_Mem_Read(hi2c1, 0xD0, 0x43, 1, buf, 6, 50); *gx (buf[0] 8) | buf[1]; *gy (buf[2] 8) | buf[3]; *gz (buf[4] 8) | buf[5]; } void Attitude_Update(void) { int16_t ax, ay, az, gx, gy, gz; float acc_pitch, gyro_deg; MPU6050_Read_Raw(ax, ay, az, gx, gy, gz); acc_pitch atan2f((float)(-ax), (float)(az)) * RAD_TO_DEG; gyro_deg (float)gx / GYRO_SCALE - gyro_offset; angle 0.98f * (angle gyro_deg * DT) 0.02f * acc_pitch; gyro_rate gyro_deg; } void Gyro_Calibrate(void) { int16_t ax, ay, az, gx, gy, gz; float sum 0.0f; for (int i 0; i 500; i) { MPU6050_Read_Raw(ax, ay, az, gx, gy, gz); sum gx; HAL_Delay(2); } gyro_offset sum / 500.0f / GYRO_SCALE; }互补滤波公式中0.98 是角速度积分的权重0.02 是加速度计角度的修正权重两者之和必须为 1。angle gyro_deg * DT是角度积分acc_pitch是绝对基准。陀螺仪短时间积分可信所以给它更高权重加速度计长期无漂移所以用它修正积分的缓慢偏移。这段代码里gyro_offset的单位已经转换成 °/s和gyro_deg保持一致。读取原始数据时要注意MPU6050 的寄存器地址在连续读取时会自动递增所以 6 个字节一次读完比单独读每个寄存器效率高也更不容易出现 I2C 总线竞争导致的角度跳变。若 I2C 读返回 HAL_TIMEOUT本次直接沿用上一次的 angle不要清零否则滤波状态突变会让车猛抽一下。3. 串级 PID 的回路结构位置环、速度环与内层角度环的嵌套3.1 为什么需要串级先压住姿态再控制速度单环 PID 直接拿角度误差算 PWM在静态能站住但人推一下车车会加速往一个方向逃直到角度误差足够大才刹车表现为“跑飞”。原因是 PWM 直接控制的是轮子的加速度而角度误差来自车身倾斜两者之间隔着动力学耦合纯比例控制没法同时保证“姿态稳定”和“位置不漂移”。串级 PID 的思路是把控制目标分级内环角度环的期望值不是固定的 0而是由外环速度环给出的目标角度速度环的期望速度又由位置环给出。这样车的控制链路是位置误差 → 目标速度 → 速度误差 → 目标角度 → 角度误差 → PWM 占空比。标题里的“速度环和位置环串级”指的就是这条链上的外两层。最常见说法其实叫“三环串级”内环角度环是独立的支撑回路速度环负责抵消车轮漂移位置环负责定点或限位。网上一些资料只提“速度环 位置环”是为了强调让车站稳之后真正决定控制品质的是这两个外环的设计。速度环给内环的“目标角度”是有限幅的一般限制在 ±15° 以内。如果外环出现大的速度误差目标角度瞬间给到 30°内环角度环就算拼尽全力车的倾角已经太大物理上就救不回来了所以限幅本身就是一种安全保护。3.2 内外环的时间间隔与控制频率内环必须高于外环一个数量级。我一般把角度环放在 500Hz也就是 2ms 一次速度环 100Hz位置环 50Hz。原因是内环是系统稳定性的根本外环只负责缓慢修正如果内外环同频速度环的噪声会直接注入角度环的期望值让车身频繁“点头”。回路频率周期作用位置环50Hz20ms输出目标速度限制车移动范围速度环100Hz10ms输出目标角度抵消车轮漂移角度环500Hz2ms输出 PWM维持车身竖直具体实现不一定要开三个定时器中断。常见做法是在一个 2ms 定时器中断里做计数分频每次执行角度环每 5 次执行一次速度环每 10 次执行一次位置环。这样的好处是时间基准统一不会出现多个中断互相抢占导致角度环周期抖动。void TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { static uint8_t tick 0; if (htim-Instance ! TIM7) return; Attitude_Update(); target_angle Angle_PID(angle, gyro_rate); Motor_PWM(target_angle); if (tick 5) { tick 0; float speed Get_Speed_From_Encoder(); target_angle Speed_PID(0.0f, speed); } }注意这里代码顺序角度环每次都执行速度环每 5ms 执行一次但目标角度target_angle在角度环计算之前已经由速度环更新过了。实际工程中更稳妥的做法是先执行外环再执行内环因为内环要用最新的目标值所以上面的代码里速度环target_angle Speed_PID(...)放在了角度环之后下一次 2ms 中断的角度环才会用到新目标这正好形成外环 10ms 周期、内环 2ms 周期的错位执行。无人机串级 PID 也是同样的思路内环角速度环跑 1000Hz外环姿态环跑 250Hz外环给定的是期望姿态角速度。二轮平衡车因为驱动是轮子而非旋翼动力学更慢频率可以整体降低一档。3.3 核心代码位置环→速度环→角度环的一次控制周期把三个环写进一个统一的控制函数便于调试时单独使能或禁掉某环。typedef struct { float kp, ki, kd; float integral, last_err; float out_max, integral_max; } PID_t; float PID_Update(PID_t* pid, float target, float feedback, float dt) { float err target - feedback; float p_out pid-kp * err; pid-integral err * dt; // 积分限幅 if (pid-integral pid-integral_max) pid-integral pid-integral_max; if (pid-integral -pid-integral_max) pid-integral -pid-integral_max; float i_out pid-ki * pid-integral; float d_out pid-kd * (err - pid-last_err) / dt; pid-last_err err; float out p_out i_out d_out; // 输出限幅 if (out pid-out_max) out pid-out_max; if (out -pid-out_max) out -pid-out_max; return out; } void Balance_Control_Cycle(PID_t* pos_pid, PID_t* speed_pid, PID_t* angle_pid) { float pos_err target_position - current_position; float target_speed PID_Update(pos_pid, target_position, current_position, 0.02f); target_speed CLAMP(target_speed, -0.3f, 0.3f); if (fabsf(pos_err) 0.03f) { target_speed 0.0f; // 位置死区防止往复振荡 } float target_angle PID_Update(speed_pid, target_speed, current_speed, 0.01f); target_angle CLAMP(target_angle, -15.0f, 15.0f); float pwm_out PID_Update(angle_pid, target_angle, angle, 0.002f); Motor_SetPWM(pwm_out); }这段代码体现了串级 PID 的关键特征每一环的输出是下一环的给定值而且每一环都有独立的限幅。位置环的输出限幅 0.3m/s意思是位置误差再大车的最大期望速度也只有 0.3m/s防止起步猛冲速度环的输出限幅 15°防止目标角度给得过大角度环的输出限幅没有写在函数里但Motor_SetPWM内部会做占空比限幅。pos_err死区 3cm 的用意车上有编码器噪声、转动摩擦、地面不平位置误差在 ±3cm 以内时实际已经算到位强行走反而引起震荡。积分限幅integral_max尤其重要速度环积分项如果无限累积停车后电机会持续输出一个偏置电流车会缓慢往一个方向爬。调参数时先设积分限幅为输出限幅的一半再逐步调整。4. 硬件连接与工程初始化从原理图到定时器编码器配置4.1 对着原理图和 stm32f407zgt6 数据手册先确认三件事拿到二轮平衡车资料包先别急着解压代码。工程环境千差万别芯片一样、外设连接不一样代码能直接跑的概率不大。我一般先看原理图确认三件事第一供电结构。电机驱动和单片机是不是同一路电源常见设计是锂电池经过降压模块同时给 STM32F407ZGT6 和 TB6612 电机驱动供电电机堵转电流大时系统电压会跌落几伏板载稳压若压差不足单片机会掉电重启。至少要让编码器供电和逻辑电源独立滤波。第二BOOT 配置。F407 的 BOOT0 引脚电平决定启动方式很多资料板出厂默认 BOOT0 悬空或拉低从主 Flash 启动。如果发现下不进程序先量 BOOT0 是不是低电平。第三MPU6050 的上拉电阻。I2C 总线上拉电阻在 2.2k 到 4.7k 之间是正常范围有些精简版资料板板载电阻根本没焊I2C 通信偶尔超时表现为上电能跑、跑着跑着角度跳变。原理图上用万用表量一下 SDA/SCL 到 3.3V 的阻值即可确认。数据手册在这里的用处主要是查引脚复用功能表和时钟树。F407 的定时器时钟来源不看手册容易搞错TIM1/TIM8 挂在 APB2168MHzTIM2-TIM5 挂在 APB184MHz如果分频系数配错PWM 频率和编码器计数周期都会差一倍。4.2 引脚分配与定时器配置以常见的 STM32F407ZGT6 平衡车方案为例引脚分配如下表所示。不同板子的引脚复用功能在数据手册里都能查到遇到资源冲突时优先调整编码器定时器因为编码器对引脚的复用功能依赖较强。外设引脚定时器/功能说明MPU6050 SCLPB8I2C1_SCL4.7kΩ 上拉到 3.3VMPU6050 SDAPB9I2C1_SDA4.7kΩ 上拉到 3.3V左电机 PWMPA8TIM1_CH110kHz-20kHz右电机 PWMPA9TIM1_CH2与左电机共定时器左编码器PB6 / PB7TIM4_CH1 / TIM4_CH2正交解码模式右编码器PA0 / PA1TIM2_CH1 / TIM2_CH2正交解码模式PWM 用 TIM1 输出比较模式编码器用 TIM2 和 TIM4 的正交编码器模式。实际上很多资料板左右编码器对调配在 TIM2 和 TIM4 上因为这两个定时器的编码器接口引脚正好分布在 GPIOA 和 GPIOB方便 PCB 布线。4.3 电机 PWM 输出与编码器测速初始化TIM1 是高级定时器初始化 PWM 后必须开启主输出使能否则引脚上量不到波形。HAL 库中的配置片段如下TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; sConfigOC.OCIdleState TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState TIM_OCNIDLESTATE_RESET; HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); __HAL_TIM_MOE_ENABLE(htim1); // 高级定时器主输出使能这行不能少__HAL_TIM_MOE_ENABLE很多人漏掉。高级定时器的输出由 MOE 位总开关控制CubeMX 生成的初始化代码通常不会默认打开结果 PWM 配置全对但电机就是不动。编码器定时器配置TIM_Encoder_InitTypeDef sEncoderConfig {0}; sEncoderConfig.EncoderMode TIM_ENCODERMODE_TI12; sEncoderConfig.IC1Polarity TIM_ICPOLARITY_RISING; sEncoderConfig.IC2Polarity TIM_ICPOLARITY_RISING; sEncoderConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sEncoderConfig.IC2Selection TIM_ICSELECTION_DIRECTTI; sEncoderConfig.IC1Prescaler 0; sEncoderConfig.IC2Prescaler 0; HAL_TIM_Encoder_Init(htim4, sEncoderConfig); HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL);编码器模式TIM_ENCODERMODE_TI12表示两路输入同时参与计数每转一个编码器格计数器增减 4 倍最大分辨率。测速时直接读__HAL_TIM_GET_COUNTER(htim4)得到的是一个带符号的整数正负表示旋转方向。速度换算公式float wheel_speed_ms (float)counter / (ticks_per_rev * 4) * wheel_circumference / dt;其中ticks_per_rev是电机输出轴每转对应的编码器线数如果是减速电机要乘以减速比。比如编码器线数 13、减速比 30那么ticks_per_rev 13 * 30 390四倍频后每转计 1560 个脉冲。轮径设为 65mm 时轮周长约 0.204m。dt 是测速周期速度环 100Hz 时 dt 0.01s。读计数值的代码要注意用int32_t强转因为__HAL_TIM_GET_COUNTER返回值类型是 uint32_t如果两个方向计数导致 CNT 为负直接按无符号数读会得到一个很大的正数。5. 调参顺序和实战踩坑从“砸地”到“站住”再到“不漂”5.1 摘掉外环先调内环角度环的 Kp 与 Kd串级 PID 调参的第一原则摘掉外环先只调内环。把速度环和位置环的输出全部清零目标角度直接给 0然后从小到大加角度环的 Kp。我习惯从 Kp 10 开始用手扶着车体感受电机是否在对抗倾斜。Kp 太小车完全无力松手直接倒Kp 慢慢加到 20 到 40 区间车身开始恢复直立但会持续抖动。这个阶段不要急着加 Kd先把 Kp 提上去找到“能短暂直立”的点。然后加 Kd每次增加 0.5直到抖动明显收敛。Kd 的值对应的是阻尼它抑制的是角速度本身不是角度的导数所以直接读陀螺仪的gyro_rate参与运算不要对 angle 做差分——差分会放大高频噪声而陀螺仪本身已经是很干净的角速度信号。内环能站住后验证一个关键现象用手推一下车车应该快速回正但回正过程有轻微超调。如果回正后反向倒下去说明 Kd 不够如果回正过程发闷、迟钝说明 Kd 过大。5.2 增量式速度环怎么调从防漂移到抗扰动内环站住后如果车开始朝一个方向持续加速这就是速度环缺失的表现。原因很简单车身略微前倾时内环为了让车保持直立会让车轮前进如果没有任何外环修正车就一直往前加速直到角度彻底失衡。速度环的目标是让“车轮速度”始终趋向 0从而抵消漂移。速度环通常用 PI不用 D。原因是速度反馈本身来自编码器差分已经有量化噪声再加 D 会引入毛刺。调整时先关掉积分项只加 Kp_speed从小值 0.3 开始观察车是否还持续加速。Kp_speed 太小漂移减得很慢Kp_speed 过大车会来回“点头”因为速度误差会变成大幅度的角度目标摆动。速度环 Kp 稳定后再慢慢加 KiKi 的作用是消除静态误差也就是长时间朝一个方向的缓慢漂移。增量式速度环的常见实现是每次累加误差增量而不是累加绝对误差这样能避免起步瞬间积分饱和void Speed_Update_Inc(float target_speed, float current_speed, float dt) { static float last_err 0.0f; float err target_speed - current_speed; float delta_out speed_pid.kp * (err - last_err) speed_pid.ki * err * dt; speed_pid.integral delta_out; speed_pid.integral CLAMP(speed_pid.integral, -5.0f, 5.0f); last_err err; }增量式的好处是输出量是“变化量”而非“绝对值”即使控制周期偶尔丢一次也不会导致输出突变。缺点是抗积分限幅能力不如位置式所以integral必须做限幅否则长期单方向漂移会让积分项悄悄爬满。5.3 几个容易反复踩的坑现象可能原因解决办法上电后角度跳变I2C 上拉电阻缺失或时钟线干扰量 SCL/SDA 电平补 4.7kΩ 上拉车朝一个方向加速内环极性反了把 Kp 设为负值试试判断反馈方向静止时电机嗡嗡抖动内环 D 项过大或 DLPF 带宽过高降低 Kd把 DLPF 调到 20Hz 左右速度环一开就振荡速度环限幅太大或 Kp_speed 过大目标角度限幅降到 ±10°Kp_speed 减半编码器读数方向异常A/B 相接反交换编码器两相输入或读值取反跑几分钟后开始漂移陀螺仪零漂受热变化运行中周期性重新标定零漂下不进程序BOOT0 配置错误确认 BOOT0 低电平从 Flash 启动调参时一个很有用的技巧把angle、target_angle、current_speed通过串口打印出来。但注意打印频率不要超过 50Hz否则大量串口中断会干扰 2ms 控制周期。打印数据时用浮点数格式化F407 的 FPU 能扛住但printf重定向到串口时建议加一个 DMA 发送避免阻塞控制循环。6. 用位置环做定点停车死区、软限位与串口验证位置环是串级链条的最外层它的实际作用是“限定车的活动范围”。做定点停车时目标位置设 0位置反馈来自速度积分。实现上有两种一种是把编码器计数值直接当作位置另一种是每次速度环更新时累加speed * dt。前者更准确但要注意编码器计数值的范围避免溢出后者简单但有累积误差。定点停车的核心参数有两个位置环 Kp 和位置死区。Kp 决定车距目标多近时开始减速Kp 太大车在目标点附近反复冲过头Kp 太小车到不了目标就停了。死区给 2cm 到 5cm车速在该区间内直接置零防止编码器噪声引起持续微调。void Position_Loop(void) { float pos_err target_position - current_position; if (fabsf(pos_err) 0.03f) { target_speed 0.0f; } else { // 软限位误差越大目标速度越大但最大不超过 0.3m/s target_speed position_pid.kp * CLAMP(pos_err, -0.3f, 0.3f); } }这里用CLAMP而不是直接线性放大是为了避免位置误差大时速度目标超限。软限位的思路可以延展到其他场景比如给车设定前后各 1m 的活动范围位置环目标设为区间中值车就会自动维持在区间内把目标位置改成遥控给定的变量就变成了遥控驻车模式。验证定位精度时把车放在地面用直尺量实际停止位置和目标位置的距离差。前提是先把车调稳否则位置环收敛得再好内环不稳也是白搭。串口打印pos_err和target_speed正常收敛过程应该是误差逐渐减小到死区内目标速度同步衰减到 0没有来回穿越。数据可以通过 Vofa 或匿名上位机画曲线观察目标速度和实际速度两条曲线是否同步收敛如果实际速度在零附近反复跳动说明位置死区还不够大或者速度环积分限幅偏高。调试到这里二轮车的平衡、防漂移和定点停车就形成了一个完整闭环剩下的工作就是根据实际机械结构微调滤波器系数和死区范围。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表