PWM驱动电机系列——PID控制 (各电机设备之间的驱动差异及区别)自动控制系统的性能指标

本文主要是介绍PWM驱动电机系列——PID控制 (各电机设备之间的驱动差异及区别)自动控制系统的性能指标,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

电机驱动

直流电机:类似于驱动LED亮灭一样,根据电机的电路原理图判断是什么数字电平有效。

步进电机:类似于驱动LED的周期翻转一样,在一个周期里面进行对步进电机的IO电平的自动翻转,LED=!LED 。(1)使用的定时器方式,设置一个确定的定时器周期 (2)PWM驱动

可以这样简单理解,首先要区分两种接线方法,一种是共阳极接法,就是PUL+和DIR+都是接5V,而PUL-就是你要输出脉冲的IO口,而DIR-就是接控制控制步进电机的正反转的IO,也就是说这种方法的DIR,给高电平的话就正转;然后如果是共阴极接法就是,相反而已,PUL-和DIR-都是接GND,而PUL+和DIR+分别接输出脉冲IO和控制正反转IO,然后只要给一个完整的脉冲给电机驱动器,然后如果设置的细分数是800的话,也就是说360°除以800等于0.45°,也就是接到上面所说的,一个完整的脉冲,步进电机就会转过0.45°。(步进电机通常通过步数控制位置,不需要PID调节)

这两类的电机驱动都是无反馈的。

像伺服电机是有反馈的,有反馈意味着闭环控制,开环控制就是只管控制不管反馈,闭环控制中PID控制算法是最为经典的。

PID

PID,就是“比例(proportional)、积分(integral)、微分(derivative)”,是一种很常见的控制算法。

单环

PID参数结构体:定义一个速度/位置闭环的PID参数结构体变量

初始化PID参数:把目标值、期望值、累计偏差清零、配置PID系数

设置目标速度/位置:在函数中(通过外设控制或上位机)设置目标速度/目标

PID闭环控制:通过PID反馈结果更新定时器的比较值,限制占空比

通过编码器的当前计数值反映电机的驱动情况。


PID参数结构体:定义一个电流闭环的PID参数结构体变量

初始化PID参数:把期望值、累计偏差清零、配置目标值、PID系数

实际电流滤波:ADC采集中断回调函数中对实际电流进行滤波

设置目标电流:在函数中(通过外设控制或上位机)设置目标电流

PID闭环控制:通过PID反馈结果更新定时器的比较值,限制占空比

双环

双环控制分内环和外环,外环控制的是优先考虑对象,内环用于对控制效果进行优化。

PID参数结构体:定义位置、速度闭环的PID参数结构体变量

初始化PID参数:把目标值、期望值、累计偏差清零、配置PID系数

设置目标速度:在函数中(通过外设控制或上位机)设置目标速度

PID双环控制:通过双环PID反馈结果更新定时器的比较值,限制占空比

三环

三环控制分内环、中环和外环,外环控制的是优先考虑对象,中环和内环用于对控制效果依次进行优化。

位置式PID

位置式控制器直接计算控制输出的位置(或值),而不是增量。通过累积误差来计算输出

u(k) = Kp * e(k) + Ki * Σe(k) + Kd * (e(k) - e(k-1))
其中 u(k) 是当前时刻的控制量, e(k) 是当前时刻的误差,Σe(k) 是累积误差,e(k) - e(k-1) 是误差的增量。

基本范式如图:

一般实现

typedef struct
{float target_val;   //目标值float Error;          /*第 k 次偏差 */float LastError;     /* Error[-1],第 k-1 次偏差 */float PrevError;    /* Error[-2],第 k-2 次偏差 */float Kp,Ki,Kd;     //比例、积分、微分系数float integral;     //积分值float output_val;   //输出值
}PID;/*** @brief  PID参数初始化*	@note 	无* @retval 无*/
void PID_param_init()
{PosionPID.target_val=3600;				PosionPID.output_val=0.0;PosionPID.Error=0.0;PosionPID.LastError=0.0;PosionPID.integral=0.0;PosionPID.Kp = 10;PosionPID.Ki = 0.5;PosionPID.Kd = 0.8;
}/*** @brief  位置PID算法实现* @param  actual_val:实际测量值*	@note 	无* @retval 通过PID计算后的输出*/
float PosionPID_realize(PID *pid, float actual_val)
{/*计算目标值与实际值的误差*/pid->Error = pid->target_val - actual_val;/*积分项*/pid->integral += pid->Error;/*PID算法实现*/pid->output_val = pid->Kp * pid->Error +pid->Ki * pid->integral +pid->Kd *(pid->Error -pid->LastError);/*误差传递*/pid-> LastError = pid->Error;/*返回当前实际值*/return pid->output_val;
}

速度转换

下面这个过程,函数usSpeed2Cycle() 将 PID 控制量转换为占空比。可以用来将速度值映射到合适的占空比范围内。

 首先完成GPIO功能复用

#define TIM3_ARR (500)
#define TIM3_PSC (12)void TIM3_CH34_Init(u16 arr,u16 psc)
{GPIO_InitTypeDef         GPIO_InitStructure;TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		GPIO_Init(GPIOB, &GPIO_InitStructure);TIM_TimeBaseStructure.TIM_Period = arr-1; 					TIM_TimeBaseStructure.TIM_Prescaler =psc-1; 					TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse=0;TIM_OC3Init(TIM3, &TIM_OCInitStructure);  TIM_OC4Init(TIM3, &TIM_OCInitStructure);  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  TIM_Cmd(TIM3, ENABLE);    
}/*左右驱动轮 CCR 范围0-499  ARR 500*/
#define LEFT_WHEEL_T3CH4_PWM_CYCLE( Cycle )   TIM_SetCompare4( TIM3, Cycle );#define RIGHT_WHEEL_T3CH3_PWM_CYCLE( Cycle )  TIM_SetCompare3( TIM3, Cycle );/*将左右驱动轮占空比设置及电机DIR进行宏函数封装
*///左轮前进 标记前进
#define LEFT_WHEEL_FORWARD(Cycle)   LEFT_WHEEL_T3CH4_PWM_CYCLE(Cycle);\CTRL_WHEEL_L=0;\g_tLeftWheel.Direction = WheelForward;  
//左轮后退 标记后退 
#define LEFT_WHEEL_RETREAT(Cycle)   LEFT_WHEEL_T3CH4_PWM_CYCLE(Cycle);\CTRL_WHEEL_L=1;\g_tLeftWheel.Direction = WheelRetreat;
//右轮前进 标记前进
#define RIGHT_WHEEL_FORWARD(Cycle)  RIGHT_WHEEL_T3CH3_PWM_CYCLE( Cycle );\CTRL_WHEEL_R=0;\g_tRightWheel.Direction = WheelForward;
//右轮后退 标记后退 
#define RIGHT_WHEEL_RETREAT(Cycle)  RIGHT_WHEEL_T3CH3_PWM_CYCLE( Cycle );\CTRL_WHEEL_R=1;\g_tRightWheel.Direction = WheelRetreat;
/*******************************************************************************
* Description    : 速度转换为对应的PWM占空比
* Input          : speed :mm/s  范围 -400~400
* Output         : None
* Return         : 电机占空比 450~50
*******************************************************************************/__inline u16 usSpeed2Cycle(s16 loc_Speed)
{if(loc_Speed<10 && loc_Speed>-10)		 //速度低于10mm/s 轮子响应线性太差 直接给0  电池电量AD大于3000 PWM线性响应450-0.return 500;							 //返回500占空比 电机停转if(loc_Speed<-400 && loc_Speed>400)      //速度超过400mm/s 返回速度最大占空比return 450-400;if(loc_Speed>0)return 450-loc_Speed;elsereturn 450+loc_Speed;
}

PID调速

下面整个过程中,该函数使用 PID 控制算法独立计算左右轮的目标占空比,并根据占空比的正负决定轮子的运动方向,从而实现对两个轮子的独立速度控制。

vMotionWheel() 实现了两个重要过程的集合,一个基于 PID 控制的双轮速度控制器,可以独立控制左右轮的速度和方向。这对于实现复杂的移动机器人控制非常有用。

/*驱动轮速度环PID参数 SpeedOut= P*E[n] + I*(E[n]+E[n-1]+E[n-2].....+E[1]))*/
float P_V           = 1;
float I_V           = 0.1;
float D_V           = 0;__IO float    SpeedLeftUiV;         //左轮速度环积分结果
__IO float    SpeedRightUiV;        //右轮速度环积分结果
/*******************************************************************************
* Description    : 将轮子的实际转速靠近期望转速 使用到全局结构体 LeftWheel RightWheel
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/void vMotionWheel(void)
{int16_t PidTemp;PidTemp = sSpeed_L_PID( g_tLeftWheel.SoftSpeed, g_tLeftWheel.RealSpeed );     //PID计算下一时刻速度if( PidTemp < 0 )//左轮倒退{                         LEFT_WHEEL_RETREAT(usSpeed2Cycle(PidTemp));} else//左轮前进{LEFT_WHEEL_FORWARD(usSpeed2Cycle(PidTemp));}PidTemp = sSpeed_R_PID( g_tRightWheel.SoftSpeed, g_tRightWheel.RealSpeed );if( PidTemp < 0 )//右轮后退{               RIGHT_WHEEL_RETREAT(usSpeed2Cycle(PidTemp));} else//右轮前进{RIGHT_WHEEL_FORWARD(usSpeed2Cycle(PidTemp));}            
}

下面两个过程即是具体PID控制过程, 这个 PID 控制算法的输入是期望速度实际速度,输出是用于控制轮子转速的 PID 控制量。

左轮速度环

这个PID调速控制器的工作原理如下:

根据期望速度 RefV 和实际反馈速度 FdbV 计算出速度误差 ErrV。
利用比例、积分两个部分对误差进行修正,得到最终的控制量 OutTempV。
对控制量进行限幅处理,防止输出超出合理范围。
最后对输出值进行修正,确保输出符合期望的正负反馈。

/*******************************************************************************
* Description    : 左轮速度环
* Input          : RefV-期望速度指令,FdbV-当前速度反馈,单位mm/s
* Output         : None
* Return         : 左轮下一个时刻应该的速度
*******************************************************************************/int16_t sSpeed_L_PID(int16_t RefV,int16_t FdbV)
{int16_t  UpV=0;int16_t  ErrV,OutTempV;int16_t  OutputV;ErrV = RefV - FdbV;//误差速度UpV = P_V*ErrV;      // 计算出比例部分SpeedLeftUiV = SpeedLeftUiV + ((float)(UpV*I_V));//计算出积分累加部分/*积分范围限制,不能无限累加*/if(SpeedLeftUiV>UIMAXV){SpeedLeftUiV=UIMAXV;}elseif(SpeedLeftUiV<UIMINV){SpeedLeftUiV=UIMINV;}OutTempV = UpV+(int16_t)SpeedLeftUiV;  //PI/*输出速度范围限制*/if (OutTempV > OUTMAXV){OutputV =  OUTMAXV;}else if (OutTempV < OUTMINV){OutputV = OUTMINV;}else{OutputV = OutTempV;}if(RefV>=0){if(OutputV<0) OutputV=1;}else if(RefV<0){if(OutputV>0) OutputV=-1;}return OutputV;
}

RefV: 期望的速度值(参考值) FdbV: 实际的反馈速度值
计算速度误差:
ErrV = RefV - FdbV: 计算出实际速度与期望速度之间的误差
比例控制部分:
UpV = P_V * ErrV: 根据比例常数 P_V 和误差 ErrV 计算出比例控制量
积分控制部分:
SpeedRightUiV = SpeedRightUiV + (UpV * I_V): 根据积分常数 I_V 和比例控制量 UpV 计算出积分累加值
对积分值进行限幅,防止积分饱和:
如果 SpeedRightUiV 大于 UIMAXV, 则设置为 UIMAXV
如果 SpeedRightUiV 小于 UIMINV, 则设置为 UIMINV
PI控制量计算:
OutTempV = UpV + (int16_t)SpeedRightUiV: 将比例控制量和积分控制量相加,得到PI控制量
输出限幅:
如果 OutTempV 大于 OUTMAXV, 则输出 OUTMAXV
如果 OutTempV 小于 OUTMINV, 则输出 OUTMINV
否则输出 OutTempV
输出修正:
如果 RefV 大于等于 0, 而输出 OutputV 小于 0, 则将其设置为 1
如果 RefV 小于 0, 而输出 OutputV 大于 0, 则将其设置为 -1
最终返回输出值 OutputV

通过这样的PID控制算法,可以实现对电机转速的精确调节和跟踪。比例项快速响应误差,积分项消除稳态误差,整个系统能够快速、平稳地达到期望的转速目标。这种直接使用误差 ErrV 和积分累加项 SpeedRightUiV 的方式就是典型的位置式 PID 控制算法。

右轮速度环

/*******************************************************************************
* Description    : 右轮速度环
* Input          : RefV-期望速度指令,FdbV-当前速度反馈,单位mm/s
* Output         : None
* Return         : 右轮下一个时刻应该的速度
*******************************************************************************/int16_t sSpeed_R_PID(int16_t RefV,int16_t FdbV)
{int16_t  UpV=0;int16_t  ErrV,OutTempV;int16_t  OutputV;ErrV = RefV - FdbV;//误差速度UpV = P_V*ErrV;      // 计算出比例部分SpeedRightUiV = SpeedRightUiV + ((float)(UpV*I_V));//计算出积分累加部分/*积分范围限制,不能无限累加*/if(SpeedRightUiV>UIMAXV){SpeedRightUiV=UIMAXV;}elseif(SpeedRightUiV<UIMINV){SpeedRightUiV=UIMINV;}OutTempV = UpV+(int16_t)SpeedRightUiV;  //PI/*输出速度范围限制*/if (OutTempV > OUTMAXV){OutputV =  OUTMAXV;}else if (OutTempV < OUTMINV){OutputV = OUTMINV;}else{OutputV = OutTempV;}if(RefV>=0){if(OutputV<0) OutputV=1;}else if(RefV<0){if(OutputV>0) OutputV=-1;}return OutputV;
}

速度平滑过渡

一种软速度调整的功能,用于将期望速度平滑地过渡到实际软速度上。

开发经验:这种平滑过渡有助于减少速度变化过程中的冲击和振荡,提高控制系统的稳定性和响应性。

#define SLOW_ACCELE_MAX_NUM   (5)      //缓变速步进单位 mm/svoid vSoftSpeedAdjust(void)
{if(g_tLeftWheel.ExpectSpeed != g_tLeftWheel.SoftSpeed){if(g_tLeftWheel.ExpectSpeed > g_tLeftWheel.SoftSpeed)g_tLeftWheel.SoftSpeed+=SLOW_ACCELE_MAX_NUM;else g_tLeftWheel.SoftSpeed-=SLOW_ACCELE_MAX_NUM;}if(g_tRightWheel.ExpectSpeed != g_tRightWheel.SoftSpeed){if(g_tRightWheel.ExpectSpeed > g_tRightWheel.SoftSpeed)g_tRightWheel.SoftSpeed+=SLOW_ACCELE_MAX_NUM;else g_tRightWheel.SoftSpeed-=SLOW_ACCELE_MAX_NUM;}    
}

期望速度 ExpectSpeed 逐步调整到实际软速度 SoftSpeed 上,以达到平滑过渡的效果。

该函数将被短周期地调用一次。对于左右驱动轮分别进行以下处理:
首先检查期望速度 ExpectSpeed 和当前软速度 SoftSpeed 是否相等。如果不相等,则需要进行调整。
如果期望速度大于当前软速度,则将软速度以 SLOW_ACCELE_MAX_NUM 的速率加大。
如果期望速度小于当前软速度,则将软速度以 SLOW_ACCELE_MAX_NUM 的速率减小。
SLOW_ACCELE_MAX_NUM 是一个常量,表示软速度每次调整的最大增量。
这种逐步调整的方式可以让速度变化更加平滑,避免突然的加速或减速,从而达到更好的控制效果。

增量式PID

当以速度误差为输入,输出速度控制量的方式,更适合采用增量式 PID 控制算法。因为此时算法关心的是速度的变化,而不是绝对速度值本身。这样可以更好地实现速度跟踪控制,减少速度跟踪过程中的振荡超调现象。这种方式通常更适合数字化控制系统的实现,一般只需要存储前一时刻的控制量和误差,计算简单。

增量式 PID 控制是基于误差的增量来计算控制量,表达式为:

u(k) = u(k-1) + Kp * (e(k) - e(k-1)) + Ki * e(k) + Kd * (e(k) - 2*e(k-1) + e(k-2))
其中 e(k) 是当前时刻的速度误差, e(k-1) 是上一时刻的速度误差, u(k) 是当前时刻的 PID 控制量。

总结:

对于速度跟踪控制来说,增量式 PID 更加适合,因为它直接计算速度变化量。
相比之下,位置式 PID 需要计算绝对速度误差,在速度跟踪控制中可能会产生超调和振荡等问题。

基本范式如图:

一般实现

/*** @brief  速度PID算法实现* @param  actual_val:实际值*	@note 	无* @retval 通过PID计算后的输出*/
float addPID_realize(PID *pid, float actual_val)
{/*计算目标值与实际值的误差*/pid->Error = pid->target_val - actual_val;/*PID算法实现,照搬公式*/pid->output_val += pid->Kp * (pid->Error - pid-> LastError) +pid->Ki * pid->Error +pid->Kd *(pid->Error -2*pid->LastError+pid->PrevError);/*误差传递*/pid-> PrevError = pid->LastError;pid-> LastError = pid->Error;/*返回当前实际值*/return pid->output_val;
}

总结:
增量式PID控制器通过计算输入数据的变化量来调整输出,适用于控制系统不需要绝对输出值的情况。


沿边速度控制

/*沿边传感器位置环PID参数 
Out = P*( E[n]-E[n-1] ) + I*E[n] + D*(E[n]- 2*E[n-1] + E[n-2])  */float P_AE           = 3;
float I_AE           = 0.1;
float D_AE           = 0.1;IncPID_t g_tIncPID={0};/*******************************************************************************
* Description    : 沿边传感器 增量式PID 计算
* Input          : NewData 新得到沿边传感器de 误差数据
* Output         : None
* Return         : 速度改变值
*******************************************************************************/
s16 iIncPIDCalc(s16 NewData)
{s16 loc_ChangeSpeed;loc_ChangeSpeed = P_AE*( NewData-g_tIncPID.LastData ) + \I_AE*NewData + D_AE*(NewData- (2*g_tIncPID.LastData) + g_tIncPID.PreData);g_tIncPID.PreData=g_tIncPID.LastData;g_tIncPID.LastData=NewData;return loc_ChangeSpeed;
}

增量式PID控制器工作原理如下:

增量计算: loc_ChangeSpeed 是计算出的增量输出。
使用了三个项:比例(P)、积分(I)、微分(D)。
比例项 (P):
P_AE * (NewData - g_tIncPID.LastData) 计算当前值与上一次值的差异。
积分项 (I):
I_AE * NewData 使用当前值来计算积分项。
微分项 (D):
D_AE * (NewData - (2 * g_tIncPID.LastData) + g_tIncPID.PreData) 计算当前值、上一次值和前一次值之间的变化率。
状态更新:
g_tIncPID.PreData = g_tIncPID.LastData; 将上一次数据保存为前一次数据。
g_tIncPID.LastData = NewData; 更新上一次数据为当前数据。

不同类型的电机控制

伺服电机:常用于精确控制位置、速度和方向,经典的PID控制适用于这样的调节。

舵机:将伺服电机的三环控制简化成了一环,即只检测位置环
无刷电机(磁极旋转,线圈不动):常用于需要精确速度和位置控制的应用,PID中的速度调整适合其控制特性。

有刷电机(磁极不动,线圈旋转):比无刷电机易于驱动,一般PID控制算法也适用于有刷电机。但是其物理结构有缺陷(碳刷与线圈接线头之间通断交替,会发生电火花,产生电磁破,干扰电子设备)

如根据与目标的偏差调整左右轮的速度,以实现路径校正和保持。适用于需要根据传感器反馈进行精确导航的场景。

u8 ucRouteAdjustByAlongEdgeDistance(u16 loc_FbValue,u16 loc_ExpValue,s16 loc_Speed)
{s16 loc_DiffValue = loc_ExpValue - loc_FbValue;//差值s16 loc_Divisor;//角度 转 速度值 loc_Divisor=iIncPIDCalc(loc_DiffValue);//速度值的调整基准 if( abs(loc_Divisor) > 100)//角度太大了 速度调整值不能超过最大速度{if(loc_Divisor>0) loc_Divisor=100;elseloc_Divisor=-100;}g_tLeftWheel.ExpectSpeed=    loc_Speed - loc_Divisor;g_tRightWheel.ExpectSpeed=   loc_Speed + loc_Divisor;return 0;}

控制差速驱动电机工作原理如下:

计算差值:
loc_DiffValue = loc_ExpValue - loc_FbValue; 计算期望值和反馈值之间的差值。
增量PID计算:
loc_Divisor = iIncPIDCalc(loc_DiffValue); 使用增量PID控制器计算速度调整基准。
限制调整值:
如果 loc_Divisor 的绝对值大于 100,将其限制在 -100 到 100 之间。
速度调整:
根据 loc_Divisor 调整左右轮的期望速度:
g_tLeftWheel.ExpectSpeed = loc_Speed - loc_Divisor;
g_tRightWheel.ExpectSpeed = loc_Speed + loc_Divisor;

串级PID

先输入位置式PID再经过增量式PID,最后再输出

自动控制系统的性能指标

主要有三个方面:稳定性、快速性、准确性。
稳定性:系统在受到外作用后,若控制系统使其被控变量随时间的增长而最终与给定期望值一致,则称系统是稳定的,我们一般称为系统收敛。如果被控量随时间的增长,越来越偏离给定值,则称系统是不稳定的,我们一般称为系统发散。稳定的系统才能完成自动控制的任务,所以,系统稳定是保证控制系统正常工作的必要条件。一个稳定的控制系统其被控量偏离给定值的初始偏差应随时间的增长逐渐减小并趋于零。
快速性:快速性是指系统的动态过程进行的时间长短。过程时间越短,说明系统快速性越好,过程时间持续越长,说明系统响应迟钝,难以实现快速变化的指令信号。稳定性和快速性反映了系统在控制过程中的性能。系统在跟踪过程中,被控量偏离给定值越小,偏离的时间越短,说明系统的动态精度偏高。
准确性:是指系统在动态过程结束后,其被控变量(或反馈量)对给定值的偏差而言,这一偏差即为稳态误差,它是衡量系统稳态精度的指标,反映了动态过程后期的性能。

在实践生产工程中,不同的控制系统对控制器效果的要求不一样。比如平衡车倒立摆对系统的快速性要求很高,响应太慢会导致系统失控。智能家居里面的门窗自动开合系统,对快速性要求就不高,但是对稳定性和准确性的要求就很高,所以需要严格控制系统的超调量和静差。

这篇关于PWM驱动电机系列——PID控制 (各电机设备之间的驱动差异及区别)自动控制系统的性能指标的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099587

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo