Arduino PID库 (1)– 简介

news2024/9/21 18:30:47

Arduino PID库 (1)– 简介

  • pid内容索引-CSDN博客
  • pid术语及整定原则
  • 参考:手把手教你看懂并理解Arduino PID控制库——引子)
  • 库的改进QuickPID-sTune库

原文地址

随着新的Arduino PID库的发布,最后一个库虽然很可靠,但并没有真正附带任何代码解释。这一次的计划是详细解释为什么代码是这样的。我希望这对两种人有用:

  • 详细解析Arduino PID库内部发生的事情。
  • 任何编写自己的PID算法的人都可以看看我是如何做的。

这将是一个艰难的过程,但我想我找到了一种不太痛苦的方式来解释我的代码。我将从我所谓的“初学者的PID”开始。然后,我将逐步改进它,直到得到一个高效、健壮的 pid 算法。

初学者的PID

这是每个人第一次学习的PID方程:
O u t p u t = K p e ( t ) + K I ∫ e ( t ) d t + K D d d t e ( t ) e = S e t p o i n t − I n p u t Output = K_pe(t)+K_I \int e(t) dt +K_D\frac{d}{dt}e(t) \\ \\ e=Setpoint - Input Output=Kpe(t)+KIe(t)dt+KDdtde(t)e=SetpointInput

在这里插入图片描述

几乎每个人都编写以下PID控制器:

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastErr;
double kp, ki, kd;
void Compute()
{
   /*How long since we last calculated*/
   unsigned long now = millis();
   double timeChange = (double)(now - lastTime);
  
   /*Compute all the working error variables*/
   double error = Setpoint - Input;
   errSum += (error * timeChange);
   double dErr = (error - lastErr) / timeChange;
  
   /*Compute PID Output*/
   Output = kp * error + ki * errSum + kd * dErr;
  
   /*Remember some variables for next time*/
   lastErr = error;
   lastTime = now;
}
  
void SetTunings(double Kp, double Ki, double Kd)
{
   kp = Kp;
   ki = Ki;
   kd = Kd;
}

其中,Compute() 在需要进行PID控制量计算的时候被调用,在这样的代码支持下,PID控制可以工作得很好。但是,如果是一个性能较强的工业控制器,还需要考虑一下几个问题:

  1. sample time采样时间 - 改变采样时间会带来怎样的后果
  2. Derivative Kick - 突然改变设定值或者微分时间,如何避免过冲
  3. On-The-Fly Tuning Changes即时调谐更改 - 一个好的PID算法是可以在不干扰内部工作的情况下更改调谐参数的算法。
  4. Reset Windup Mitigation重置清盘缓解 - 我们将介绍什么是重置清盘,并实施具有副作用的解决方案
  5. on/off(Auto/Manual开/关(自动/手动)- 在大多数应用中,有时希望关闭PID控制器并手动调整输出,而不会干扰控制器
  6. Initialization初始化– 当控制器首次打开时,我们想要“无颠簸传输”。也就是说,我们不希望输出突然猛地跳动到某个新值。
  7. Controller Direction控制器方向 – 最后一个并不是健壮性名称本身的更改。 它旨在确保用户输入具有正确符号的优化参数。
  8. NEW: Proportional on Measurement新:测量比例 – 添加此功能可以更轻松地控制某些类型的进程

一旦我们解决了所有这些问题,我们将拥有一个可靠的PID算法。我们还将拥有在最新版本的Arduino PID库中使用的代码。因此,无论您是尝试编写自己的算法,还是尝试了解PID库中发生的事情,我都希望对您有所帮助。让我们开始吧。

库代码下载地址
更新:在所有代码示例中,我都使用双精度。在Arduino上,双精度与浮点数相同(单精度)。真正的双精度对于PID来说是矫枉过正的。如果您使用的语言确实是双精度,我建议将所有双精度更改为浮点数。

代码注释

PID_v1.h

#ifndef PID_v1_h
#define PID_v1_h
#define LIBRARY_VERSION	1.1.1

class PID
{
public:
	//Constants used in some of the functions below
	// 这里定义的两个变量分别指代两种工作模式:AUTOMATIC 对应 PID控制开启; MANUAL 对应PID控制关闭
	#define AUTOMATIC	1
	#define MANUAL	0
	
	// 这里定义两个变量分别代表【控制量与被控量】方向:DIRECT 对应两者同向; REVERSE 对应两者反向
	// 其中同向指: 如果控制量增大,那么被控量也会增大;反之亦然。
	// 其中反向指: 如果控制量增大,那么被控量缺减小;反之亦然。
	#define DIRECT  0
	#define REVERSE  1

	//commonly used functions **************************************************************************
	//构造函数
	PID(double*, double*, double*,        // * constructor.  links the PID to the Input, Output, and 
		double, double, double, int);     //   Setpoint.  Initial tuning parameters are also set here

	// 设置自动模式还是手动模式,两者区别目前还未清楚
	void SetMode(int Mode);               // * sets PID to either Manual (0) or Auto (non-0)

	// 计算PID, 在每个计算周期都应当调用 ,计算频率和是否计算可以在setMode和SetSampleTime中指定
	bool Compute();               // *执行 PID 计算,每次循环 loop() 时都应调用它。
	               //可以使用 SetMode SetSampleTime 分别设置开/关和计算频率。

	//将输出限制在特定范围内。默认值为 0-255,但用户可能会根据应用需要进行更改
	void SetOutputLimits(double, double); //clamps the output to a specific range. 0-255 by default, but
										  //it's likely the user will want to change this depending on
										  //the application



	//available but not commonly used functions ********************************************************
	// 设定P、I、D参数,可以在运行的时间周期内,指定运行需要的参数
	void SetTunings(double, double, double);       // * While most users will set the tunings once in the 
		        	                                          //   constructor, this function gives the user the option
							 								 //   of changing tunings during runtime for Adaptive control

	// 设定控制器的方向,限制输出的正反向,仅需要在开始的时候设置一次
	void SetControllerDirection(int);	  // * Sets the Direction, or "Action" of the controller. DIRECT
										  //   means the output will increase when error is positive. REVERSE
										  //   means the opposite.  it's very unlikely that this will be needed
										  //   once it is set in the constructor.

	// 采样周期,以毫秒作为设置单位,默认为100
	void SetSampleTime(int);              // * sets the frequency, in Milliseconds, with which 
										  //   the PID calculation is performed.  default is 100



	 //Display functions ****************************************************************
	// 获取PID运行参数
	double GetKp();						  // These functions query the pid for interal values.
	double GetKi();						  //  they were created mainly for the pid front-end,
	double GetKd();						  // where it's important to know what is actually 
	
	// 获取运行模式
	int GetMode();						  //  inside the PID.
	
	//获取PID 方向
	int GetDirection();					  //

private:
	// 此函数初始化,完成从手动模式到自动模式的无缝切换
	void Initialize();
	
    // 保留用户输入的参数格式,以便显示
	double dispKp;				// * we'll hold on to the tuning parameters in user-entered 
	double dispKi;				//   format for display purposes
	double dispKd;				//

	double kp;                  // * (P)roportional Tuning Parameter
	double ki;                  // * (I)ntegral Tuning Parameter
	double kd;                  // * (D)erivative Tuning Parameter

	int controllerDirection;

	// 其中包含了INput、 OUTput以及setPoint
	double *myInput;              // * Pointers to the Input, Output, and Setpoint variables
	double *myOutput;             //   This creates a hard link between the variables and the 
	double *mySetpoint;           //   PID, freeing the user from having to constantly tell us
								  //   what these values are.  with pointers we'll just know.
								  
	// 此3个参数需要参考CPP才知道		  
	unsigned long lastTime;
	double ITerm, lastInput;

	unsigned long SampleTime;
	double outMin, outMax;
	
	// 是否自动参数的标志
	bool inAuto;
};
#endif

PID_v1.cpp

/**********************************************************************************************
 * Arduino PID Library - Version 1.1.1
 * by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
 * This Library is licensed under a GPLv3 License
 **********************************************************************************************/
#include "PID_v1.h"

/*Constructor (...)*********************************************************
 *    The parameters specified here are those for for which we can't set up 
 *    reliable defaults, so we need to have the user set them.
 *  这里指定的参数是我们无法设置可靠的默认值参数,因此需要由用户来设置。
 ***************************************************************************/
PID::PID(double* Input, double* Output, double* Setpoint,
        double Kp, double Ki, double Kd, int ControllerDirection)
{
	// 赋值控制量、被控量及设定值初始地址,注意这里是地址
    myOutput = Output;
    myInput = Input;
    mySetpoint = Setpoint;
    
	// 初始化auto模式为false
	inAuto = false;
	
	// 默认控制量限制在0到255,此函数可以根据实际系统需要修改控制量输出限制范围
	PID::SetOutputLimits(0, 255);				//default output limit corresponds to 
												//the arduino pwm limits
	// 默认采样周期为100ms,同样可以根据需求修改
    SampleTime = 100;							//default Controller Sample Time is 0.1 seconds

	// 设置输出的方向
    PID::SetControllerDirection(ControllerDirection);
	// 设置PID 控制参数
    PID::SetTunings(Kp, Ki, Kd);

	// 用于存储PID构造时,对应的系统运行时间
	// millis()作用是获取当前系统运行时间(单位ms),此函数针对arduino;移植到别的系统,可以其他类似作用函数替代
	// 这里减去SampleTime是为了保证在构造后能马上进行PID控制,而不需要等待到下一个SampleTime周期
    lastTime = millis()-SampleTime;	
}
 
 
/* Compute() **********************************************************************
 *     This, as they say, is where the magic happens.  this function should be called
 *   every time "void loop()" executes.  the function will decide for itself whether a new
 *   pid Output needs to be computed.  returns true when the output is computed,
 *   false when nothing has been done.
 *   此函数用于PID控制量计算,函数可以频繁的在进程中被调用。
 **********************************************************************************/ 
bool PID::Compute()
{
	// 如果没有开启PID返回失败,退出;控制量不变,仍为上一次控制量
   if(!inAuto) return false;
   
   // 获取当前系统运行时间并求出相对上一次计算时间间隔
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
   
   // 如果时间间隔大于或者等于采样时间,那么则计算,否则不满足采样条件,计算失败,退出;
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
	   // 保存当前被控量,如果是一个实时控制系统,此时被控量可能与构造时的被控量不一致
	  double input = *myInput;
	  
	  // 求出设定值与当前被控量之间的偏差
      double error = *mySetpoint - input;
      
	  // 计算积分项 此处积分项和标准PID控制方程略微有差距
      ITerm+= (ki * error);
      
	  // 如果 积分项超过最大限制,那么设置积分项为最大限制;同样,最小限制也做同样处理
	  // 此处为何这么做一句两句说不清楚,主要是为了PID 控制量长时间超限后,突然降低设定值,能够让系统马上反应而不会产生一个时间滞后。
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;

	  // 求出两个被控量之间偏差,也就是在计算周期(这里不用采用周期是因为计算周期可能会超过采样周期)被控量的变化。
	  // 总的来说是为了防止控制量和被控量突变
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
	  // PID 调节算式,这就不需要说明了
      double output = kp * error + ITerm- kd * dInput;

      // 这里做限制和ITerm做限制的作用是一样的。。
	  if(output > outMax) output = outMax;
      else if(output < outMin) output = outMin;
      
	  *myOutput = output;
	  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
	  return true;
   }
   else return false;
}


/* SetTunings(...)*************************************************************
 * This function allows the controller's dynamic performance to be adjusted. 
 * it's called automatically from the constructor, but tunings can also
 * be adjusted on the fly during normal operation
 * 此函数用于设定PID调节参数
 ******************************************************************************/ 
void PID::SetTunings(double Kp, double Ki, double Kd)
{
	// 如果PID参数中有小于0的参数,那么设定失败,直接退出,仍然沿用原来的参数
   if (Kp<0 || Ki<0 || Kd<0) return;
	// 仅做显示用。
   dispKp = Kp; dispKi = Ki; dispKd = Kd;
   
   // 获取采样时间,由ms转为s
   double SampleTimeInSec = ((double)SampleTime)/1000;  
   
   // 调整PID参数, I 和 D 参数的调节主要是为了满足采样周期改变导致的影响,
   // 主要是 积分项和 微分项是和时间有关的参数,所以采样周期改变会导致这两项需要重新计算,
   // 这里为了减少这些工作,通过采样周期变换,转换ID参数变化
   // 至于为什么可以这么做,是因为前面做了特殊处理,修改了PID标准表达式,使每一次计算对历史依赖较小
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
 
	//  设定PID调节方向
  if(controllerDirection ==REVERSE)
   {
      kp = (0 - kp);
      ki = (0 - ki);
      kd = (0 - kd);
   }
}
  
/* SetSampleTime(...) *********************************************************
 * sets the period, in Milliseconds, at which the calculation is performed	
 ******************************************************************************/
//更新新的采样时间,同时按照比例更新ID参数
void PID::SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}
 
/* SetOutputLimits(...)****************************************************
 *     This function will be used far more often than SetInputLimits.  while
 *  the input to the controller will generally be in the 0-1023 range (which is
 *  the default already,)  the output will be a little different.  maybe they'll
 *  be doing a time window and will need 0-8000 or something.  or maybe they'll
 *  want to clamp it from 0-125.  who knows.  at any rate, that can all be done
 *  here.
 * 此函数容易产生控制量的突变,在运行过程中,尽量不要缩小范围。
 * 这个函数的使用频率要远远高于 SetInputLimits。
 * 虽然控制器的输入一般在 0-1023 范围内(这已经是默认值了),
 * 但输出会有些不同。
 **************************************************************************/
void PID::SetOutputLimits(double Min, double Max)
{
	// 赋值限制
   if(Min >= Max) return;
   outMin = Min;
   outMax = Max;
 
   if(inAuto)
   {
	   if(*myOutput > outMax) *myOutput = outMax;
	   else if(*myOutput < outMin) *myOutput = outMin;
	 
	   if(ITerm > outMax) ITerm= outMax;
	   else if(ITerm < outMin) ITerm= outMin;
   }
}

/* SetMode(...)****************************************************************
 * Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
 * when the transition from manual to auto occurs, the controller is
 * automatically initialized
 * 允许将控制器模式设置为手动(0)或自动(非 0),当从手动转换为自动时,控制器将自动初始化
 ******************************************************************************/ 
void PID::SetMode(int Mode)
{
    bool newAuto = (Mode == AUTOMATIC);
	// 如果模式不一样,那么则重新初始化
    if(newAuto == !inAuto)
    {  /*we just went from manual to auto*/
        PID::Initialize();
    }
    inAuto = newAuto;
}
 
/* Initialize()****************************************************************
 *	does all the things that need to happen to ensure a bumpless transfer
 *  from manual to automatic mode.
 * 完成从手动模式到自动模式的无缝切换
 ******************************************************************************/ 
void PID::Initialize()
{
   ITerm = *myOutput;
   lastInput = *myInput;
   if(ITerm > outMax) ITerm = outMax;
   else if(ITerm < outMin) ITerm = outMin;
}

/* SetControllerDirection(...)*************************************************
 * The PID will either be connected to a DIRECT acting process (+Output leads 
 * to +Input) or a REVERSE acting process(+Output leads to -Input.)  we need to
 * know which one, because otherwise we may increase the output when we should
 * be decreasing.  This is called from the constructor.
 * PID 要么正向作用过程(+输出导致+输入),
 * 要么连接到反向作用过程(+输出导致-输入),我们需要知道是哪一个,
 * 否则可能会在本应减少输出的情况下增加输出。
 * 在构造函数中调用此函数。
 ******************************************************************************/
void PID::SetControllerDirection(int Direction)
{
   if(inAuto && Direction !=controllerDirection)
   {
	  kp = (0 - kp);
      ki = (0 - ki);
      kd = (0 - kd);
   }   
   controllerDirection = Direction;
}

/* Status Funcions*************************************************************
 * Just because you set the Kp=-1 doesn't mean it actually happened.  these
 * functions query the internal state of the PID.  they're here for display 
 * purposes.  this are the functions the PID Front-end uses for example
 ******************************************************************************/
double PID::GetKp(){ return  dispKp; }
double PID::GetKi(){ return  dispKi;}
double PID::GetKd(){ return  dispKd;}
int PID::GetMode(){ return  inAuto ? AUTOMATIC : MANUAL;}
int PID::GetDirection(){ return controllerDirection;}

采样时间

参考:手把手教你看懂并理解Arduino PID控制库——采样时间
(这是关于编写固定 PID 算法的更大系列)

问题所在

一般来说,PID 控制都是周期性调用(也就是意味着,每次计算的间隔都是固定的常量),但或多或少,由于各种需求会被奇葩的非周期调用。如果非得修改采样时间,对 PID 控制进行非周期调用,那么这样会导致以下问题:

观察这个 “可恶” 的方程:
O u t p u t = k p e ( t ) + K I ∫ e ( t ) d t + K D d d t e ( t ) e = S e t p o i n t − I n p u t Output = k_pe(t)+K_I \int e(t) dt +K_D\frac{d}{dt}e(t) \\ \\ e=Setpoint - Input Output=kpe(t)+KIe(t)dt+KDdtde(t)e=SetpointInput

如果采样时间变化了,那么对于积分项和微分项(也就是 KI 和 KD 对应的项),这两项是和采样时间间隔有关的,那么则需要进行额外的微积分运算 (不能再按照原来写好的代码进行运算,必须对时间参数进行调整)。

初学者的PID被设计为不规则地调用。这会导致 2 个问题:

  • 您不会从 PID 获得一致的行为,因为有时它经常被调用,有时则不然。
  • 你需要做额外的计算导数和积分,因为它们都依赖于时间的变化。

解决方案

确保定期调用 PID。这样做的方法是指定每个周期调用计算函数。根据预先确定的采样时间,PID 决定是否应立即计算或返回。

一旦我们知道PID正在以恒定的间隔进行评估,也可以简化导数和积分计算。

《代码》

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastErr;
double kp, ki, kd;
int SampleTime = 1000;  //1 sec

void Compute() {
  unsigned long now = millis();
  int timeChange = (now - lastTime);
  if (timeChange >= SampleTime) {
    /*Compute all the working error variables*/
    double error = Setpoint - Input;
    errSum += error;
    double dErr = (error - lastErr);

    /*Compute PID Output*/
    Output = kp * error + ki * errSum + kd * dErr;

    /*Remember some variables for next time*/
    lastErr = error;
    lastTime = now;
  }
}

void SetTunings(double Kp, double Ki, double Kd) {
  double SampleTimeInSec = ((double)SampleTime) / 1000;
  kp = Kp;
  ki = Ki * SampleTimeInSec;
  kd = Kd / SampleTimeInSec;
}

void SetSampleTime(int NewSampleTime) {
  if (NewSampleTime > 0) {
    double ratio = (double)NewSampleTime
                   / (double)SampleTime;
    ki *= ratio;
    kd /= ratio;
    SampleTime = (unsigned long)NewSampleTime;
  }
}

在第 10 行和第 11 行,算法现在自行决定是否需要计算。此外,因为现在知道样本之间的时间是相同的,所以我们不需要不断地乘以时间变化。我们只要适当地调整 Ki 和 Kd(第 29 和30 行),结果在数学上是等价的,但效率更高。

不过,这样做有点问题。如果用户决定在操作过程中更改采样时间,则需要重新调整Ki和Kd以反映此新更改。这就是第 35-39 行的全部内容。

另请注意,我将第 27 行的采样时间转换为秒。严格来说,这不是必需的,但允许用户以 1/秒为单位输入,而不是 1/mS

观察 SetTunings 和 SetSampleTime 两个函数,这两个函数完成了适应采样间隔改变功能。其中 SetSampleTime 在采样间隔改变后,按照比例放大 / 缩小了与采样间隔改变相同的倍数。在 SetTunings 做归一化处理。为什么这里只对 Ki 和 Kd 进行处理在前面已经说过了。那么大家可能又会存在这样的疑问:

如果 Ki 变化了,那么和经典的 PID 控制公式结果不是会差很大吗?答案是差别不大!!

观察积分项,并改写为离散形式:
在这里插入图片描述

如果在调节过程中,Ki 不是一个常量的话,那么可以进一步改写为:

在这里插入图片描述

上式的第一项分别观察 Ki 及 e (t),改变采样间隔后,e (t) 受采样间隔变化影响,产生对应时间内的变化,而 ki 等比例反向放大 / 缩小,效果相当。

结果

上述更改为我们做了 3 件事

  1. 无论调用 Compute() 的频率如何,PID 算法都将定期评估 [第 11 行]
  2. 由于时间减法 [第 10 行],当 millis() 返回 0 时不会有问题。这每 55 天才会发生一次,但我们要防止还记得吗?
  3. 我们不再需要乘以时间变化。由于它是一个常量,我们可以将其从计算代码 [第 15+16 行] 中移出,并将其与调参常量 [第 31+32 行] 混为一谈。从数学上讲,它的工作原理相同,但是每次计算PID时都会节省乘法和除法

关于中断的旁注

如果这个PID进入微控制器,则可以为使用中断提出一个很好的论据。SetSampleTime 设置中断频率,然后在需要时调用 Compute。在这种情况下,不需要第 9-12、23 和 24 行。如果您打算用PID影响来做到这一点,那就去做吧!不过,请继续阅读本系列。希望您仍然可以从随后的修改中获得一些好处。
我没有使用中断有三个原因

  1. 就本系列而言,并不是每个人都能使用中断。
  2. 如果您希望它同时实现许多PID控制器,事情会变得棘手。

example

  1. Arduino PID 库 - 亮度控制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1977292.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈AC自动机算法(c++)

文章目录 自动机一些简单的自动机&#xff1a; AC 自动机字典树构建失配指针构建指针 [HNOI2006] 最短母串问题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路AC代码 「一本通 2.4 例 1」Keywords SearchAC代码 自动机 自动机是什么&#xff1f; 自动机的作…

Linux小组件:gcc

gcc 是C语言的编译器&#xff0c;在Linux下我们也用这个编译C语言 安装gcc sudo apt install build-essential 查看gcc版本信息 gcc --version 有时候会出现代码编译不过去的问题&#xff0c;通常可能是gcc的编译标准太低&#xff0c;不支持某些写法 比如在很多旧的编译标…

rk3588 部署yolov8.rknn

本文从步骤来记录在rk3588芯片上部署yolov8模型 主机&#xff1a;windows10 VMware Workstation 16 Pro 硬件&#xff1a;RK3588 EVB板 模型&#xff1a; RK3588.rknn 软件开发环境&#xff1a; c cmake step1: 主机上执行&#xff1a; 将rknn_model_zoo 工程文件下载…

spring:标签property

标签property对应于bean类公开的JavaBean setter方法。标签property的属性中&#xff0c;name为属性名&#xff0c;type为“”引号里面的类型&#xff0c;use为是否必须出现。 1.ref引用一个已经存在的对象,value创建一个新的对象 2.value可以赋一些简单类型的值&#xff0c;…

【MySQL】常用数据类型

目录 数据类型 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 float decimal 字符串类型 char varchar 日期和时间类型 enum和set 数据类型 数据类型分类 数值类型 tinyint类型 tinyint类型只占用一个字节类似于编程语言中的字符char。有带符号和无符号两…

【系统架构设计师】二十四、安全架构设计理论与实践②

目录 三、系统安全体系架构规划框架 3.1 信息系统安全体系规划 3.2 信息系统安全规划框架 3.2.1 信息系统安全规划依托企业信息化战略规划 3.2.2 信息系统安全规划需要围绕技术安全、管理安全、组织安全考虑 3.2.3 信息系统安全规划以信息系统与信息资源的安全保护为核心…

Java——多线程(6/9):线程池、处理Runnable、Callable任务(认识线程池-线程池的工作原理,ThreadPoolExecutor构造器)

目录 认识线程池 介绍 线程池的工作原理 如何创建线程池 介绍 ThreadPoolExecutor构造器 代码实例 线程池的注意事项 线程池处理Runnable任务 ExecutorService的常用方法 代码实例 新任务拒绝策略 线程池处理Callable任务 ExecutorService的常用方法 代码实例…

二叉树的前序遍历 - 力扣(LeetCode)C语言

144. 二叉树的前序遍历 - 力扣&#xff08;LeetCode&#xff09;(点击前面链接即可查看题目) 一、题目 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; …

Datawhale AI 夏令营——AI+逻辑推理——Task4

# Datawhale AI 夏令营 夏令营手册&#xff1a;从零入门 AI 逻辑推理 比赛&#xff1a;第二届世界科学智能大赛逻辑推理赛道&#xff1a;复杂推理能力评估 代码运行平台&#xff1a;魔搭社区 赛题任务 本次任务主要采用大语言模型解决推理任务&#xff0c;如何使用大语言模…

Python3 第六十一课 -- 实例三十

目录 一. 堆排序 二. 计数排序 一. 堆排序 堆排序&#xff08;Heapsort&#xff09;是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构&#xff0c;并同时满足堆积的性质&#xff1a;即子结点的键值或索引总是小于&#xff08;或者大于&#xff…

Yolov8在RK3588上进行自定义目标检测(二)

best.pt转best.onnx Yolov8在RK3588上进行自定义目标检测(一)已经进行了配置文件修改。接下来可以直接进行模型的转换。 下面是两种转换方法&#xff1a; 1.命令行 yolo export modelbest.pt formatrknn 2.转换脚本 convert_to_onnx.py from ultralytics import YOLOmode…

数据求均值背后的原理 - 最小二乘法

1. 背景 对采集数据求均值是一种常见简单有效的数据处理手段&#xff0c;比如用直尺去测量物体的长度一般情况会多次测量然后计算平均值然后将平均值作为物体的长度&#xff0c;又如我们需要测量某电源的电压也会采取类似的方法&#xff0c;可以说对数据求均值在我们工作生活中…

【时时三省】unity test 测试框架 介绍(适用于C语言进行测试的)

1&#xff0c;关于 unity test 测试框架的介绍 unity test 是 ThrowTheSwitch.org 的一个主要工程。它是专注于为嵌入式工具链而生的C语言单元测试框架。它可以适用于大工程或者小工程都可以。它的核心文件是一个.c文件和两个头文件。 备注&#xff1a; 下载源码地址&#xff…

btslab靶场-通过xss获取他人cookie并利用

目录 安装 通过xss获取cookie cookie利用 安装 下载btslab靶场链接&#xff1a;https://pan.baidu.com/s/1I9ZgzlZEWdobINGQUhy7Jw?pwd8888 提取码&#xff1a;8888 用phpEnv或者phpStudy部署好靶场环境&#xff08;这里就省略了&#xff09; 通过xss获取cookie 先访问…

Apache和nginx!!!!

⼀、Apache 概念 1、概述 最早的 web 服务程序&#xff0c;基于 http 协议提供⽹⻚浏览服务。 2、特点 模块化设置、开放源代码、跨平台应⽤、⽀持多种 web 编程语 ⾔、运⾏稳定。 3、⼯作模式 &#xff08;1&#xff09;Prefork&#xff1a;使⽤进程处理请求&#xff0…

操作系统|day2.进程、线程、协程

文章目录 进程概念特点并行和并发进程之间的通信进程的状态进程的调度基本准则调度方式具体算法 特殊进程 线程概念线程状态转换线程状态线程调度线程同步多线程通信 线程池种类工作流程五种状态拒绝策略参数队列大小 协程概念优势 进程 概念 进程就是正在运行的程序,它会占用…

进阶SpringBoot之 yaml 语法

SpringBoot 使用一个全局的配置文件&#xff0c;名字固定 application.properties 语法结构&#xff1a;keyvalue application.yml 语法结构&#xff1a;key&#xff1a;&#xff08;空格&#xff09;value 配置文件的作用是可以修改 SpringBoot 自动配置的默认值 在 res…

【NOI-题解】1022. 百钱百鸡问题1024. 购买文具1249. 搬砖问题1250. 马克思手稿的问题1342. 怎样种树?

文章目录 一、前言二、问题问题&#xff1a;1022. 百钱百鸡问题问题&#xff1a;1024. 购买文具问题&#xff1a;1249. 搬砖问题问题&#xff1a;1250. 马克思手稿的问题问题&#xff1a;1342. 怎样种树&#xff1f; 三、感谢 一、前言 欢迎关注本专栏《C从零基础到信奥赛入门…

无心剑小诗《郑钦文,为您骄傲》

郑钦文&#xff0c;为您骄傲 在赛场上如猎豹出击 每一拍都交织着力量与智慧 郑钦文&#xff0c;您是无畏的勇士 曾经的挫折是砥砺的砂石 今日的辉煌&#xff0c;是拼搏的勋章 今晚&#xff0c;红土上您书写传奇 战胜强敌&#xff0c;您气势如虹 汗水与激情洒满整个赛场 梦想…

49 序列解包的多种形式和用法

序列解包&#xff08;Sequence Unpacking&#xff09;是 Python 中非常重要和常用的一个功能&#xff0c;可以使用非常简洁的形式完成复杂的功能&#xff0c;提高了代码的可读性&#xff0c;减少了程序员的代码输入量。 x, y, z 1, 2, 3 # 多个变量同时赋值 v_tuple (False…