PID 算法
1.1 概述
比例(Proportion)积分(Integral)微分(Differential)控制器(PID控制器或三项控制器)是一种采用反馈的控制回路机制,广泛应用于工业控制系统和需要连续调制控制的各种其他应用。
PID控制器连续计算误差值 e(t) 作为所需设定点(SP) 和测量过程变量(PV)之间的差值,并应用基于比例、积分和导数项(分别表示为P、I和D)的校正,因此得名。
r(t) 是期望的过程值或设定点(SP),y(t) 是测量的过程值(PV)。
1.2 历史发展
1911年,第一个PID控制器是由Elmer Sperry开发的。
1922 年,俄裔美国工程师尼古拉斯·米诺斯基 ( Nicolas Minorsky)才首次利用理论分析制定了我们现在所说的 PID 或三项控制的正式控制律。米诺斯基当时正在为美国海军研究和设计自动船舶转向系统,他的分析基于对舵手的观察。
他指出,舵手不仅根据当前航向误差,还根据过去的误差以及当前的变化率来驾驶船舶;然后 Minorsky 对此进行了数学处理。他的目标是稳定,而不是一般控制,这大大简化了问题。
1933年,TIC(泰勒仪器公司)实现了完全可调节的前气动控制器。几年后,控制工程师通过将末端返回到一些假值,直到误差不为零,消除了比例控制器中发现的稳态误差。这个返回包含了误差,这被称为比例积分控制器。
1940年,第一个气动PID控制器通过导数动作开发,以减少超调问题。
1942年,Ziegler & Nichols引入了调谐规则,由工程师发现和设置PID控制器的合适参数。
20世纪50年代中期,自动PID控制器在工业上得到了广泛的应用。工业中大多数现代 PID 控制都是作为DCS、PLC 或单片机程序来实现的。
1.3 应用
•火箭的姿态控制
•无人机悬停控制等
•相机稳定器、相机云台
•平衡小车
•汽车的定速巡航控制、转向控制
•发动机转速控制
•3D打印机上的温度控制器
•工业自动化领域,大约95%的闭环操作使用PID控制器。
1.4 与 ON/OFF 型控制器对比
像PID控制器这样的闭环系统包括一个反馈控制系统。该系统利用一个固定点对反馈变量进行评估,从而产生误差信号。在此基础上,它改变系统输出。这个过程将继续,直到误差达到零,否则反馈变量的值就等于一个固定点。
与ON/OFF型控制器相比,该控制器提供了良好的效果。在开/关型控制器中,只需两个条件即可管理系统。大多数暖通空调系统、冰箱都采用这种方法。
例如,在冰箱中,它会冷却内部直到达到所需温度,然后关闭冷却器,直到达到高于所需温度的设定值。一旦工艺值低于固定点,则开启。
类似地,一旦该值高于固定值,它将关闭。这种控制器的输出不稳定,在不动点的区域内振荡频繁。然而,与ON/OFF型控制器相比,PID 控制器更加稳定和准确。
1.6 响应类型
Introduction to PID:
https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/introduction-to-pid.html
由PID控制器驱动的系统通常具有三种类型的响应:欠阻尼、过阻尼和临界阻尼。
•欠阻尼响应在稳定之前围绕参考值振荡。
•过阻尼响应上升缓慢并且不会超过参考值。
•临界阻尼响应具有最快的上升时间,且不会超过参考值。
公式
2.1 PID 系统定义与公式
r(t) setpoint, reference,是期望的过程值或设定值(SP);
y(t) output, process variable,是测量的过程值,输出值(PV);
e(t) error,是偏差;
u(t) control effort,是控制量;
PID控制器的显着特点是能够利用比例、积分和微分这三个控制项对控制器输出的影响来进行精确和最优的控制。
PID 控制器,不断计算误差值e(t) 作为所需设定点之间的差异SP=r(t) 和测量的过程变量PV=y(t):e(t)=r(t)−y(t) ,并应用基于比例、积分和导数项的修正。
控制器尝试通过调整控制变量来最小化随时间变化的误差u(t)。manipulated variable (MV)。
From:
http://matlab.fei.tuke.sk/orhs/subory/podklady/pid_controller_slidy.pdf
2.2 PID 数字公式
由于计算机控制是一种采样控制,它只能根据采样时刻的偏差计算控制量,而不能像模拟控制那样连续输出控制量,进行连续控制。由于这一特点,(式 1-1)中的积分项和微分项不能直接使用,必须进行离散化处理。
离散化处理的方法为:以τ作为采样周期,k作为采样序号,则离散采样时间kτ对应着连续时间t,用矩形法数值积分近似代替积分,用一阶后向差分近似代替微分,可作如下近似变换:
2.3 位置式 PID 算法
将(式 2-1)代入(式 1-1),就可以得到离散的 PID 表达式为
将(式 2-1)代入(式 1-2),就可以得到离散的PID 表达式为
积分系数、微分系数做如下替换:
注意:必须使τ为定值,或者变化小到可以忽略,这样P、I、D才是固定常数,才可能调节
2.4 增量式 PID 算法
增量式 PID 控制算法可以通过(式 2-2)推导出。由(式 2-2)可以得到控制器的第 k-1 个采样时刻的输出值为:
由(式 2-3)可以得到控制器的第 k-1 个采样时刻的输出值为:
用(式 2-3)减去(式 2-7)相减并整理,就可以得到增量式 PID 控制算法公式:
由(式 2-8)可以看出,如果计算机控制系统采用恒定的采样周期τ,一旦确定 A、 B、 C,只要使用前后三次测量的偏差值,就可以由(式 2-8)求出控制量。
增量式 PID 控制算法与位置式 PID 算法(式 2-3)相比,只需要保持当前时刻以前三个时刻的偏差值即可,累计误差较小,计算量小的多,因此在实际中得到广泛的应用。
而位置式 PID 控制算法也可以通过增量式控制算法推出递推计算公式:
(式 2-9)就是目前在计算机控制中广泛应用的数字递推 PID 控制算法。
调试技巧
代码实现
python
From:
https://blog.csdn.net/weixin_43863487/article/details/124604299
import numpy as np
import matplotlib.pyplot as plt
class PositionPID(object):
"""位置式PID算法实现"""
def __init__(self, target, cur_val, dt, max, min, p, i, d) -> None:
self.dt = dt # 循环时间间隔
self._max = max # 最大输出限制,规避过冲
self._min = min # 最小输出限制
self.k_p = p # 比例系数
self.k_i = i # 积分系数
self.k_d = d # 微分系数
self.target = target # 目标值
self.cur_val = cur_val # 算法当前PID位置值,第一次为设定的初始位置
self._pre_error = 0 # t-1 时刻误差值
self._integral = 0 # 误差积分值
def calculate(self):
"""
计算t时刻PID输出值cur_val
"""
error = self.target - self.cur_val # 计算当前误差
# 比例项
p_out = self.k_p * error
# 积分项
self._integral += (error * self.dt)
i_out = self.k_i * self._integral
# 微分项
derivative = (error - self._pre_error) / self.dt
d_out = self.k_d * derivative
# t 时刻pid输出
output = p_out + i_out + d_out
# 限制输出值
if output > self._max:
output = self._max
elif output < self._min:
output = self._min
self._pre_error = error
self.cur_val = output
return self.cur_val
def fit_and_plot(self, count = 200):
"""
使用PID拟合setPoint
"""
counts = np.arange(count)
outputs = []
for i in counts:
outputs.append(self.calculate())
print('Count %3d: output: %f' % (i, outputs[-1]))
print('Done')
# print(outputs)
plt.figure()
plt.axhline(self.target, c='red')
plt.plot(counts, np.array(outputs), 'b.')
plt.ylim(min(outputs) - 0.1 * min(outputs), max(outputs) + 0.1 * max(outputs))
plt.plot(outputs)
plt.show()
pid = PositionPID(10, -5, 0.5, 100, -100, 0.2, 0.1, 0.01)
pid.fit_and_plot(150)
c/c++
From:
https://blog.csdn.net/skythinker616/article/details/123019829
//首先定义PID结构体用于存放一个PID的数据
typedef struct
{
float kp,ki,kd;//三个系数
float error,lastError;//误差、上次误差
float integral,maxIntegral;//积分、积分限幅
float output,maxOutput;//输出、输出限幅
}PID;
//用于初始化pid参数的函数
void PID_Init(PID *pid,float p,float i,float d,float maxI,float maxOut)
{
pid->kp=p;
pid->ki=i;
pid->kd=d;
pid->maxIntegral=maxI;
pid->maxOutput=maxOut;
}
//进行一次pid计算
//参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc(PID *pid,float reference,float feedback)
{
//更新数据
pid->lastError=pid->error;//将旧error存起来
pid->error=reference-feedback;//计算新error
//计算微分
float dout=(pid->error-pid->lastError)*pid->kd;
//计算比例
float pout=pid->error*pid->kp;
//计算积分
pid->integral+=pid->error*pid->ki;
//积分限幅
if(pid->integral > pid->maxIntegral) pid->integral=pid->maxIntegral;
else if(pid->integral < -pid->maxIntegral) pid->integral=-pid->maxIntegral;
//计算输出
pid->output=pout+dout+pid->integral;
//输出限幅
if(pid->output > pid->maxOutput) pid->output=pid->maxOutput;
else if(pid->output < -pid->maxOutput) pid->output=-pid->maxOutput;
}
PID mypid;//创建一个PID结构体变量
int main()
{
//...这里有些其他初始化代码
PID_Init(&mypid,10,1,5,800,1000);//初始化PID参数
while(1)//进入循环运行
{
float feedbackValue=...;//这里获取到被控对象的反馈值
float targetValue=...;//这里获取到目标值
PID_Calc(&mypid,targetValue,feedbackValue);//进行PID计算,结果在output成员变量中
设定执行器输出大小(mypid.output);
delay(10);//等待一定时间再开始下一次循环
}
}
单环效果
串级PID的C语言代码
//此处需要插入上面的单级PID相关代码
//串级PID的结构体,包含两个单级PID
typedef struct
{
PID inner;//内环
PID outer;//外环
float output;//串级输出,等于inner.output
}CascadePID;
//串级PID的计算函数
//参数(PID结构体,外环目标值,外环反馈值,内环反馈值)
void PID_CascadeCalc(CascadePID *pid,float outerRef,float outerFdb,float innerFdb)
{
PID_Calc(&pid->outer,outerRef,outerFdb);//计算外环
PID_Calc(&pid->inner,pid->outer.output,innerFdb);//计算内环
pid->output=pid->inner.output;//内环输出就是串级PID的输出
}
CascadePID mypid;//创建串级PID结构体变量
int main()
{
//...其他初始化代码
PID_Init(&mypid.inner,10,0,0,0,1000);//初始化内环参数
PID_Init(&mypid.outer,5,0,5,0,100);//初始化外环参数
while(1)//进入循环运行
{
float outerTarget=...;//获取外环目标值
float outerFeedback=...;//获取外环反馈值
float innerFeedback=...;//获取内环反馈值
PID_CascadeCalc(&mypid,outerTarget,outerFeedback,innerFeedback);//进行PID计算
设定执行机构输出大小(mypid.output);
delay(10);//延时一段时间
}
}
双环效果
双环控制
串联
from:
https://blog.csdn.net/weixin_43058521/article/details/115503356
如果电机控制既要控制速度又要控制位置,因为速度和位置相关,所以需要串联。
并联
from:
https://blog.csdn.net/weixin_43058521/article/details/115503356
姿态角度与速度间无相关性,各自单独算一路控制
示例
循迹小车
https://blog.csdn.net/m0_38106923/article/details/109545445
可见小车的循迹效果。
野火中步进电机位置速度双环控制
https://doc.embedfire.com/motor/motor_tutorial/zh/latest/improve_part/step_motor_double_loop_control.html
步进电机速度环控制实现和 10. 步进电机位置环控制实现介绍了单环控制已经能很好地提高电机的性能了,但是仍有其局限性。
使用速度环精确控制了电机的转速,但是停止的位置难以精确控制;
使用位置环精确控制了电机转过的角度,却不得不人为限制速度来防止堵转。
位置环和速度环双环控制,既实现位置的精确调节又实现速度的自动控制。
该控制下,编码器不仅起到了反馈位置的作用,也起到了反馈速度的作用。
调参技巧:在PID参数整定时,采取先内环再外环的方法,也就是先单独使用速度环控制,得到满意的参数后, 再把位置环套在外面,整定位置环参数,最后根据整体效果对速度环参数进行微调。
bsp_pid.h
/*pid*/
typedef struct
{
float target_val; //目标值
float actual_val; //实际值
float err; //定义当前偏差值
float err_next; //定义下一个偏差值
float err_last; //定义上一个偏差值
float Kp, Ki, Kd; //定义比例、积分、微分系数
}_pid;
bsp_stepper_ctrl.h
/*宏定义*/
/*******************************************************/
#define TIM_STEP_FREQ (SystemCoreClock/TIM_PRESCALER) // 频率ft值
/*电机单圈参数*/
#define STEP_ANGLE 1.8f //步进电机的步距角 单位:度
#define FSPR (360.0f/STEP_ANGLE) //步进电机的一圈所需脉冲数
#define MICRO_STEP 32 //细分器细分数
#define SPR (FSPR*MICRO_STEP) //细分后一圈所需脉冲数
#define PULSE_RATIO (float)(SPR/ENCODER_TOTAL_RESOLUTION)//步进电机单圈脉冲数与编码器单圈脉冲的比值
#define SAMPLING_PERIOD 50 //PID采样频率,单位Hz
#define MOVE_CTRL 0.1f //启用速度环控制量
#define TARGET_DISP 20 //步进电机运动时的目标圈数,单位:转
#define TARGET_SPEED_MAX 800 // 目标速度的最大值
typedef struct {
unsigned char stepper_dir : 1; //步进电机方向
unsigned char stepper_running : 1; //步进电机运行状态
unsigned char MSD_ENA : 1; //驱动器使能状态
}__SYS_STATUS;
bsp_stepper_ctrl.c-增量式PID算法实现-增量式PID
/**
* @brief 增量式PID算法实现
* @param val:当前实际值
* @note 无
* @retval 通过PID计算后的输出
*/
float PID_realize(_pid *pid, float temp_val)
{
/*传入实际值*/
pid->actual_val = temp_val;
/*计算目标值与实际值的误差*/
pid->err=pid->target_val-pid->actual_val;
/*PID算法实现*/
float increment_val = pid->Kp*(pid->err - pid->err_next) + pid->Ki*pid->err + pid->Kd*(pid->err - 2 * pid->err_next + pid->err_last);
/*传递误差*/
pid->err_last = pid->err_next;
pid->err_next = pid->err;
/*返回增量值*/
return increment_val;
}
bsp_stepper_ctrl.c-步进电机位置速度双闭环控制
/**
* @brief 步进电机位置速度双闭环控制
* @retval 无
* @note 基本定时器中断内调用
*/
void Stepper_Ctrl(void)
{
/* 编码器相关变量 */
static __IO float last_count = 0;
__IO float capture_count = 0;
__IO float capture_per_unit = 0;
/* 经过pid计算后的期望值 */
static __IO float speed_cont_val = 0.0f;
static __IO float move_cont_val = 0.0f;
static int cont_val = 0;
/* 当电机运动时才启动pid计算 */
if((sys_status.MSD_ENA == 1) && (sys_status.stepper_running == 1))
{
/* 计算编码器脉冲数 */
capture_count = (int)__HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (encoder_overflow_count * ENCODER_TIM_PERIOD);
/* 计算速度环的传入值 */
capture_per_unit = capture_count - last_count;
last_count = capture_count;
/* 编码器脉冲累计值作为实际值传入位置环pid控制器 */
move_cont_val += PID_realize_move(&move_pid, (float)capture_count);// 进行 PID 计算
/* 判断运动方向 */
move_cont_val > 0 ? (MOTOR_DIR(CW)) : (MOTOR_DIR(CCW));
/* 判断是否启用速度环 */
if (fabsf(move_cont_val) >= MOVE_CTRL)
{
/* 传递位置环计算值,便于计算*/
cont_val = move_cont_val;
/* 目标速度上限处理 */
if (cont_val > TARGET_SPEED_MAX)
{
cont_val = TARGET_SPEED_MAX;
}
else if (cont_val < -TARGET_SPEED_MAX)
{
cont_val = -TARGET_SPEED_MAX;
}
#if defined(PID_ASSISTANT_EN)
int32_t temp = cont_val;
set_computer_value(SEED_TARGET_CMD, CURVES_CH2, &temp, 1); // 给通道 2 发送目标值
#endif
/* 设定速度的目标值 */
set_pid_target(&speed_pid, cont_val);
/* 单位时间内的编码器脉冲数作为实际值传入速度环pid控制器 */
speed_cont_val += PID_realize_speed(&speed_pid, (float)capture_per_unit);// 进行 PID 计算
/* 由于OC_Pulse_num为uint16_t变量,取速度环输出值的绝对值进行后续计算*/
cont_val = fabsf(speed_cont_val);
/* 计算比较计数器的值 */
OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / (cont_val * PULSE_RATIO * SAMPLING_PERIOD))) >> 1;
}
else
{
/* 计算比较计数器的值 */
OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / ((float)move_cont_val * PULSE_RATIO))) >> 1;
}
#if PID_ASSISTANT_EN
int Temp_ch2 = capture_per_unit; // 上位机需要整数参数,转换一下
int Temp_ch1 = capture_count;
set_computer_value(SEED_FACT_CMD, CURVES_CH2, &Temp_ch2, 1); // 给通道 1 发送实际值 // 给通道 2 发送实际值
set_computer_value(SEED_FACT_CMD, CURVES_CH1, &Temp_ch1, 1); // 给通道 1 发送实际值
#else
printf("实际值:%d,目标值:%.0f\r\n", capture_per_unit, pid.target_val);// 打印实际值和目标值
#endif
}
else
{
/*停机状态所有参数清零*/
last_count = 0;
speed_cont_val = 0;
move_cont_val = 0;
speed_pid.actual_val = 0;
speed_pid.err = 0;
speed_pid.err_last = 0;
speed_pid.err_next = 0;
move_pid.actual_val = 0;
move_pid.err = 0;
move_pid.err_last = 0;
move_pid.err_next = 0;
}
}
main
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/* 初始化系统时钟为168MHz */
SystemClock_Config();
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
DEBUG_USART_Config();
printf("欢迎使用野火 电机开发板 步进电机位置速度双环控制 例程\r\n");
printf("按下按键3启动和停止电机\r\n");
/* 初始化时间戳 */
HAL_InitTick(5);
/*按键中断初始化*/
Key_GPIO_Config();
/*led初始化*/
LED_GPIO_Config();
/* 初始化基本定时器定时,20ms产生一次中断 */
TIMx_Configuration();
/* 编码器接口初始化 */
Encoder_Init();
/*步进电机初始化*/
stepper_Init();
/* 上电默认停止电机 */
Set_Stepper_Stop();
/* PID算法参数初始化 */
PID_param_init();
// MOTOR_DIR(CW);
/* 目标位置转换为编码器的脉冲数作为pid目标值 */
move_pid.target_val = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
int32_t Temp = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
#if PID_ASSISTANT_EN
set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0); // 同步上位机的启动按钮状态
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &Temp, 1);// 给通道 1 发送目标值
#endif
while(1)
{
/* 扫描KEY1,启动电机 */
if( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON )
{
#if PID_ASSISTANT_EN
Set_Stepper_Start();
set_computer_value(SEED_START_CMD, CURVES_CH1, NULL, 0);// 同步上位机的启动按钮状态
#else
Set_Stepper_Start();
#endif
}
/* 扫描KEY2,停止电机 */
if( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON )
{
#if PID_ASSISTANT_EN
Set_Stepper_Stop();
set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0);// 同步上位机的启动按钮状态
#else
Set_Stepper_Stop();
#endif
}
/* 扫描KEY3,增大目标位置*/
if( Key_Scan(KEY3_GPIO_PORT,KEY3_PIN) == KEY_ON )
{
/* 目标位置增加48000,对应电机位置增加20圈 */
move_pid.target_val += 48000;
#if PID_ASSISTANT_EN
int temp = move_pid.target_val;
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 给通道 1 发送目标值
#endif
}
/* 扫描KEY4,减小目标位置 */
if( Key_Scan(KEY4_GPIO_PORT,KEY4_PIN) == KEY_ON )
{
/* 目标位置减小48000,对应电机位置减少20圈 */
move_pid.target_val -= 48000;
#if PID_ASSISTANT_EN
int temp = move_pid.target_val;
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 给通道 1 发送目标值
#endif
}
}
}
补充知识点
模拟量数字化
实际数字化应用中,PID 系统中的积分项和微分项需要进行离散化处理。
类似的典型应用有数字示波器。对于数字示波器来说它无法直接量化模拟信号,替代的办法就是持续周期性采样,然后将得到的一系列采样点显示出来,当采样速率越高,显示的图像越真实,这就是数学中极限的与微分的思想。
香农(Shannon) 采样定律
•定理内容
香农取样定理是针对有限带宽函数的,为了不失真地恢复模拟信号,采样频率应该不小于模拟信号频谱中最高频率的2倍。
相关文章:

PID 算法
1.1 概述 比例(Proportion)积分(Integral)微分(Differential)控制器(PID控制器或三项控制器)是一种采用反馈的控制回路机制,广泛应用于工业控制系统和需要连续调制控制的…...

13.Redis 事务
Redis 事务 redis 事务事务操作multi 开启事务exec 执行事务discard 放弃当前事务watchunwatch redis 事务 Redis 的事务和 MySQL 的事务概念上是类似的。 都是把⼀系列操作绑定成⼀组。 让这⼀组能够批量执⾏。 Redis 的事务和 MySQL 事务的区别: 弱化的原⼦性: 这里指的是 …...

李宏毅机器学习课程笔记(更新ing)
CNN 为什么AlphaGo可以用CNN?棋盘抽象成图片时需要注意什么? 首先图片有两个特点: 1,只观察局部就可以显示某种pattern,比如要得出一个鸟嘴的结论,只需要观察局部图片 2,某种pattern可以出现在图…...

SIP mini 对讲终端,带sip热点功能
SV-A10/SV-A10W SIP mini 对讲终端,带sip热点功能 SV-A10/SV-A10W 是专门针对行业用户需求研发的一款 SIP mini 对讲产品,外观小巧,功能 强大,集智能安防、音/视频对讲和广播功能于一体,性价比高。支持壁挂式安装/86…...

PHP中根据出生年月日计算年龄的封装函数
在 PHP 中,你可以使用以下函数来根据出生年月日计算年龄: 封装函数 function calculateAge($birthday) {$currentDate date(Y-m-d);$birthdayDate date(Y-m-d, strtotime($birthday));$age date_diff(date_create($currentDate), date_create($birt…...

Linux巡检脚本
做运维的朋友都知道,大家经常要对一些系统做月度维护或者自查,在这个过程中,如果只是小工作量的查询,进入系统直接输入命令即可。但是如果说要查询的系统很多,每个系统中要查询的项目也很多,那么每次都要一…...

SQLite 3.43.0 发布,又有啥新功能?
SQLite 开发团队于 2023 年 08 月 24 日发布了 SQLite 3.43.0 版本。本文给大家分析一下该版本的更新。 全文索引 SQLite 3.43.0 增加了 Contentless-Delete FTS5 索引。这是一种 FTS5 全文索引的变种,不存储被索引的内容,同时支持数据的删除操作。 例…...

百度自研高性能ANN检索引擎,开源了
作者 | Puck项目组 导读 Puck是百度自研的开源ANN检索引擎。Puck开源项目包含两种百度自研的检索算法,以高召回、高准确、高吞吐为目标,适用于多种数据规模和场景。随着业务发展不断的优化和迭代,进行充分的技术开发和测试,确保了…...

golang遍历map的方法
在Go语言中,可以使用range关键字来遍历一个map。range关键字会返回两个值:key和value。 以下是遍历map的示例代码: package main import "fmt" func main() { myMap : map[string]int{ "apple": 1, "banana…...

如何让Android平台像网络摄像机一样实现GB28181前端设备接入?
技术背景 好多开发者在做国标对接的时候,首先想到的是IPC(网络摄像头),通过参数化配置,接入到国标平台,实现媒体数据的按需查看等操作。 像执法记录仪等智能终端,跑在Android平台,…...

文盘Rust -- 生命周期问题引发的 static hashmap 锁 | 京东云技术团队
2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了,于是想看看能不能用rus…...

SpringMVC入门篇
目录 1.SpringMVC工作流程 2.SpringMVC核心组件 2.1 DispatcherServlet 2.2 HandlerMapping 2.3 Handler 2.4 HandlerAdapter 2.5 ViewResolver 2.6 View 3.SpringMVC的入门 3.1 添加相关依赖 3.2 创建Spring-mvc.xml 3.3 配置web.xml 3.4 效果演示 4.静态资源处…...

面经:安卓学习笔记
文章目录 1. Android系统架构2. Activity2.0 定义2.1 生命周期2.2 生命状态2.3 启动模式 3. Service3.1 定义3.2 两种启动方式3.3 生命周期3.4 跨进程service3.5 IntentService 4. BroadCastReceiver4.1 概念4.2 组成4.3 广播接收器的分类4.4 生命周期4.5 静态注册和动态注册 5…...

Java设计模式:四、行为型模式-06:观察者模式
文章目录 一、定义:观察者模式二、模拟场景:观察者模式2.1 观察者模式2.2 引入依赖2.3 工程结构2.4 模拟摇号2.4.1 摇号服务接口2.4.2 摇号返回结果类 三、违背方案:观察者模式3.0 引入依赖3.1 工程结构3.2 添加摇号接口和实现3.2.1 摇号服务…...

vscode中讨厌的蓝色波浪线的去除小trick和原理
问题描述 不小心“设置同步”时和远程电脑的合并(merge)了,然后就出现了这个问题!烦死了!!! 大概是这个样子: 解决办法 站在了巨人的肩膀上,在下图位置输入这样一行参数&…...

开发工具——IDE安装 / IDEA子module依赖导入失败编译提示xx找不到符号 / IDEA在Git提交时卡顿
近期换了工作电脑,公司的IT团队不够给力,不能复制电脑系统,所以又到了需要重装IDE配置开发环境的时候了;在安装和导入Java编译器IDEA的时候遇到一些"棘手"问题,这里整理下解决方法以备不时之需; …...

AcWing 787:归并排序
【题目来源】https://www.acwing.com/problem/content/789/【题目描述】 给定你一个长度为 n 的整数数列。 请你使用归并排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。【输入格式】 输入共两行,第一行包含整数 n。 第二行包含 n 个整数&#…...

SeamlessM4T—Massively Multilingual Multimodal Machine Translation
本文是LLM系列的文章,针对《SeamlessM4T—Massively Multilingual & Multimodal Machine Translation》的翻译。 SeamlessM4T:大规模语言多模态机器翻译 摘要1 引言2 多模态翻译的社会技术维度2.12.22.3 3 SeamlessAlign:自动创建语音对…...

Python数据分析-Numpy
Numpy 个人笔记,仅供参考,谢谢 导入 import numpy import numpy as np from numpy import *Numpy数组对象 引入 # 让列表1 a [1,2,3,4],b [4,5,6,7] [x1 for x in a] # 实现ab a b > [1,2,3,4,5,6,7,8] [x y for (x,y) in zip(a,b)] -------…...

【真题解析】系统集成项目管理工程师 2023 年上半年真题卷(案例分析)
本文为系统集成项目管理工程师考试(软考) 2023 年上半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析(问答题 4 道,75分)案例分析(问答题*4)试题一试题二试题三试题四案例分析(问答题*4) …...

【GAMES202】Real-Time Global Illumination(in 3D)—实时全局光照(3D空间)
一、SH for Glossy transport 1.Diffuse PRT回顾 上篇我们介绍了PRT,并以Diffuse的BRDF作为例子分析了预计算的部分,包括Lighting和Light transport,如上图所示。 包括我们还提到了SH,可以用SH的有限阶近似拟合球面函数ÿ…...

金蝶云星空二开,公有云执行SQL
功能背景; 金蝶公有云执行sql工具,因官方为云部署 用户无法连接数据库增删改查 天梯维护网页仅支持增删改操作 二开单据已支持根据sql动态生成单据体 与sql可视化界面操作一致 功能实现及场景: 1.可用于公有云执行sql类操作 2.私有云部署&am…...

JAVA String 二维的字符串数组 String[][]
String[][] 表示一个二维的字符串数组,也可以称为字符串矩阵。它是由多个一维的字符串数组组成的,每个一维数组都表示矩阵中的一行。 在 Java 中,可以使用如下方式声明和初始化一个二维字符串数组: String[][] matrix new Strin…...

【Unity3D赛车游戏优化篇】【九】Unity中如何让汽车丝滑漂移?
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...

el-dialog设置高度、使用resetFields清除表单项无效问题
初学者容易踩坑的的el-dialog、el-form问题 1. el-dialog设置高度2. el-form中表单项对不齐3. 使用resetFields清除表单项无效 1. el-dialog设置高度 在el-dialog中里面添加一个div设置固定高度,或者限制最小的高度。 <el-dialogtitle"选择图标"v-mod…...

MySql切换到达梦数据库,各种问题解决记录
参考官方文档: https://eco.dameng.com/document/dm/zh-cn/sql-dev/practice-func.html 1. 关键字导致的报错:如ref,comment,top,domain等 Error -2007: 第 1 行, 第 117 列[ref]附近出现错误: 语法分析出错解决方案:修改关键字即可 2. 查…...

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆
2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆...

vscode中使用eslint+prettier的配置
eslintprettiervscode自动保存用起来感觉非常爽快。 一般来说,安装eslintprettier插件,然后使用相关脚手架配套的eslintprettier,无法自动格式代码,每次都需要执行格式化命令。这里贴出保存自动格式化代码的setting.json。 // .…...

HTML 标签讲解
HTML 标签讲解 HTML 语言结构根元素元数据元素主体根元素大纲元素文本内容语义化内联文本图像与多媒体编辑标识table表格内容表单内容table表单 HTML 语言结构 Markup (标记、标签)用来容纳和描述内容 严格意义上,标签是指开始标签…...

ue5 小知识点 ue的world type,pie editor game
说明以该命令行模式启动游戏的前提下的两个问题: 1.WITH_EDITOR中的代码会被编译 2.由于没有在编辑器中(即没有打开虚幻编辑器),所以GIsEditor为false WITH_EDITOR和WITH_EDITORONLY_DATA的区别 在论坛中找到的答案: WITH_EDITORONLY_DAT…...