无人机中的PID控制代码略解

本文深入探讨了无人机中的PID控制原理,包括比例、积分、微分环节在时域和频域的表示,并展示了简单的C语言代码实现。此外,详细介绍了PX4飞行控制系统中的姿态控制,特别是内环角速度PID控制的实现,包括积分项的饱和限制和前馈控制的作用,以提高系统的稳定性和响应速度。
摘要由CSDN通过智能技术生成

无人机中的PID控制代码略解

  • PID的控制规律:

参考:Amov实验室-PX4中级课程-PID基础

频域函数:
u ( s ) e r r ( s ) = k p + k i s + k d s \frac{u(s)}{err(s)}=k_p+\frac{k_i}{s}+k_ds err(s)u(s)=kp+ski+kds
写成时域的函数:
u ( t ) = k p ( e r r ( t ) + 1 T i ∫ e r r ( t ) d t + T d d e r r ( t ) d t ) (1) u(t)=k_p(err(t)+\frac{1}{T_i} \int err(t)dt+T_d\frac {derr(t)}{dt}) \tag 1 u(t)=kp(err(t)+Ti1err(t)dt+Tddtderr(t))(1)

假设采样间隔为T,则在第K个T时刻,有:
e r r ( K ) = R i n ( K ) − C o u t ( K ) err(K) = R_{in}(K)-C_{out}(K) err(K)=Rin(K)Cout(K)

对于积分环节,按照定义则有:
I ( K ) = T ∗ ( e r r ( 0 ) + e r r ( 1 ) + ⋯ + e r r ( K ) ) = T ∑ i = 0 K e r r ( i ) (2) I(K)=T*(err(0)+err(1)+\cdots + err(K))=T\sum_{i=0}^{K}err(i) \tag 2 I(K)=T(err(0)+err(1)++err(K))=Ti=0Kerr(i)(2)
对于微分环节,按照定义则有:
D ( K ) = e r r ( K ) − e r r ( K − 1 ) T (3) D(K) = \frac{err(K)-err(K-1)}{T} \tag3 D(K)=Terr(K)err(K1)(3)
( 2 ) , ( 3 ) (2),(3) (2),(3)代入回 ( 1 ) (1) (1)则有:
u ( K ) = k p [ e r r ( K ) + T T i ∑ i = 0 K e r r ( i ) + T d e r r ( K ) − e r r ( K − 1 ) T ] u(K)=k_p[err(K)+\frac{T}{T_i}\sum_{i=0}^{K}err(i)+T_d\frac{err(K)-err(K-1)}{T}] u(K)=kp[err(K)+TiTi=0Kerr(i)+TdTerr(K)err(K1)]
化简一下则有:
u ( K ) = k p e r r ( K ) + k i ∑ i = 0 K e r r ( i ) + k d [ e r r ( K ) − e r r ( K − 1 ) ] (4) u(K)=k_perr(K)+k_i\sum_{i=0}^{K}err(i)+k_d[err(K)-err(K-1)] \tag4 u(K)=kperr(K)+kii=0Kerr(i)+kd[err(K)err(K1)](4)
为了减小存储空间,还有增量式PID,可写为:
Δ u ( K ) = k p [ e r r ( K ) − e r r ( K − 1 ) ] + k i ⋅ e r r ( K ) + k d [ e r r ( K ) − 2 e r r ( K − 1 ) + e r r ( K − 2 ) ] \Delta u(K)=k_p[err(K)-err(K-1)]+k_i \cdot err(K)+k_d[err(K)-2err(K-1)+err(K-2)] Δu(K)=kp[err(K)err(K1)]+kierr(K)+kd[err(K)2err(K1)+err(K2)]
u ( K ) u(K) u(K)还可以写为:
u ( K ) = u ( K − 1 ) + Δ u ( K ) u(K)=u(K-1)+\Delta u(K) u(K)=u(K1)+Δu(K)

  • PID的C语言代码初步实现:
//定义pid结构体,定义主要参数
struct _pid{
    float SetSpeed;
    float ActualSpeed;
    float err;
    float err_last;	//err(K-1)
    float Kp,Ki,Kd;
    float voltage;//Object Signal
    float integral;

    float err_llast;//err(K-2)
}pid;

//初始化pid模块
void PID_init(){
    pid.SetSpeed = 0;
    pid.ActualSpeed=0.0;
    pid.err = 0;
    pid.err_last = 0;
    pid.voltage = 0;
    pid.integral = 0;
    //Set Each PID Gain
    pid.Kp = 0.4;
    pid.Ki = 0.3;
    pid.Kd = 0.2;

    pid.err_llast = 0;
}

//pid实现,包括增量形式的pid,输入为目标速度,输出为当前的速度
float PID_realize(float speed){
    pid.SetSpeed = speed;
    pid.err = pid.SetSpeed - pid.ActualSpeed;
    
    //PID Calculate
    pid.voltage = pid.Kp*pid.err + \
                pid.Ki*pid.integral + \
                pid.Kd*(pid.err-pid.err_last);
    
    //Increment Type
/*  
	float incrementVoltage;
    incrementVoltage = pid.Kp*(pid.err-pid.err_last)+\
                    pid.Ki*pid.err+\
                    pid.Kd*(pid.err-2*pid.err_last+pid.err_llast);

    pid.voltage += incrementVoltage;
    pid.err_llast = pid.err_last;
*/    
    //Update Parameters
    pid.err_last = pid.err;
    pid.ActualSpeed = pid.voltage*1.0;
    return pid.ActualSpeed;
}

  • PX4中姿态控制介绍:

PX4中的控制主要分为***姿态控制*** 和***位置控制***,这两者都采用***串级 P I D PID PID*** 控制,这样的好处是可以增加系统的稳定性,抗干扰,因为参数更多了,直观上自然能够适应的情况就相对来说多一点。位置控制中外环是位置,内环是速度,由内环得到的速度积分就是位置的改变量。而姿态控制中,外环是角度差,内环是角速度差,这是因为角度的变化同时也是由角速度过渡来的。参考:Pixhawk-串级pid介绍

因为时间关系,我下面介绍一下内环角速度PID控制的实现:

  1. 首先是角速度PID环控制器的类,成员函数主要由:设置初始PID增益,设置积分器的最大绝对值,设置前馈增益,饱和状态(和Mixer有关),重设积分项,获取状态信息,积分项特别调控以及PID最终的计算结果实现。不要看其这么复杂,其实主要的函数就是最后两个,而这两个函数也只是在我之前介绍的简易PID实现的基础上加了一点点工业上的限制。
class RateControl
{
public:
	RateControl() = default;
	~RateControl() = default;

	/**
	 * Set the rate control gains
	 * @param P 3D vector of proportional gains for body x,y,z axis
	 * @param I 3D vector of integral gains
	 * @param D 3D vector of derivative gains
	 */
	void setGains(const matrix::Vector3f &P, const matrix::Vector3f &I, const matrix::Vector3f &D);

	/**
	 * Set the mximum absolute value of the integrator for all axes
	 * @param integrator_limit limit value for all axes x, y, z
	 */
	void setIntegratorLimit(const matrix::Vector3f &integrator_limit) { _lim_int = integrator_limit; };

	/**
	 * Set direct rate to torque feed forward gain
	 * @see _gain_ff
	 * @param FF 3D vector of feed forward gains for body x,y,z axis
	 */
	void setFeedForwardGain(const matrix::Vector3f &FF) { _gain_ff = FF; };

	/**
	 * Set saturation status
	 * @param status message from mixer reporting about saturation
	 */
	void setSaturationStatus(const MultirotorMixer::saturation_status &status);

	/**
	 * Run one control loop cycle calculation
	 * @param rate estimation of the current vehicle angular rate
	 * @param rate_sp desired vehicle angular rate setpoint
	 * @param dt desired vehicle angular rate setpoint
	 * @return [-1,1] normalized torque vector to apply to the vehicle
	 */
	matrix::Vector3f update(const matrix::Vector3f &rate, const matrix::Vector3f &rate_sp,
				const matrix::Vector3f &angular_accel, const float dt, const bool landed);

	/**
	 * Set the integral term to 0 to prevent windup
	 * @see _rate_int
	 */
	void resetIntegral() { _rate_int.zero(); }

	/**
	 * Get status message of controller for logging/debugging
	 * @param rate_ctrl_status status message to fill with internal states
	 */
	void getRateControlStatus(rate_ctrl_status_s &rate_ctrl_status);

private:
	void updateIntegral(matrix::Vector3f &rate_error, const float dt);

	// Gains
	matrix::Vector3f _gain_p; ///< rate control proportional gain for all axes x, y, z
	matrix::Vector3f _gain_i; ///< rate control integral gain
	matrix::Vector3f _gain_d; ///< rate control derivative gain
	matrix::Vector3f _lim_int; ///< integrator term maximum absolute value
	matrix::Vector3f _gain_ff; ///< direct rate to torque feed forward gain only useful for helicopters

	// States
	matrix::Vector3f _rate_int; ///< integral term of the rate controller

	bool _mixer_saturation_positive[3] {};
	bool _mixer_saturation_negative[3] {};
};
  1. 说一下积分项特别调控和PID的计算输出两个模块:

积分项特别调控:

void RateControl::updateIntegral(Vector3f &rate_error, const float dt)
{
	for (int i = 0; i < 3; i++) {
		// prevent further positive control saturation
		if (_mixer_saturation_positive[i]) {
			rate_error(i) = math::min(rate_error(i), 0.f);
		}

		// prevent further negative control saturation
		if (_mixer_saturation_negative[i]) {
			rate_error(i) = math::max(rate_error(i), 0.f);
		}

		// I term factor: reduce the I gain with increasing rate error.
		// This counteracts a non-linear effect where the integral builds up quickly upon a large setpoint
		// change (noticeable in a bounce-back effect after a flip).
		// The formula leads to a gradual decrease w/o steps, while only affecting the cases where it should:
		// with the parameter set to 400 degrees, up to 100 deg rate error, i_factor is almost 1 (having no effect),
		// and up to 200 deg error leads to <25% reduction of I.
		float i_factor = rate_error(i) / math::radians(400.f);
		i_factor = math::max(0.0f, 1.f - i_factor * i_factor);

		// Perform the integration using a first order method
		float rate_i = _rate_int(i) + i_factor * _gain_i(i) * rate_error(i) * dt;

		// do not propagate the result if out of range or invalid
		if (PX4_ISFINITE(rate_i)) {
			_rate_int(i) = math::constrain(rate_i, -_lim_int(i), _lim_int(i));
		}
	}
}

首先是积分饱和的限制:

		// prevent further positive control saturation	
		if (_mixer_saturation_positive[i]) {
			rate_error(i) = math::min(rate_error(i), 0.f);
		}

		// prevent further negative control saturation
		if (_mixer_saturation_negative[i]) {
			rate_error(i) = math::max(rate_error(i), 0.f);
		}

在实际过程中,如果系统一直存在一个方向的偏差,PID的输出因为积分项的存在可能会变得非常大,但是执行器的输入是有限的。当执行器的输入达到上限,PID的输出持续增大,此时就进入了饱和区,这主要是由于积分项饱和造成的。一旦系统出现了反向的偏差,此时积分项就要逐渐减小,但是由于进入了饱和区,不可能一下从饱和区中退出来,使得执行机构的输入一直在极限状态,无法立刻减小,进入饱和区时间越长,越难退出来,这就导致了像是失控一样现象。所以为了抑制这种现象,必须让积分项在PID的输出达到执行器饱和的时候停止让PID输出的绝对值继续增加,包括正向和负向,这就有了上面这两句函数。

其次注意这个函数中以下两句函数:

		float i_factor = rate_error(i) / math::radians(400.f);
		i_factor = math::max(0.0f, 1.f - i_factor * i_factor);
		float rate_i = _rate_int(i) + i_factor * _gain_i(i) * rate_error(i) * dt;

这个对于积分项的计算可以看成如下的公式:
R a t e I n t e g r a l ( K ) = ∑ i = 0 K − 1 e r r ( i ) + i f a c t o r ∗ k i ∗ e r r ( K ) ∗ d t RateIntegral(K)=\sum_{i=0}^{K-1}err(i)+i_{factor}*k_i*err(K)*dt RateIntegral(K)=i=0K1err(i)+ifactorkierr(K)dt
其中, i f a c t o r i_{factor} ifactor的计算如下:
i f a c t o r = 1 − ( e r r 40 0 ∘ ) 2 i_{factor} = 1-(\frac{err}{400^\circ})^2 ifactor=1(400err)2
整个函数限制了积分增益不能太大,当期望的角速度误差在 20 0 ∘ 200^\circ 200的时候,就可以看到积分增益减少到25%了,而当角速度误差超过 40 0 ∘ 400^\circ 400的时候,此时积分的增益就会完全为0。这样做是为了防止积分项导致PID的输出非常巨大,产生超调,甚至引起积分饱和。

PID最终的计算:

Vector3f RateControl::update(const Vector3f &rate, const Vector3f &rate_sp, const Vector3f &angular_accel,
			     const float dt, const bool landed)
{
	// angular rates error
	Vector3f rate_error = rate_sp - rate;

	// PID control with feed forward
	// 带有前馈的PID控制
    // torque=p*rate_error(比例项)+_rate_int(积分项)-d*angular_accel(微分项)+ff*rate_sp(前馈)
	const Vector3f torque = _gain_p.emult(rate_error) + _rate_int - _gain_d.emult(angular_accel) + _gain_ff.emult(rate_sp);

	// update integral only if we are not landed
	if (!landed) {
		updateIntegral(rate_error, dt);
	}

	return torque;
}

这里大家可能会有疑惑,为什么要加一个前馈的控制呢?实际上这和积分项的滞后有关,加入前馈调节可以使得系统的控制速度更快,提高系统的响应速度:参考:多旋翼姿态控制中前馈的作用

加入前馈矫正的串级PID系统框图

可以看到,其中在外环已经产生一个角速度期望控制了,可是由于内环积分器的滞后(分析积分环节的频域响应可知,中高频段的相角滞后严重),前馈的作用是跳过积分器,直接从输入到输出,以提高系统的响应速度

设PID控制环节的传递函数 G c ( s ) = K p + K i s + K d s G_c(s) = K_p+\frac{K_i}{s}+K_ds Gc(s)=Kp+sKi+Kds

则有不加前馈的开环传递函数(从 θ d \theta_d θd θ \theta θ):
G 1 ( s ) = P G c J s 2 + G c s G_1(s)=\frac{PG_c}{Js^2+G_cs} G1(s)=Js2+GcsPGc
加了前馈的开环传递函数为:
G 1 ( s ) = P K + P G c J s 2 + G c s G_1(s)=\frac{PK+PG_c}{Js^2+G_cs} G1(s)=Js2+GcsPK+PGc
假如我们简略一点,把 G c G_c Gc看成常数,也就是说只有比例增益,至少很容易看出此时这个二阶系统的增益变大了,这样的话系统的响应速度变快,稳态误差减小;同时系统的自然频率变大,阻尼比不变,相应的系统的闭环带宽增加,系统能够响应更多的高频分量使得控制系统的快速性提高。 G c G_c Gc为一般的情况下,也有类似的分析,主要就是提高了截止频率,使得带宽增加,相角裕度减小,超调量增加,系统快速性增强。

四旋翼无人机的模糊PID控制器是一种常用的方法,用于精确地控制飞行器的姿态。PID(比例-积分-微分)控制器包含三个部分:比例、积分和微分。 模糊逻辑控制器通常基于输入(如姿态偏差、速度等)和输出(调整电机转速)的模糊规则集。以下是模糊PID控制器的基本结构: 1. **模糊化**(Fuzzification):将连续的输入值转化为模糊集合,比如使用三角形、菱形或多边形的隶属函数。 2. **推理**(Inference):根据模糊规则库确定每个输入的模糊集,并计算它们之间的关联度,这涉及到模糊推理和模糊集合的操作。 3. **PID处理**:将模糊集的结果转换到PID控制器的标准输出范围内,即电机的PWM信号。比例项直接映射误差,积分项考虑了累积误差的影响,微分项则关注的是误差的变化率。 4. **解模糊化**(Defuzzification):把模糊控制信号转换回具体的数值,例如通过心平均法或最大隶属度原则。 由于涉及较多的技术细节和代码实现复杂性,这里提供一个简化的伪代码示例: ```python class FuzzyPIDController: def __init__(self, kp, ki, kd): self.p = PID(kp, 0, 0) # 比例控制部分 self.i = PID(0, ki, 0) # 积分控制部分 self.d = PID(0, 0, kd) # 微分控制部分 def update(self, error, last_error, dt): p_output = self.p.update(error) i_output = self.i.update(error, last_error, dt) d_output = self.d.update(error - last_error, dt) output = p_output + i_output + d_output return output ``` 实际编写这样的代码需要深入理解模糊逻辑和PID算法,以及如何将其应用到无人机的具体硬件上。如果你需要详细了解某个特定编程语言的实现,可以继续提问:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值