STM32实现四驱小车(四)姿态控制任务——偏航角串级PID控制算法

2023-05-16

目录

  • 一. 绪论
  • 二. 角度环串级PID原理
    • 1. PID基本算法
    • 2. 姿态角串级PID原理
  • 三. 如何用STM32实现角度-角速度的串级PID控制
    • 1. PID算法的代码实现
    • 2. 串级PID算法的代码实现
  • 四. UCOS-III姿态控制任务的实现

一. 绪论

这一部分是核心内容,讲解姿态角的串级PID控制。在智能小车、四旋翼、四足狗子等等一系列机器人的控制系统中,姿态控制(俯仰角、滚转角、偏航角)都是核心内容,它决定了小车开得直不直,飞机飞得稳不稳。虽然现在先进的、智能的控制算法有很多,如自适应控制、神经网络控制、模糊控制等在机器人控制系统的设计上有了很多应用,但是最常用的最好用的依然是PID控制器,搞通了PID控制器就能够应付绝大多数场合了。

本文续接上一篇STM32实现四驱小车(三)传感任务——姿态角解算。

二. 角度环串级PID原理

1. PID基本算法

PID控制器的原理图如图所示。
在这里插入图片描述PID控制器是一种线性控制器,根据给定值和实际输出值的偏差构成控制偏差
e ( t ) = y d ( t ) − y ( t ) e(t)={{y}_{d}}(t)-y(t) e(t)=yd(t)y(t)
PID的控制率为
u ( t ) = k p [ e ( t ) + 1 T I ∫ 0 t e ( t ) d t + T D d e ( t ) d t ] u(t)={{k}_{p}}\left[ e(t)+\frac{1}{{{T}_{I}}}\int_{0}^{t}{e(t)dt+{{T}_{D}}\frac{de(t)}{dt}} \right] u(t)=kp[e(t)+TI10te(t)dt+TDdtde(t)]
其中, k p k_p kp为比例系数, T I T_I TI为积分时间常数, T D T_D TD为微分时间常数。PID控制器各校正环节的作用为:
(1)比例环节:成比例的反应控制系统的偏差信号e(t),偏差一旦产生,控制器立即产生控制作用,以减少偏差。但是比例环节不能消除稳态误差。
(2)积分环节:主要是消除静差,提高系统的无差度。积分作用的强弱取决于积分时间常数 T I T_I TI T I T_I TI越大,积分作用越弱,反之则越强。
(3)微分环节:反映偏差信号的变化趋势(变化速率),并能在偏差信号变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减少调节时间。

如何调节PID参数是实现PID控制器的核心内容,以笔者的经验,比例环节是起主要调节作用的,从小到大逐渐调整,直到系统有发散的趋势,然后往回取一个适中的值;积分环节的作用是消除误差,确定了比例系数后,从小到大增大积分系数(减少积分时间常数),直到系统有发散的趋势,积分环节不需要取得很大,记住它的作用是消除误差。微分环节的作用是超前校正,但是在噪声较大的情况下会放大噪声,引起系统不稳定,所以对于延迟没有太高要求的场合可以不加微分环节。

在实际中我们都是用的离散系统,所以我们关心数字PID控制的实现。在应用中一般有位置式PID控制增量式PID控制

位置式PID的算法为:
u ( k ) = k p e ( k ) + k i ∑ j = 0 k e ( j ) T + k d e ( k ) − e ( k − 1 ) T u(k)={{k}_{p}}e(k)+{{k}_{i}}\sum\limits_{j=0}^{k}{e(j)}T+{{k}_{d}}\frac{e(k)-e(k-1)}{T} u(k)=kpe(k)+kij=0ke(j)T+kdTe(k)e(k1)
式中,T为采样周期,也就是单片机的控制周期。k为采样序列,e(k)和e(k-1)分别是第k次和第k-1次所得的偏差信号。

当执行机构需要的是控制量的增量时(例如驱动步进电机),应该采用增强式PID控制。由位置式PID的算法:
u ( k − 1 ) = k p e ( k − 1 ) + k i ∑ j = 0 k − 1 e ( j ) T + k d e ( k − 1 ) − e ( k − 2 ) T u(k-1)={{k}_{p}}e(k-1)+{{k}_{i}}\sum\limits_{j=0}^{k-1}{e(j)}T+{{k}_{d}}\frac{e(k-1)-e(k-2)}{T} u(k1)=kpe(k1)+kij=0k1e(j)T+kdTe(k1)e(k2)
得到增量式PID算法为:
Δ u ( k ) = u ( k ) − u ( k − 1 ) = k p [ e ( k ) − e ( k − 1 ) ] + k i e ( k ) + k d [ e ( k ) − 2 e ( k − 1 ) + e ( k − 2 ) ] \Delta u(k)=u(k)-u(k-1)=k_{p}[e(k)-e(k-1)]+k_{i} e(k)+k_{d}[e(k)-2 e(k-1)+e(k-2)] Δu(k)=u(k)u(k1)=kp[e(k)e(k1)]+kie(k)+kd[e(k)2e(k1)+e(k2)]

2. 姿态角串级PID原理

对于姿态角的控制,我们希望给定姿态角机器人能够跟随给定的输入,其实这就是一个位置跟踪问题。按照单级PID的思路应该是这样的:
在这里插入图片描述但是这里不用这种方式,而是采用串级PID,也就是一个PID套一个PID,外面是角度环,里面是角速度环。这样做的好处是增加了控制系统的响应速度和稳态精度,具体的原理大家可以去找文章专门研究,这里不过多讲解。
在这里插入图片描述

三. 如何用STM32实现角度-角速度的串级PID控制

1. PID算法的代码实现

原理弄明白之后其实实现起来很简单,PID的控制算法是通用的,完全可以移植,只要调整三个系数以适应自己做的东西就可以了,这里我们一起写一下,建立一个pid.h和一个pid.c文件,添加到工程中。
pid.h的内容如下,定义PID的结构体和一些数据结构、声明函数。

#ifndef __PID_H
#define __PID_H
#include "sys.h"
#include "stdbool.h"

typedef struct
{
	float kp;
	float ki;
	float kd;
} pidInit_t;

typedef struct
{
	pidInit_t roll;
	pidInit_t pitch;
	pidInit_t yaw;
} pidParam_t;

typedef struct
{
	pidInit_t vx;
	pidInit_t vy;
	pidInit_t vz;
} pidParamPos_t;

typedef struct
{
	pidParam_t pidAngle;  /*角度PID*/
	pidParam_t pidRate;	  /*角速度PID*/
	pidParamPos_t pidPos; /*位置PID*/
	float thrustBase;		  /*油门基础值*/
	u8 cksum;
} configParam_t;

typedef struct
{
	float desired;	 //< set point
	float error;	 //< error
	float prevError; //< previous error
	float integ;	 //< integral
	float deriv;	 //< derivative
	float kp;		 //< proportional gain
	float ki;		 //< integral gain
	float kd;		 //< derivative gain
	float outP;		 //< proportional output (debugging)
	float outI;		 //< integral output (debugging)
	float outD;		 //< derivative output (debugging)
	float iLimit;	 //< integral limit
	float iLimitLow; //< integral limit
	float maxOutput;
	float dt; //< delta-time dt
} PidObject;

/*pid结构体初始化*/
void pidInit(PidObject *pid, const float desired, const pidInit_t pidParam, const float dt);
void pidParaInit(PidObject *pid, float maxOutput, float iLimit, const pidInit_t pidParam);
void pidSetIntegralLimit(PidObject *pid, const float limit); /*pid积分限幅设置*/
void pidSetOutLimit(PidObject *pid, const float maxoutput);	 /*pid输出限幅设置*/
void pidSetDesired(PidObject *pid, const float desired);	 /*pid设置期望值*/
float pidUpdate(PidObject *pid, const float error);			 /*pid更新*/
float pidGetDesired(PidObject *pid);						 /*pid获取期望值*/
bool pidIsActive(PidObject *pid);							 /*pid状态*/
void pidReset(PidObject *pid);								 /*pid结构体复位*/
void pidSetError(PidObject *pid, const float error);		 /*pid偏差设置*/
void pidSetKp(PidObject *pid, const float kp);				 /*pid Kp设置*/
void pidSetKi(PidObject *pid, const float ki);				 /*pid Ki设置*/
void pidSetKd(PidObject *pid, const float kd);				 /*pid Kd设置*/
void pidSetPID(PidObject *pid, const float kp, const float ki, const float kd);
void pidSetDt(PidObject *pid, const float dt); /*pid dt设置*/

#endif /* __PID_H */

pid.c当中实现函数:

#include <stdbool.h>
#include "pid.h"

void abs_outlimit(float *a, float ABS_MAX){
    if(*a > ABS_MAX)
        *a = ABS_MAX;
    if(*a < -ABS_MAX)
        *a = -ABS_MAX;
}

void pidInit(PidObject* pid, const float desired, const pidInit_t pidParam, const float dt)
{
	pid->error     = 0;
	pid->prevError = 0;
	pid->integ     = 0;
	pid->deriv     = 0;
	pid->desired = desired;
	pid->kp = pidParam.kp;
	pid->ki = pidParam.ki;
	pid->kd = pidParam.kd;
	pid->iLimit    = DEFAULT_PID_INTEGRATION_LIMIT;
	pid->iLimitLow = -DEFAULT_PID_INTEGRATION_LIMIT;
	pid->dt        = dt;
}

float pidUpdate(PidObject* pid, const float error)
{
	float output;

	pid->error = error;   
	pid->integ += pid->error * pid->dt;
	pid->deriv = (pid->error - pid->prevError) / pid->dt;

	pid->outP = pid->kp * pid->error;
	pid->outI = pid->ki * pid->integ;
	pid->outD = pid->kd * pid->deriv;

	abs_outlimit(&(pid->integ), pid->iLimit);
	output = pid->outP + pid->outI + pid->outD;
	abs_outlimit(&(output), pid->maxOutput);
	pid->prevError = pid->error;

	return output;
}

void pidSetIntegralLimit(PidObject* pid, const float limit) 
{
    pid->iLimit = limit;
}

void pidSetIntegralLimitLow(PidObject* pid, const float limitLow) 
{
    pid->iLimitLow = limitLow;
}

void pidSetOutLimit(PidObject* pid, const float maxoutput) 
{
    pid->maxOutput = maxoutput;
}

void pidReset(PidObject* pid)
{
	pid->error     = 0;
	pid->prevError = 0;
	pid->integ     = 0;
	pid->deriv     = 0;
}

void pidSetError(PidObject* pid, const float error)
{
	pid->error = error;
}

void pidSetDesired(PidObject* pid, const float desired)
{
	pid->desired = desired;
}

float pidGetDesired(PidObject* pid)
{
	return pid->desired;
}

bool pidIsActive(PidObject* pid)
{
	bool isActive = true;

	if (pid->kp < 0.0001f && pid->ki < 0.0001f && pid->kd < 0.0001f)
	{
		isActive = false;
	}

	return isActive;
}

void pidSetKp(PidObject* pid, const float kp)
{
	pid->kp = kp;
}

void pidSetKi(PidObject* pid, const float ki)
{
	pid->ki = ki;
}

void pidSetKd(PidObject* pid, const float kd)
{
	pid->kd = kd;
}

void pidSetPID(PidObject* pid, const float kp,const float ki,const float kd)
{
	pid->kp = kp;
	pid->ki = ki;
	pid->kd = kd;
}
void pidSetDt(PidObject* pid, const float dt) 
{
    pid->dt = dt;
}

这一部分代码大家自行阅读,很好理解,另外大家如果嫌函数太多可以用C++来用对象实现PID结构体。(网上有,不想自己写去copy也行)

2. 串级PID算法的代码实现

由于我们要使用串级PID控制航向角,仅仅有上面的PID控制器代码还不够,咱们继续创建一个attitude_control.h和一个attitude_control.c文件,用来实现串级PID控制。

attitude_control.h文件内容如下:

#ifndef __ATTITUDE_PID_H
#define __ATTITUDE_PID_H
#include <stdbool.h>
#include "pid.h"

#define ATTITUDE_UPDATE_RATE 	500  //更新频率100hz
#define ATTITUDE_UPDATE_DT 		(1.0f / ATTITUDE_UPDATE_RATE)

typedef struct 
{
	float x;
	float y;
	float z;
} Axis3f;

//姿态集
typedef struct
{
	float roll;
	float pitch;
	float yaw;
} attitude_t;

extern PidObject pidAngleRoll;
extern PidObject pidAnglePitch;
extern PidObject pidAngleYaw;
extern PidObject pidRateRoll;
extern PidObject pidRatePitch;
extern PidObject pidRateYaw;
extern PidObject pidDepth;
extern configParam_t configParamCar;

void attitudeControlInit(void);
bool attitudeControlTest(void);

void attitudeRatePID(attitude_t *actualRate, attitude_t *desiredRate,attitude_t *output);	/* 角速度环PID */
void attitudeAnglePID(attitude_t *actualAngle,attitude_t *desiredAngle,attitude_t *outDesiredRate);	/* 角度环PID */
void attitudeResetAllPID(void);		/*复位PID*/
void attitudePIDwriteToConfigParam(void);

#endif /* __ATTITUDE_PID_H */

attitude_control.c文件内容如下:

#include <stdbool.h>
#include "pid.h"
#include "sensor.h"
#include "attitude_pid.h"

//pid参数
configParam_t configParamCar =
{
	.pidAngle=	/*角度PID*/
	{	
		.roll=
		{
			.kp=5.0,
			.ki=0.0,
			.kd=0.0,
		},
		.pitch=
		{
			.kp=5.0,
			.ki=0.0,
			.kd=0.0,
		},
		.yaw=
		{
			.kp=5.0,
			.ki=0.0,
			.kd=0.0,
		},
	},	
	.pidRate=	/*角速度PID*/
	{	
		.roll=
		{
			.kp=320.0,
			.ki=0.0,
			.kd=5.0,
		},
		.pitch=
		{
			.kp=320.0,
			.ki=0.0,
			.kd=5.0,
		},
		.yaw=
		{
			.kp=18.0,
			.ki=0.2,
			.kd=0.0,
		},
	},	
	.pidPos=	/*位置PID*/
	{	
		.vx=
		{
			.kp=0.0,
			.ki=0.0,
			.kd=0.0,
		},
		.vy=
		{
			.kp=0.0,
			.ki=0.0,
			.kd=0.0,
		},
		.vz=
		{
			.kp=21.0,
			.ki=0.0,
			.kd=60.0,
		},
	},
	
};


PidObject pidAngleRoll;
PidObject pidAnglePitch;
PidObject pidAngleYaw;
PidObject pidRateRoll;
PidObject pidRatePitch;
PidObject pidRateYaw;
PidObject pidDepth;

static inline int16_t pidOutLimit(float in)
{
	if (in > INT16_MAX)
		return INT16_MAX;
	else if (in < -INT16_MAX)
		return -INT16_MAX;
	else
		return (int16_t)in;
}


void attitudeControlInit()
{

	//pidInit(&pidAngleRoll, 0, configParamCar.pidAngle.roll, ATTITUDE_UPDATE_DT);   /*roll  角度PID初始化*/
	//pidInit(&pidAnglePitch, 0, configParamCar.pidAngle.pitch, ATTITUDE_UPDATE_DT); /*pitch 角度PID初始化*/
	pidInit(&pidAngleYaw, 0, configParamCar.pidAngle.yaw, ATTITUDE_UPDATE_DT);	   /*yaw   角度PID初始化*/
	//pidSetIntegralLimit(&pidAngleRoll, PID_ANGLE_ROLL_INTEGRATION_LIMIT);		   /*roll  角度积分限幅设置*/
	//pidSetIntegralLimit(&pidAnglePitch, PID_ANGLE_PITCH_INTEGRATION_LIMIT);		   /*pitch 角度积分限幅设置*/
	pidSetIntegralLimit(&pidAngleYaw, PID_ANGLE_YAW_INTEGRATION_LIMIT);			   /*yaw   角度积分限幅设置*/
	pidSetOutLimit(&pidAngleYaw, PID_ANGLE_YAW_INTEGRATION_LIMIT);

	//pidInit(&pidRateRoll, 0, configParamCar.pidRate.roll, ATTITUDE_UPDATE_DT);	 /*roll  角速度PID初始化*/
	//pidInit(&pidRatePitch, 0, configParamCar.pidRate.pitch, ATTITUDE_UPDATE_DT); /*pitch 角速度PID初始化*/
	pidInit(&pidRateYaw, 0, configParamCar.pidRate.yaw, ATTITUDE_UPDATE_DT);	 /*yaw   角速度PID初始化*/
	//pidSetIntegralLimit(&pidRateRoll, PID_RATE_ROLL_INTEGRATION_LIMIT);			 /*roll  角速度积分限幅设置*/
	//pidSetIntegralLimit(&pidRatePitch, PID_RATE_PITCH_INTEGRATION_LIMIT);		 /*pitch 角速度积分限幅设置*/
	pidSetIntegralLimit(&pidRateYaw, PID_RATE_YAW_INTEGRATION_LIMIT);			 /*yaw   角速度积分限幅设置*/
	pidSetOutLimit(&pidRateYaw, PID_RATE_YAW_INTEGRATION_LIMIT);
}

void attitudeRatePID(attitude_t *actualRate, attitude_t *desiredRate, attitude_t *output) /* 角速度环PID */
{
	//output->roll = pidOutLimit(pidUpdate(&pidRateRoll, desiredRate->roll - actualRate->roll));
	//output->pitch = pidOutLimit(pidUpdate(&pidRatePitch, desiredRate->pitch - actualRate->pitch));
	output->yaw = pidOutLimit(pidUpdate(&pidRateYaw, desiredRate->yaw - actualRate->yaw));
}

void attitudeAnglePID(attitude_t *actualAngle, attitude_t *desiredAngle, attitude_t *outDesiredRate) /* 角度环PID */
{
	//outDesiredRate->roll = pidUpdate(&pidAngleRoll, desiredAngle->roll - actualAngle->roll);
	//outDesiredRate->pitch = pidUpdate(&pidAnglePitch, desiredAngle->pitch - actualAngle->pitch);

	float yawError = desiredAngle->yaw - actualAngle->yaw;
	if (yawError > 180.0f)
		yawError -= 360.0f;
	else if (yawError < -180.0)
		yawError += 360.0f;
	outDesiredRate->yaw = pidUpdate(&pidAngleYaw, yawError);
}

void attitudeResetAllPID(void) /*复位PID*/
{
	pidReset(&pidAngleRoll);
	pidReset(&pidAnglePitch);
	pidReset(&pidAngleYaw);
	pidReset(&pidRateRoll);
	pidReset(&pidRatePitch);
	pidReset(&pidRateYaw);
}

attitude_control.c文件一开始声明并初始化了一个结构体变量configParamCar ,类型为configParam(在pid.h中定义的),里面保存的就是小车所有PID的参数值,后续要做的就是对这个结构体进行PID调参。

大家可能注意到了attitudeControlInit(), attitudeRatePID(), attitudeAnglePID里面全部都有三轴的角度,只不过我屏蔽掉了俯仰角和滚装角,因为对于小车来说我们只需要航向角。后期实现四旋翼我们依然用的这一套代码框架,届时只需要使能其他两个角度就能实现四旋翼的姿态控制了。

四. UCOS-III姿态控制任务的实现

有了上面的驱动代码和PID算法,下面我们写main.c文件里面的StabilizationTask,实现姿态控制任务。

在上一篇STM32实现四驱小车(三)传感任务——姿态角解算的基础上,补充StabilizationTask函数的内容如下:

//stabilization姿态控制任务
void stabilization_task(void *p_arg)
{
	OS_ERR err;
	CPU_SR_ALLOC();

	int dt_ms = 1000 / ATTITUDE_UPDATE_RATE; //姿态数据采样周期,默认500Hz,2ms
	float ft = (float)(dt_ms) / 1000.0;		 //积分间隔,单位秒
	float throttle_base;					 //油门基础值,由油门通道决定
	float zoom_factor = 0.10f;				 //转弯角速度
	attitude_t realAngle, expectedAngle, expectedRate;
	attitude_t realRate, output;

	attitudeControlInit();
	
	while (1)
	{
/********************************   航向角姿态控制  ****************************************/
/********************************   油门 控制      ****************************************/
		//zoom_factor速度放大因子
		expectedAngle.yaw -= (float)(command[YAW]) * zoom_factor * ft;
		if (expectedAngle.yaw > 180.0f)
			expectedAngle.yaw -= 360.0f;
		if (expectedAngle.yaw < -180.0f)
			expectedAngle.yaw += 360.0f;

		//油门值,最高速9000,减速输出400rpm
		if (command[SPEED_MODE] == HIGH_SPEED)
			throttle_base = (float)(command[THROTTLE] * 8);
		else if (command[SPEED_MODE] == LOW_SPEED)
			throttle_base = (float)(command[THROTTLE] * 4);

		//没有油门输出,也没有转弯信号,此时机器人在静止状态
		//始终把当前姿态角作为期望姿态角
		//不使能PID计算,复位所有PID
		if (command[THROTTLE] == 0 && command[YAW] == 0)
		{
			expectedAngle.yaw = realAngle.yaw;
			attitudeResetAllPID(); //PID复位
			expectedRate.yaw = 0;
			output.yaw = 0;
		}
		//有油门输出,说明机器人在运动状态,此时应该做姿态控制
		else
		{
			//姿态角串级pid计算
			attitudeAnglePID(&realAngle, &expectedAngle, &expectedRate); /* 角度环PID */
			attitudeRatePID(&realRate, &expectedRate, &output);			 /* 角速度环PID */
		}

		//pid控制量分配到电机混控
		set_speed[1] = throttle_base - output.yaw;
		set_speed[0] = set_speed[1];
		set_speed[3] = -(throttle_base + output.yaw);
		set_speed[2] = set_speed[3];
	
		//延时采样
		delay_ms(dt_ms);
	}
}

这里面while循环里面的步骤为,首先根据读到的遥控器的方向摇杆的值更新期望偏航角,期望偏航角来自于方向摇杆的积分。然后根据速度档位按钮的值确定当前的油门量(低速与高速模式)。之后判断遥控器油门摇杆与方向摇杆的位置,如果都居中说明机器人应该静止,此时复位所有PID,PID输出置零。如果任何一个摇杆不是中间位置,说明是在前进后退或者原地转弯状态,此时使能串级PID控制,控制器的输出送入到混合控制器(注意这个词,在飞控中还会用到),由于四驱车的模型很简单,其实就是一侧加上这个控制量加速,一侧减去这个控制量减速,从而实现差速,控制机器人转弯。

这里面有一个数组set_speed[4],存储的是各个电机的速度,这个速度值在下一篇电机伺服任务中我们要用到,它作为期望速度值,作为电机速度伺服的PID控制器输入。

这里做下说明,本系列文章笔者重在分享思想、算法,在讲解上会弱化一些基本知识(比如单片机各个外设的原理、单片机编程的基本知识等),在代码的粘贴上会忽视一些底层的驱动代码和无关紧要的部分,事实上上面的代码我都经过删减了,只留下了干货。所以可以说面向的是中高级选手,拿来主义者可以打道回府了,本系列文章不开源,不提供源码,请见谅。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32实现四驱小车(四)姿态控制任务——偏航角串级PID控制算法 的相关文章

  • 嵌入式控制器EC是如何运行起来的

    EC的作用在本文中就不谈了 xff0c 百度里面可以找到很多 xff0c 反正像笔记本这样的移动设备 xff0c 都需要EC来做相关的控制 xff0c 具体可以自己去百度 这里主要介绍EC是如何运行起来的 xff0c 其实和CPU是如何运行
  • 如何在C/C++中利用变量来创建变化长度的数组

    在C C 43 43 中可以直接使用 xff1a int a n 创建长度为n的整型数组 xff0c 这种定义数组的方法需要事先确定好数组的长度 xff0c 即 n 必须为常量 xff0c 这意味着 xff0c 如果在实际应用中无法确定数组
  • Host是如何与EC通信的(BIOS通过ACPI协议对EC RAM进行读写)

    文章目录 一 EC RAM是什么 二 使用步骤 1 高级配置和电源接口 ACPI 规范 2 EC RAM读写过程 总结 一 EC RAM是什么 EC提供256字节的可被系统读写的RAM空间 EC的资源 包括电池信息 EC版本等信息 在该RA
  • PX4之飞行控制框架

    PX4的飞行控制程序通过模块来实现 xff0c 与飞控相关的模块主要有commander xff0c navigator xff0c pos control xff0c att control这几个 xff0c 分别可以在src modul
  • 使用树莓派学习Linux驱动开发-02 面向对象/分层/分离驱动设计思想编写LED驱动程序

    系列文章目录 此博客内容根据韦东山嵌入式Linux驱动开发课程书写而来 将课程中用到的代码移植到树莓派4B板子 文章目录 系列文章目录 前言 一 驱动设计思想 分离 二 示例代码 三 操作步骤如下 前言 在上一篇内容中 书写了一个设备驱动程
  • 0.嵌入式控制器EC实战 Embedded Controller开发概述

    文章目录 1 嵌入式控制器EC概述2 EC芯片框图 xff08 IT8502为例 xff09 3 General Purpose I O Port GPIO 4 SMBus xff08 System Management Bus xff0c
  • 12.嵌入式控制器EC实战 SMBus概述

    文章目录 SMBus概述 ACPI规范中的函数返回值含义 SMBus h中的宏定义含义 EC中SMBus各个读写函数分析 bRWSMBus读写函数 在嵌入式控制器EC中 SMBus的起到的作用有两个 第一个是通过SMBus读取智能电池中的相
  • Win10下开机自动启动运行bat脚本并打开cmd运行命令

    场景 xff1a 本菜鸟有一台工作站安装了windows10操作系统 xff0c 机器设置的是开机自动启动 xff0c 但是维护人员无法一直在机房 xff0c 一旦机房断电重启就会导致工作站中运行的程序无法重新启动 xff0c 需要维护人员
  • 银河麒麟V10系统 syslog和kern.log文件过大问题解决,定时清理日志文件

    文章目录 1 新建clear log sh脚本文件 2 设置cron任务 3 解决不执行的方法 需求 在使用银河麒麟V10系统时 var log kern log 和 var log syslog两个文件随着使用的时间增长会一直增大 最后可
  • 2.龙芯2k1000 linux3.10内核编译过程

    龙芯2k1000 linux3 10内核编译过程 文章目录 龙芯2k1000 linux3 10内核编译过程 xff08 一 xff09 在Ubuntu环境下载并配置交叉编译链 xff08 二 xff09 下载linux3 10内核源码 x
  • Vulkan实战之逻辑设备和队列

    文章目录 介绍指定要创建的队列指定使用的设备特性创建逻辑设备检索队列句柄最终代码 介绍 在选择要使用的物理设备之后 xff0c 我们需要设置一个逻辑设备来与它接口 逻辑设备创建过程类似于实例创建过程 xff0c 并描述了我们想要使用的特性
  • Vulkan实战之Window surface

    文章目录 创建window surface查询演示支持创建surface队列最终代码 由于Vulkan是一个平台无关的API xff0c 因此它不能自己直接与窗口系统接口交互 为了在Vulkan和窗口系统之间建立连接并将结果显示到屏幕上 x
  • 飞腾UEFI配置GPIO—飞腾FT2000/4 (D2000/8) GPIO引脚配置及使用

    文章目录 一 FT2000 4 GPIO介绍 GPIO 接口信号说明 专用GPIO GPIO0 A1 GPIO0 A7 SCI 介绍 二 UEFI下配置GPIO相关寄存器 GPIO相关相关引脚功能配置说明 UEFI配置GPIO相关代码 UE
  • stm32-sbus数据接收,并通过CAN转发给车辆控制

    1 xff09 串口程序 代码如下 xff1a span class token macro property span class token directive hash span span class token directive
  • 基于stm32的无线多点温度采集系统设计

    本科时候做过关于ds18b20温度传感器的课程设计 xff0c 当时好像是先用单片机A采集温度 xff0c 其中用矩阵键盘设置报警值 xff0c 然后通过232串口将温度值传给单片机B xff0c 单片机B上的数码管显示 xff0c 同时单
  • 无刷电机和桨叶的选择

    无刷电机和桨叶的选择 无刷电机的kv值越高 就要配越小的螺旋桨 简单说 高kv配小桨 低kv配大桨 无刷电机KV值定义为 转速 V xff0c 意思为输入电压增加1伏特 xff0c 无刷电机空转转速增加的转速值 由此无刷电机电压的输入与电机
  • Intel RealSense Win10+QT+Cmaker 开发环境搭建

    文章目录 一 Intel RealSense SDK开发工具下载安装二 QT 43 CMaker配置Intel RealSense SDK2 0开发环境 一 Intel RealSense SDK开发工具下载安装 从官方github上下载S
  • NVIDIA Jetson不同系列对比

    文章目录 一 NVIDIA Jetson介绍二 NVIDIA Jetson模组比较 一 NVIDIA Jetson介绍 NVIDIA Jetson 是世界领先的平台 xff0c 适用于自主机器和其他嵌入式应用程序 该平台包括 Jetson
  • SLAM笔记五——EKF-SLAM

    上一节主要讲解了EKF的基本原理 xff0c 这一次主要关注如何将EKF算法应用在SLAM上 EKF SLAM 现在的问题就是解决下面这个概率分布的估计问题 xff1a 阴影部分为未知 这里我们需要确定均值和方差到底是什么 xff1f 假设
  • HTTP协议-报文解析

    概述 HTTP xff08 超文本传输协议 xff09 是一个基于请求与响应模式的 无状态的 应用层的协议 xff0c 常基于TCP的连接方式 HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成 请求消息和响应消息的组成 xff1

随机推荐

  • 站在巨人的肩膀上——Linux信号量操作

    感谢那些让我度过此学海的无名勇士 信号量简介 xff1a 在对于临界区资源管理的过程中 xff0c 多个程序同时访问一个共享资源经常容易引发一系列问题 xff1a 如死锁 xff0c 结果不唯一等等 xff0c 在1965年 xff0c 由
  • 详解常用的ROS内置消息类型

    1 std msgs 该类型是ROS内置的标准消息类型 xff0c 是最基础的消息类型 xff08 1 xff09 对于单类型 xff0c 下表是其与C 43 43 和python的对应关系 xff1a Primitive TypeSeri
  • 【Robomaster-ICRA-AI挑战赛跳坑篇】(二、TX2刷机)

    TX2刷机与程序下载 TX2安装在步兵车上作为机载计算机使用 xff0c 跑上层代码 xff0c RoboRTS代码托管在github上 xff1a https github com RoboMaster RoboRTS xff0c 目前一
  • 字符串目录判断

    tcschr tcsrchr 好处 xff1a 是可以不管是用unicode 编码还是其他 代码都不用改 C 43 43 标准库函数提供了字符和字符串的操作函数 xff0c 并提供了其UNICODE版本 xff0c 如 xff1a 1 tc
  • @Bean放入其引用Bean中初始化失败分析

    以下讨论的问题及术语均在SpringBoot框架下 xff0c 问题十分小众 xff0c 仅做整理记录 1 先说重点 Bean依赖属性的注入顺序 xff0c 与代码定义顺序无关 xff1b 最好是将 64 Bean注解配置的Bean放在 6
  • MPU6050+HMC5883+BMP180+GPS导航系统设计

    老师有个项目 xff0c 让我搞惯导这一块 虽然最后也没有用上廉价的MPU6050 xff0c 而是用了一两万的Xsens 但是本人还是想写一下MPU6050 xff0c 虽然技术含量不高 xff0c 但是写下来 xff0c 留个纪念吧 首
  • 使用BeanCopier抛出NullPointerException溯源

    问题 使用cglib提供的net sf cglib beans BeanCopier进行对象拷贝时 xff0c 抛出如下异常 xff1a Exception in thread span class token string 34 main
  • 自制Alfred/Wox插件推荐

    最近上手Alfred的使用 xff0c 日常工作中存在很多需要高频执行的连续性动作 xff0c 将这一系列动作封装成Workflow xff0c 通过命令触发 xff0c 对提升效率确有很大帮助 自己封装了一些简单的Workflow xff
  • Python删除某一目录下的空文件(夹)

    Python删除某一目录下的空文件 夹 用途 输入文件夹路径 xff0c 将此文件夹下所有的空文件夹和空文件删除 xff0c 算是文件操作的一个习作吧 我拿它做什么就不广而告之了 代码 span class hljs comment cod
  • Win10下pip的安装

    pip简介 pip 是一个安装和管理 Python 包的工具 xff0c 通过pip我们能够轻松地下载和卸载python的第三方包 原料 64位Windows10 专业版python 2 7 12 下载安装包 进入python官方网站 xf
  • Python爬虫爬取动态页面思路+实例(一)

    简介 有时候 xff0c 我们天真无邪的使用urllib库或Scrapy下载HTML网页时会发现 xff0c 我们要提取的网页元素并不在我们下载到的HTML之中 xff0c 尽管它们在浏览器里看起来唾手可得 这说明我们想要的元素是在我们的某
  • zerorpc-python官方入门

    原文地址 xff1a http www zerorpc io 一个易于使用的 xff0c 直观的 xff0c 跨语言的RPC zerorpc是一个在服务端进程上提供分布式通信的轻量级的 可靠的跨语言的库 它基于ZeroMQ和MessageP
  • Python保存json文件并格式化

    背景 最近自己搞些小东西 xff0c 需要用json文件存储些文件属性什么的 xff0c 但是发现用json包里的json dump 方法存json文件的效果好丑 xff08 其实是没仔细看方法 xff09 于是上网找了一份格式化json文
  • No module named 'django.templates'起因和解决

    当我跟着django官网上的教程写到template模板这一部分的时候 xff0c 出现了上述问题 span class hljs constant No span span class hljs class span class hljs
  • Celery ValueError: not enough values to unpack (expected 3, got 0)的解决方案

    背景 最近因项目需要 xff0c 学习任务队列Celery的用法 xff0c 跟着官网写Demo xff0c 出现如题错误 xff0c 最终在github的Issues里找到解决办法 xff0c 记录如下 场景还原 本地环境如下 xff1a
  • 震惊!Selenium分手PhantomJS

    背景 今天本地调试基于Selenium 43 PhantomJS的动态爬虫程序顺利结束后 xff0c 着手部署到服务器上 xff0c 刚买的热乎的京东云 xff0c 噼里啪啦一顿安装环境 xff0c 最后跑的时候报了这么个错误 xff1a
  • 基于labview的姿态测量系统上位机界面编写

    当时学习VB是为了写个上位机去控制LED亮灭 xff0c 相信大家学习51 stm32都是从流水灯开始的 xff0c 就像那句 hello world 一样经典 后来学习了LABVIEW xff0c 决定用它写个界面 当时主要是想用LABV
  • STM32解析SBUS信号例程详解

    文章目录 1 SBUS信号简介2 STM32F7解析SBUS信号例程 xff08 1 xff09 串口配置 xff08 2 xff09 串口中断接收 xff08 3 xff09 信号解析 1 SBUS信号简介 最近在搞一个项目的通信和控制
  • STM32实现四驱小车(二)通信任务——遥控器SBUS通信

    目录 一 遥控器通信原理简介二 SBUS信号解析1 SBUS信号简介2 STM32F7解析SBUS信号 三 通信任务实现 一 遥控器通信原理简介 要实现一个遥控小车当然要有一个遥控器了 xff0c 目前市面上常用的航模遥控器基本都是2 4G
  • STM32实现四驱小车(四)姿态控制任务——偏航角串级PID控制算法

    目录 一 绪论二 角度环串级PID原理1 PID基本算法2 姿态角串级PID原理 三 如何用STM32实现角度 角速度的串级PID控制1 PID算法的代码实现2 串级PID算法的代码实现 四 UCOS III姿态控制任务的实现 一 绪论 这