使用c语言实现ADRC,并且将浮点数转为定点数运算
时间: 2025-07-08 09:31:45 浏览: 3
<think>好的,我现在需要帮助用户了解如何使用C语言实现ADRC(自抗扰控制器),并将浮点数转换为定点数运算。首先,我得回顾一下ADRC的基本原理,然后思考如何在C语言中实现它,特别是定点数的转换部分。
ADRC主要由跟踪微分器(TD)、扩张状态观测器(ESO)和非线性状态误差反馈(NLSEF)组成。用户可能对这三个部分的具体实现不太清楚,特别是数学公式部分。我需要将每个部分的离散化公式整理出来,并用C语言的结构体或函数来表示。
接下来是定点数运算的问题。用户可能是在嵌入式系统或资源受限的环境中工作,需要优化性能。将浮点数转为定点数需要考虑精度(Q格式),比如Q15或Q31,这取决于处理器的位宽。需要解释如何选择Q格式,以及如何进行乘法和加法的运算,避免溢出。
可能用户对定点数的转换步骤不熟悉,比如如何将浮点数乘以2的n次方取整,以及运算后的移位操作。这部分需要详细说明,并给出示例代码,比如定义宏或函数来处理乘法和加法。
另外,用户可能在实际实现中遇到溢出或精度损失的问题,需要提醒他们在运算顺序和中间结果的存储上注意,比如使用更大的数据类型来暂存中间值,再进行截断。
还需要考虑代码的结构,比如用结构体来保存ADRC的状态变量和参数,这样便于模块化。每个函数对应ADRC的一个部分,如TD、ESO、NLSEF,以及最终的控制器输出计算。
最后,测试和验证也是重要的部分,用户可能需要知道如何验证定点数转换后的效果,比如与浮点版本对比,调整Q格式的精度,或者处理动态范围较大的情况。
总结下来,我需要分步骤解释ADRC的实现,然后详细说明定点数转换的方法,并提供代码示例和注意事项,确保用户能够理解并正确应用。</think>以下分步骤介绍C语言实现ADRC及定点数转换方法:
# 一、ADRC基本原理
ADRC由三个核心模块构成:
$$ TD: \begin{cases}
v_1(k+1) = v_1(k) + h \cdot v_2(k) \\
v_2(k+1) = v_2(k) + h \cdot fhan(v_1(k)-v(k), v_2(k), r, h_0)
\end{cases} $$
$$ ESO: \begin{cases}
e = z_1(k) - y(k) \\
z_1(k+1) = z_1(k) + h(z_2(k) - \beta_{01}e) \\
z_2(k+1) = z_2(k) + h(z_3(k) - \beta_{02}fal(e, \alpha_1, \delta) + b_0u(k)) \\
z_3(k+1) = z_3(k) + h(-\beta_{03}fal(e, \alpha_2, \delta))
\end{cases} $$
$$ NLSEF: \begin{cases}
e_1 = v_1 - z_1 \\
e_2 = v_2 - z_2 \\
u_0 = \beta_1 fal(e_1, \alpha, \delta) + \beta_2 fal(e_2, \alpha, \delta)
\end{cases} $$
# 二、定点数转换方法
## 1. Q格式选择
- 32位系统推荐Q15格式(16位整数+15位小数)
- 64位系统可用Q31格式(32位整数+31位小数)
```c
typedef int32_t q15_t; // Q15格式定义
#define Q 15 // 小数位数
```
## 2. 基本运算实现
```c
// 浮点转定点
#define FLOAT_TO_Q15(x) ((q15_t)((x) * (1 << Q)))
// 定点转浮点
#define Q15_TO_FLOAT(x) ((float)(x) / (1 << Q))
// 定点乘法(防溢出)
q15_t q15_mul(q15_t a, q15_t b) {
int32_t tmp = (int32_t)a * (int32_t)b;
return (q15_t)(tmp >> Q);
}
// 定点加法(需保证不溢出)
q15_t q15_add(q15_t a, q15_t b) {
return a + b; // 需确保运算在范围内
}
```
# 三、C语言实现
## 1. 数据结构定义
```c
typedef struct {
q15_t v1; // TD状态1
q15_t v2; // TD状态2
q15_t z1; // ESO状态1
q15_t z2; // ESO状态2
q15_t z3; // ESO状态3
q15_t beta01; // ESO参数
q15_t beta02;
q15_t beta03;
q15_t b0; // 系统系数
q15_t h; // 采样周期
} ADRC_Controller;
```
## 2. 核心函数实现
```c
void ADRC_Update(ADRC_Controller* ctrl, q15_t ref, q15_t y) {
// TD跟踪微分器
q15_t e = ctrl->v1 - ref;
q15_t fh = fhan(e, ctrl->v2, FLOAT_TO_Q15(1.0), ctrl->h);
ctrl->v1 += q15_mul(ctrl->h, ctrl->v2);
ctrl->v2 += q15_mul(ctrl->h, fh);
// ESO观测器
e = ctrl->z1 - y;
q15_t fe = fal(e, FLOAT_TO_Q15(0.5), FLOAT_TO_Q15(0.01));
ctrl->z1 += q15_mul(ctrl->h, ctrl->z2 - q15_mul(ctrl->beta01, e));
ctrl->z2 += q15_mul(ctrl->h, ctrl->z3 - q15_mul(ctrl->beta02, fe) +
q15_mul(ctrl->b0, ctrl->u));
ctrl->z3 += q15_mul(ctrl->h, -q15_mul(ctrl->beta03, fe));
// NLSEF控制律
q15_t e1 = ctrl->v1 - ctrl->z1;
q15_t e2 = ctrl->v2 - ctrl->z2;
ctrl->u0 = q15_add(q15_mul(FLOAT_TO_Q15(0.8), fal(e1,...)),
q15_mul(FLOAT_TO_Q15(1.2), fal(e2,...)));
}
```
# 四、注意事项
1. **精度控制**:Q值越大精度越高,但动态范围越小
2. **溢出防护**:
- 乘法运算使用32位中间变量
- 加法前进行饱和判断
3. **函数近似**:
```c
q15_t fal(q15_t e, q15_t alpha, q15_t delta) {
q15_t abs_e = abs(e);
if(abs_e > delta) {
return q15_mul(pow_q15(abs_e, alpha), sign(e));
}
return q15_div(e, pow_q15(delta, Q15_ONE - alpha));
}
```
4. **参数整定**:先完成浮点版本验证,再转换定点参数
# 五、验证方法
1. 白盒测试:对比定点与浮点计算的中间结果
2. 时域测试:阶跃响应、抗扰动的时域波形对比
3. 量化误差分析:统计最大误差和平均误差
> 建议:首次实现时保持浮点与定点版本并存,通过宏定义切换计算模式,便于调试和性能对比。
阅读全文
相关推荐


















