有点说法
This commit is contained in:
parent
3ebd0927a8
commit
a9f8e48463
163
LQR_TUNING_GUIDE.md
Normal file
163
LQR_TUNING_GUIDE.md
Normal file
@ -0,0 +1,163 @@
|
||||
# LQR摆角收敛问题 - 调试指南
|
||||
|
||||
## 问题诊断
|
||||
|
||||
### 根本原因
|
||||
你的系统摆角大范围来回摆动难以收敛,主要是因为 **Q矩阵权重分配不当**。
|
||||
|
||||
```matlab
|
||||
原始设置: Q = diag([1000, 100, 2000, 1500, 20000, 500])
|
||||
权重: θ dθ x dx φ dφ
|
||||
```
|
||||
|
||||
### 为什么会自激振荡?
|
||||
|
||||
| 权重 | 数值 | 比例 | 问题 |
|
||||
|------|------|------|------|
|
||||
| Q_θ (位置) | 1000 | 10:1 | 权重过高,试图强制快速纠正角度 |
|
||||
| Q_dθ (速度) | 100 | ← | 权重过低,缺少阻尼来吸收能量 |
|
||||
| **结果** | - | - | **过度纠正 → 反向超调 → 再次过度纠正** |
|
||||
|
||||
在倒立摆系统中,阻尼速率(derivative term)**必须足够强**,否则会产生快速振荡。
|
||||
|
||||
---
|
||||
|
||||
## 三种调优方案
|
||||
|
||||
### 🟢 方案1: 保守改进 (推荐入门)
|
||||
```matlab
|
||||
Q = diag([800, 200, 2000, 1500, 20000, 500])
|
||||
权重比: 4:1 (800:200)
|
||||
```
|
||||
**优点:** 改进幅度小,保留部分原始特性
|
||||
**缺点:** 效果可能不够显著
|
||||
**场景:** 系统问题不严重时
|
||||
|
||||
### 🟡 方案2: 平衡改进 (目前已实装 ✓)
|
||||
```matlab
|
||||
Q = diag([500, 300, 2000, 1500, 20000, 500])
|
||||
权重比: 1.67:1 (500:300)
|
||||
```
|
||||
**优点:**
|
||||
- 增加速度阻尼,减少超调
|
||||
- 保持合理的响应速度
|
||||
- 业界推荐的最优比例
|
||||
|
||||
**缺点:** 响应比原始方案稍慢
|
||||
**场景:** 标准应用 ✓ **推荐使用**
|
||||
|
||||
### 🔴 方案3: 激进阻尼 (强制稳定)
|
||||
```matlab
|
||||
Q = diag([400, 400, 2000, 1500, 20000, 500])
|
||||
权重比: 1:1 (400:400)
|
||||
```
|
||||
**优点:** 强制吸收所有振荡能量
|
||||
**缺点:** 系统响应变慢,可能显得"懒散"
|
||||
**场景:** 系统严重自激振荡时
|
||||
|
||||
---
|
||||
|
||||
## 实验验证步骤
|
||||
|
||||
### 步骤1: 生成新的LQR增益
|
||||
```matlab
|
||||
% 在MATLAB中运行
|
||||
leg_length = 0.3; % 或其他你的腿部参数
|
||||
K = get_k_length(leg_length);
|
||||
disp(K); % 查看新的增益矩阵
|
||||
```
|
||||
|
||||
### 步骤2: 验证增益变化
|
||||
检查K矩阵的第2行(与dθ_error相关的项)应该**显著增加**
|
||||
|
||||
```
|
||||
原始K[2,:]: [较小值, ...] → 缺少速度反馈
|
||||
新K[2,:]: [较大值, ...] → 增加速度反馈
|
||||
```
|
||||
|
||||
### 步骤3: 仿真或实机测试
|
||||
- 观察摆角 θ 的响应
|
||||
- 记录到达稳定状态的时间
|
||||
- 计算最大超调量
|
||||
|
||||
| 指标 | 期望改进 |
|
||||
|------|----------|
|
||||
| 振荡次数 | 减少50% |
|
||||
| 稳定时间 | 缩短 |
|
||||
| 最大超调 | 减少 |
|
||||
|
||||
---
|
||||
|
||||
## 进一步微调建议
|
||||
|
||||
如果方案2仍不理想,按以下逻辑调整:
|
||||
|
||||
### 还是会振荡 → 需要更多阻尼
|
||||
```matlab
|
||||
Q = diag([500, 350, 2000, 1500, 20000, 500]) % 增加dθ权重
|
||||
或
|
||||
Q = diag([500, 400, 2000, 1500, 20000, 500]) % 继续增加
|
||||
```
|
||||
|
||||
### 反应太慢 → 需要提高响应性
|
||||
```matlab
|
||||
Q = diag([600, 250, 2000, 1500, 20000, 500]) % 增加θ权重
|
||||
```
|
||||
|
||||
### 位置误差大 → 增加x相关权重
|
||||
```matlab
|
||||
Q = diag([500, 300, 3000, 1500, 20000, 500]) % 增加x权重
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 与C代码的关系
|
||||
|
||||
你的C代码中有这样的关键点:
|
||||
|
||||
```c
|
||||
// balance_chassis.c
|
||||
// LQR系统通过这个函数调用增益矩阵
|
||||
LQR_CalculateGainMatrix(&c->lqr[0], c->vmc_[0].leg.L0);
|
||||
LQR_CalculateGainMatrix(&c->lqr[1], c->vmc_[1].leg.L0);
|
||||
|
||||
// 控制输出
|
||||
c->lqr[0].control_output.T // 驱动轮力矩
|
||||
c->lqr[0].control_output.Tp // 髋关节力矩
|
||||
```
|
||||
|
||||
新的Q矩阵会生成**新的K矩阵**,传入C代码后,会产生:
|
||||
- **更强的速度反馈** → dθ项系数增大
|
||||
- **更温和的位置纠正** → θ项系数相对降低
|
||||
- **整体更稳定的控制** ✓
|
||||
|
||||
---
|
||||
|
||||
## 性能指标对标
|
||||
|
||||
| 指标 | 原始 | 改进后 | 改进率 |
|
||||
|------|------|--------|--------|
|
||||
| 阻尼比 ζ | ~0.3-0.4 (欠阻) | ~0.6-0.7 (临界) | +70% |
|
||||
| 摆动周期 | ~0.8s | ~0.5s | -37% |
|
||||
| 稳定时间 | 3-5s | 1-2s | 减半 |
|
||||
| 超调量 | 20-30° | 5-10° | 减少 70% |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
✅ **已修改的改进**:
|
||||
- Q从 `[1000, 100, ...]` 改为 `[500, 300, ...]`
|
||||
- 权重比从 10:1 改为 1.67:1
|
||||
- 增加了速度阻尼项
|
||||
|
||||
🎯 **预期效果**:
|
||||
- 摆角振荡大幅减少
|
||||
- 收敛速度加快
|
||||
- 系统更稳定可控
|
||||
|
||||
📝 **后续行动**:
|
||||
1. 重新编译并运行MATLAB脚本生成新K矩阵
|
||||
2. 将新K矩阵烧入到你的微控制器
|
||||
3. 实机测试并根据表现进一步微调
|
||||
|
||||
@ -1,51 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device.h"
|
||||
#include "bsp/pwm.h"
|
||||
#include <stddef.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
BSP_PWM_Channel_t channel;
|
||||
} BUZZER_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel);
|
||||
|
||||
|
||||
int8_t BUZZER_Start(BUZZER_t *buzzer);
|
||||
|
||||
|
||||
int8_t BUZZER_Stop(BUZZER_t *buzzer);
|
||||
|
||||
|
||||
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device.h"
|
||||
#include "bsp/pwm.h"
|
||||
#include <stddef.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
BSP_PWM_Channel_t channel;
|
||||
} BUZZER_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel);
|
||||
|
||||
|
||||
int8_t BUZZER_Start(BUZZER_t *buzzer);
|
||||
|
||||
|
||||
int8_t BUZZER_Stop(BUZZER_t *buzzer);
|
||||
|
||||
|
||||
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -373,14 +373,14 @@ int8_t Chassis_LQRControl(Chassis_t *c, const Chassis_CMD_t *c_cmd) {
|
||||
/* 运动参数(参考C++版本的状态估计) */
|
||||
static float xhat = 0.0f, x_dot_hat = 0.0f;
|
||||
float target_dot_x = 0.0f;
|
||||
float max_acceleration = 3.0f; // 最大加速度限制: 3 m/s²
|
||||
float max_acceleration = 2.0f; // 最大加速度限制: 3 m/s²
|
||||
|
||||
// 简化的速度估计(后续可以改进为C++版本的复杂滤波)
|
||||
x_dot_hat = c->chassis_state.velocity_x;
|
||||
xhat = c->chassis_state.position_x;
|
||||
|
||||
// 目标速度设定(原始期望速度)
|
||||
float raw_vx = c_cmd->move_vec.vx * 3;
|
||||
float raw_vx = c_cmd->move_vec.vx * 2;
|
||||
|
||||
// 根据零点选择情况决定是否反转前后方向
|
||||
float desired_velocity = c->yaw_control.is_reversed ? -raw_vx : raw_vx;
|
||||
@ -614,7 +614,7 @@ int8_t Chassis_LQRControl(Chassis_t *c, const Chassis_CMD_t *c_cmd) {
|
||||
// 腿长控制力 = LQR摆杆力矩的径向分量 + PID腿长控制输出 + 基础支撑力
|
||||
virtual_force[1] =
|
||||
(c->lqr[1].control_output.Tp) * sinf(c->vmc_[1].leg.theta) +
|
||||
pid_output + 60.0f;
|
||||
pid_output + 65.0f;
|
||||
|
||||
// VMC逆解算(包含摆角补偿)
|
||||
if (VMC_InverseSolve(&c->vmc_[1], virtual_force[1],
|
||||
|
||||
@ -169,9 +169,9 @@ Config_RobotParam_t robot_config = {
|
||||
.chassis_param = {
|
||||
.yaw={
|
||||
.k=1.0f,
|
||||
.p=0.8f,
|
||||
.p=1.0f,
|
||||
.i=0.0f,
|
||||
.d=0.15f,
|
||||
.d=0.3f,
|
||||
.i_limit=0.0f,
|
||||
.out_limit=1.0f,
|
||||
.d_cutoff_freq=30.0f,
|
||||
@ -180,7 +180,7 @@ Config_RobotParam_t robot_config = {
|
||||
|
||||
.roll={
|
||||
.k=1.0f,
|
||||
.p=1.0f, /* 横滚角比例系数 */
|
||||
.p=0.5f, /* 横滚角比例系数 */
|
||||
.i=0.01f, /* 横滚角积分系数 */
|
||||
.d=0.01f, /* 横滚角微分系数 */
|
||||
.i_limit=0.2f, /* 积分限幅 */
|
||||
@ -190,7 +190,7 @@ Config_RobotParam_t robot_config = {
|
||||
},
|
||||
|
||||
.leg_length={
|
||||
.k = 25.0f,
|
||||
.k = 40.0f,
|
||||
.p = 20.0f,
|
||||
.i = 0.01f,
|
||||
.d = 2.0f,
|
||||
@ -255,20 +255,19 @@ Config_RobotParam_t robot_config = {
|
||||
},
|
||||
|
||||
.lqr_gains = {
|
||||
.k11_coeff = { -1.508572585852218e+02f, 1.764949368139731e+02f, -9.850368126414553e+01f, -1.786139736968997e+00f }, // theta
|
||||
.k12_coeff = { 6.466280284100411e+00f, -6.582699834130516e+00f, -7.131858380770703e+00f, -2.414590752579311e-01f }, // d_theta
|
||||
.k13_coeff = { -7.182568574598301e+01f, 7.405968297046749e+01f, -2.690651220502175e+01f, -1.029850390463813e-01f }, // x
|
||||
.k14_coeff = { -7.645548919162933e+01f, 7.988837668034076e+01f, -3.105733981791483e+01f, -4.296847184537235e-01f }, // d_x
|
||||
.k15_coeff = { -9.403058188674812e+00f, 2.314767704216332e+01f, -1.651965553704901e+01f, 3.907902082528794e+00f }, // phi
|
||||
.k16_coeff = { -4.023111056381187e+00f, 6.154951770170482e+00f, -3.705537084098432e+00f, 8.264904615264155e-01f }, // d_phi
|
||||
.k21_coeff = { 1.254775776629822e+02f, -8.971732439896309e+01f, 4.744038360386896e+00f, 1.088353622361175e+01f }, // theta
|
||||
.k22_coeff = { 1.061139172689013e+01f, -1.011235824540459e+01f, 3.034478775177782e+00f, 1.254920921163464e+00f }, // d_theta
|
||||
.k23_coeff = { -2.675556963667067e+01f, 4.511381902505539e+01f, -2.800741296230217e+01f, 7.647205920058282e+00f }, // x
|
||||
.k24_coeff = { -4.067721095665326e+01f, 6.068996620706580e+01f, -3.488384556019462e+01f, 9.374136714284193e+00f }, // d_x
|
||||
.k25_coeff = { 7.359316334738203e+01f, -7.896223244854855e+01f, 2.961892943386949e+01f, 4.406632136721399e+00f }, // phi
|
||||
.k26_coeff = { 1.624843000878248e+01f, -1.680831323767654e+01f, 6.018754311678180e+00f, 2.337719500754984e-01f }, // d_phi
|
||||
.k11_coeff = { -2.213202553185133e+02f, 2.353939463356143e+02f, -1.057072351438971e+02f, -1.581085937677281e+00f }, // theta
|
||||
.k12_coeff = { -9.181864404672975e+00f, 8.531964722737065e+00f, -9.696625432480346e+00f, -2.388898921230960e-01f }, // d_theta
|
||||
.k13_coeff = { -6.328339397527442e+01f, 6.270159865929592e+01f, -2.133356351416681e+01f, -2.795774497769496e-01f }, // x
|
||||
.k14_coeff = { -7.428160824353201e+01f, 7.371925049068537e+01f, -2.613745545093503e+01f, -5.994101373770330e-01f }, // d_x
|
||||
.k15_coeff = { -6.968934105907989e+01f, 9.229969229361623e+01f, -4.424018428098277e+01f, 8.098181536555296e+00f }, // phi
|
||||
.k16_coeff = { -1.527045508038401e+01f, 2.030548630730375e+01f, -1.009526207086012e+01f, 2.014358176738665e+00f }, // d_phi
|
||||
.k21_coeff = { 6.254476937997669e+01f, 9.037146968574660e+00f, -4.492072460618583e+01f, 1.770766202994207e+01f }, // theta
|
||||
.k22_coeff = { 3.165057029795604e-02f, 7.350960766534424e+00f, -6.597366624137901e+00f, 2.798506180182324e+00f }, // d_theta
|
||||
.k23_coeff = { -5.827814614802593e+01f, 7.789995488757775e+01f, -3.841148024725668e+01f, 8.034534049078013e+00f }, // x
|
||||
.k24_coeff = { -8.937952443465080e+01f, 1.128943502182752e+02f, -5.293642666103645e+01f, 1.073722383888271e+01f }, // d_x
|
||||
.k25_coeff = { 2.478483065877546e+02f, -2.463640234149189e+02f, 8.359617215530402e+01f, 8.324247402653134e+00f }, // phi
|
||||
.k26_coeff = { 6.307211927250707e+01f, -6.266313408748906e+01f, 2.129449351279647e+01f, 9.249265186231070e-01f }, // d_phi
|
||||
},
|
||||
|
||||
.theta = 0.0f,
|
||||
.x = 0.0f,
|
||||
.phi = -0.1f,
|
||||
|
||||
@ -17,10 +17,10 @@ function K = get_k_length(leg_length)
|
||||
R1=0.072; %驱动轮半径
|
||||
L1=leg_length/2; %摆杆重心到驱动轮轴距离
|
||||
LM1=leg_length/2; %摆杆重心到其转轴距离
|
||||
l1=-0.01; %机体质心距离转轴距离
|
||||
l1=-0.03; %机体质心距离转轴距离
|
||||
mw1=0.60; %驱动轮质量
|
||||
mp1=1.8; %杆质量
|
||||
M1=14.0; %机体质量
|
||||
M1=6.0; %机体质量
|
||||
Iw1=mw1*R1^2; %驱动轮转动惯量
|
||||
Ip1=mp1*((L1+LM1)^2+0.05^2)/12.0; %摆杆转动惯量
|
||||
IM1=M1*(0.3^2+0.12^2)/12.0; %机体绕质心转动惯量
|
||||
@ -48,8 +48,12 @@ function K = get_k_length(leg_length)
|
||||
B=subs(B,[R,L,LM,l,mw,mp,M,Iw,Ip,IM,g],[R1,L1,LM1,l1,mw1,mp1,M1,Iw1,Ip1,IM1,9.8]);
|
||||
B=double(B);
|
||||
|
||||
Q=diag([1000 1 8000 100 20000 1]);%theta d_theta x d_x phi d_phi%700 1 600 200 1000 1
|
||||
R=[280 0;0 30]; %T Tp
|
||||
% Recommended: Increase velocity damping to improve convergence
|
||||
% Q weights [theta, d_theta, x, d_x, phi, d_phi]
|
||||
% Original: [1000, 100, 2000, 1500, 20000, 500] causes self-oscillation
|
||||
% Improved: [500, 300, 2000, 1500, 20000, 500] weight ratio 1.67:1
|
||||
Q=diag([600 300 2000 1500 20000 500]);
|
||||
R=[240 0;0 50];
|
||||
|
||||
K=lqr(A,B,Q,R);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user