STM32实现水下四旋翼(十一)飞行控制任务3——定深控制

26 篇文章 35 订阅

一. 定深控制原理

前面我们讲过了姿态角的串级PID控制,那么现在来理解定深控制是非常好理解了。他们都是位置控制,这里的深度控制我只用了单级的PID控制器,显得会更简单。期望深度来源于遥控器摇杆的积分(在定深模式下油门通道变为上下运动的速度值),反馈深度来自于水深传感器的数据。控制原理为:
在这里插入图片描述这个图上一章就出现了,这里我们增加深度控制的位置环,只不过我没有加入Z轴的速度环,实验证明基本能实现定深,当然了响应不是很快,后续可以改进算法。

定深控制时需要设置一个定深油门的基准值,在这个值下,四旋翼浮力与重力基本平衡,从而基本能够悬浮,深度环的输出叠加在这个油门基础值上,就可以使其上下机动。这个基准值可以人为去调参,如同PID参数一样,也可以通过算法去计算。方法就是每次启动定深控制后实时检测油门值与设定的油门基准值的差值,一段时间后四旋翼会大致悬浮(即使设定的油门基准值偏差很大),如果检测到差值很大,说明当前设定的油门基值不对,就可以将当前的实际油门值设定为新的油门基准值。这样的好处就是一旦水下四旋翼由于负载或更换部件导致重量变了,就不需要认为去调参了。

二. 姿态控制任务增加定深控制功能

建立一个position_pid.h和position_pid.c文件,专门处理位置控制的问题。大家不要嫌弃我建了这么多文件哈,请养成这个习惯,将不同的功能放在不同的文件里,代码的可读性会非常强。

position_pid.h文件:

#ifndef __POSITION_PID_H
#define __POSITION_PID_H
#include "sys.h"
#include "stabilizer.h"
#include "pid.h"

#define POS_UPDATE_RATE 		200
#define POS_UPDATE_DT 			(1.0f / POS_UPDATE_RATE)
#define PID_DEPTH_INTEGRATION_LIMIT 10000.0

typedef struct  
{
	PidObject pidVX;
	PidObject pidVY;
	PidObject pidVZ;
	float thrustBase; 			// 定深时的油门基准值,这个值可以让四轴悬停
	bool preMode;				// 前一次的模式,为true时对应定深模式
	bool isAltHoldMode;         // 为true时对应定深模式
}posPid_t;

void positionControlInit(void);
void positionResetAllPID(void);
void depthPID(float *actualDepth, float *desiredDepth, control_t *output);
void getPositionPIDZ(float* kp, float* ki, float* kd);
void setPositionPIDZ(float kp, float ki, float kd);

extern posPid_t posPid;

#endif

position_pid.c文件:

#include "position_pid.h"
#include "stabilizer.h"
#include <math.h>
#include "pid.h"
#include "pwm_control.h"

//#define THRUST_SCALE	(5.0f)
#define THRUST_SCALE	(1.0f)
#define START_DEPTH     (0.0f)
#define THRUSTBASE_HIGH	(10000.f)
#define THRUSTBASE_LOW	(-10000.f)

posPid_t posPid;


/*基础油门值限制*/
float limitThrustBase(float input)
{
	if(input > THRUSTBASE_HIGH)
		return THRUSTBASE_HIGH;
	else if(input < THRUSTBASE_LOW)
		return THRUSTBASE_LOW;
	else 
		return input;
}

void positionControlInit(void)
{
	pidInit(&posPid.pidVZ, 0, configParam.pidPos.vz, POS_UPDATE_DT);           /*vz PID初始化*/
	pidSetIntegralLimit(&posPid.pidVZ, PID_DEPTH_INTEGRATION_LIMIT);		   /*roll  角度积分限幅设置*/
	pidSetOutLimit(&posPid.pidVZ, PID_DEPTH_INTEGRATION_LIMIT);
	positionResetAllPID();
	posPid.thrustBase = limitThrustBase(configParam.thrustBase);		// 每次初始化重新给定油门基值
}

 深度环PID
void depthPID(float *actualDepth, float *desiredDepth, control_t *output)
{
	float PIDoutThrust;
	float depthError = *desiredDepth - *actualDepth;
	PIDoutThrust = THRUST_SCALE * pidUpdate(&posPid.pidVZ,depthError);

	if (posPid.isAltHoldMode == true)		// 在定高模式下检测机体的重量,自动调整定高油门基准值
	{
		//detectWeight(PIDoutThrust);			// 检测重量,更新油门值
	}

	// thrustBase是负值
	output->thrust = limitThrust(posPid.thrustBase - PIDoutThrust);		// z轴向下为正,油门值向上为正
}

void positionResetAllPID(void)
{
	//pidReset(&posPid.pidVX);
	//pidReset(&posPid.pidVY);
	pidReset(&posPid.pidVZ);
}

void getPositionPIDZ(float* kp, float* ki, float* kd)
{
	*kp = posPid.pidVZ.kp;
	*ki = posPid.pidVZ.ki;
	*kd = posPid.pidVZ.kd ;
}

void setPositionPIDZ(float kp, float ki, float kd)
{
	posPid.pidVZ.kp = kp;
	posPid.pidVZ.ki = ki;
	posPid.pidVZ.kd = kd;
}

void depthPID(float *actualDepth, float *desiredDepth, control_t *output)实现了深度环的计算,计算结果叠加到油门基准值,作为输出。里面有一个detectWeight(PIDoutThrust)就是实现自动检测重量并更新油门值的哈,这里我注释掉了,因为项目后期我没有时间调试这个功能了。虽然已经写了,但是没有通过时间检验,没有经过检验的东西我不放上来。

好的,到这里我们实现了定深航行的功能了,现在我们需要更新void Water_Attitude_Control(control_t *output)函数,之前我们只在里面写了手动模式的功能,现在加入定深模式。

void Water_Attitude_Control(control_t *output)
{
    float turn_speed;
    float z_speed;
    float forward_speed;

/*************************** 针对 手动模式和定高模式 以及 模式切换做预处理 ***********************************/
    // 手动模式下油门通道转化为油门基准值
    if (command[CARRY_MODE] == HAND_MODE)
    {
        posPid.isAltHoldMode = false;
    }
    // 定高(定深)时油门保持在设定的基准值,这个基准值刚好让机器人悬浮,油门通道此时转化为 z 轴速度
    else if (command[CARRY_MODE] == DEPTH_MODE)
    {
        posPid.isAltHoldMode = true;
    }
    // 由手动模式切换到定深模式 // 由定深模式切换到手动模式
    if ((posPid.isAltHoldMode == true && posPid.preMode == false) ||
        (posPid.isAltHoldMode == false && posPid.preMode == true))
    {
        positionResetAllPID();
        control.depthOut = 0; // 深度环PID置0
    }
    posPid.preMode = posPid.isAltHoldMode; // 当前模式变为pre模式

    turn_speed = pwm2Range(command[YAW], -1000.0f, 1000.0f);
    if (turn_speed < 30.0f && turn_speed > -30.0f)
        turn_speed = 0.f; // 死区
    z_speed = pwm2Range(command[THROTTLE], -1000.0f, 1000.0f);
    if (z_speed < 30.0f && z_speed > -30.0f)
        z_speed = 0.f; // 死区
/*************************** 针对 手动模式和定高模式 以及 模式切换做预处理 ***********************************/

/*************************************** 定高模式下控制 ************************************************/
    // 高度环 PID 计算并作用到油门值,调整直到达到悬浮状态
    if (posPid.isAltHoldMode == true)
    {
        // 不使能时,电机锁定
        if (command[ALTHOLD_ENABLE] == HOLD_DISABLE) // 定高禁止,不运动,复位所有PID
        {
            attitudeResetAllPID(); //PID复位
            positionResetAllPID();
            setstate.expectedAngle.yaw = state.realAngle.yaw;
            setstate.expectedAngle.roll = state.realAngle.roll;
            setstate.expectedAngle.pitch = state.realAngle.pitch;
            setstate.expectedDepth = state.realDepth;
            control.thrust = 0;
            control.yaw = 0;
            control.roll = 0;
            control.pitch = 0;
            control.depthOut = 0; // 深度环PID置0
        }
        // 使能时 开始定高控制
        else if (command[ALTHOLD_ENABLE] == HOLD_ENABLE)
        {
            setstate.expectedAngle.roll = pwm2Range(command[ROLL], -30.0f, 30.0f);
            if (setstate.expectedAngle.roll < 0.9f && setstate.expectedAngle.roll > -0.9f)
                setstate.expectedAngle.roll = 0.f; // 摇杆死区
            setstate.expectedAngle.pitch = pwm2Range(command[PITCH], -30.0f, 30.0f);
            if (setstate.expectedAngle.pitch < 0.9f && setstate.expectedAngle.pitch > -0.9f)
                setstate.expectedAngle.pitch = 0.f; //遥感死区

            setstate.expectedAngle.yaw -= turn_speed * zoom_factor_yaw * ft;
            if (setstate.expectedAngle.yaw > 180.0f)
                setstate.expectedAngle.yaw -= 360.0f;
            if (setstate.expectedAngle.yaw < -180.0f)
                setstate.expectedAngle.yaw += 360.0f;
            setstate.expectedDepth -= z_speed * zoom_factor_vz * ft; // 向上调整时期望深度减小
            depthPID(&state.realDepth, &setstate.expectedDepth, &control);
            attitudeAnglePID(&state.realAngle, &setstate.expectedAngle, &setstate.expectedRate); /* 角度环PID */
            attitudeRatePID(&state.realRate, &setstate.expectedRate, &control);                  /* 角速度环PID */
        }
    }
/*************************************** 定高模式下控制 ************************************************/

/******************************************* 手动模式下控制 ********************************************/
    if (posPid.isAltHoldMode == false)
    {
        // 手动模式下 油门通道为0时不运动,复位所有 姿态pid与pid输出
        control.thrust = pwm2thrust(command[THROTTLE]);
        setstate.expectedDepth = state.realDepth;          // 手动模式下期望高度始终等于当前高度/
                                                           // 切换到定高时从当前高度开始定高
        if (control.thrust < 200 && control.thrust > -200) 
            control.thrust = 0;                            // 油门死区

        if ((int)control.thrust == 0 && (int)turn_speed == 0) // 油门和方向摇杆都居中,机器人不使能
        {
            attitudeResetAllPID(); //PID复位
            setstate.expectedAngle.yaw = state.realAngle.yaw;
            setstate.expectedAngle.roll = state.realAngle.roll;
            setstate.expectedAngle.pitch = state.realAngle.pitch;
            control.yaw = 0;
            control.roll = 0;
            control.pitch = 0;
        }
        else // 油门有输出,从遥控器获得期望值,姿态PID		// 或者油门没输出,原地转圈
        {
            setstate.expectedAngle.roll = pwm2Range(command[ROLL], -30.0f, 30.0f);
            if (setstate.expectedAngle.roll < 0.9f && setstate.expectedAngle.roll > -0.9f)
                setstate.expectedAngle.roll = 0.f; // 摇杆死区
            setstate.expectedAngle.pitch = pwm2Range(command[PITCH], -30.0f, 30.0f);
            if (setstate.expectedAngle.pitch < 0.9f && setstate.expectedAngle.pitch > -0.9f)
                setstate.expectedAngle.pitch = 0.f; //遥感死区	
            setstate.expectedAngle.yaw -= turn_speed * zoom_factor_yaw * ft;
            if (setstate.expectedAngle.yaw > 180.0f)
                setstate.expectedAngle.yaw -= 360.0f;
            if (setstate.expectedAngle.yaw < -180.0f)
                setstate.expectedAngle.yaw += 360.0f;
            attitudeAnglePID(&state.realAngle, &setstate.expectedAngle, &setstate.expectedRate); /* 角度环PID */
            attitudeRatePID(&state.realRate, &setstate.expectedRate, &control);                  /* 角速度环PID */
        }
    }
/******************************************* 手动模式下控制 ********************************************/
}

好的,现在加入了定深控制的模式,可以通过遥控器的一个拨码开关进行模式切换。在main函数中不需要进行更改了,因为姿态控制任务里面是调用的void Water_Attitude_Control(control_t *output)。这就是封装的好处啦。

到这里控制任务就结束了,下一讲开始进行上位机的开发,当然了上位机开发需要下位机制定相同的通信协议,后面继续。

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为其然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值