stm32平衡车

目录

一.所需材料

二.PID算法(简单说明)

直立环

速度环

串级PID 

三.使用到的外设 

         1.定时器输出比较-PWM

2.定时器编码器模式 

3.编码器读取速度 

4.电机驱动函数

5.外部中断

四、小车

        调试 


一.所需材料

1.陀螺仪MPU6050--读取三轴的加速度和角速度,然而我们平衡车需要的不是加速度和角速度,而是需要实时的角度,即我们还需要进行姿态角转换,可以直接移植正点原子的DMP函数

2.TB6612电机驱动,l298N也可,我用的是TB6612,点击查看接线

3.OLED,用来显示姿态角,可以直接移植

4.12v锂电池和DCDC降压模块给电机和单片机供电

5.编码电机,点击查看接线

6.c8t6单片机

二.PID算法(简单说明)

P(比例)算法:Kp*误差。

        可以看出,误差越大,P的输出就越大,所以P的作用是减小误差,但是只有P算法会发生超调现象,即在不受外力下可以看作简谐运动,一直震荡

I(积分)算法:Ki*误差的积分。

        可以看出,只要存在误差(一般叫做稳态误差,比如小车在平衡位置偏左或右一点点,但是又不会倒下等等的情况下产生的角度误差)就会一直累加,累加累加的总误差就变的很大,滚雪球一样,越来越大,所以I可以消除稳态误差

D(微分):Kd*误差的微分。

        可以看出,两次误差之差反映的是系统的反应速度,响应越快,误差之差越大,D输出很大,反之很小,所以D算法会抑制过冲,消弱系统的反应速度。例如简谐运动,以最下点为期望点,小球从空中开始速度很小,后面越来越快,即可以理解为误差微分越来越大,且为负的,所以D输出就越来越大,力的方向与小球运动方向相反,若没有D算法,小球就会继续简谐运动,若此时有了D算法,小球的运动就会收到阻碍而令小球到达的高度降低,长此以往,小球就会静止在最低点

        也可以这样看,在水中挥拳,挥的速度越大,受到的阻尼越大

直立环

PWM_OUT=Kp*误差+Kd*误差的微分

误差=真实角度-期望角度

误差的微分=角速度,姿态角转换得到

作用:让小车保持短时间的平衡,但是推一下就可能倒下

/*
直立环
PWM_OUT = Kp*角度偏差+Kd*角度偏差的微分
角度偏差求微分就是对角度偏差求导,即为角速度
gyro_Y:俯仰角的角速度
*/

int Vertical(float Expect_Angle,float Angle,float gyro_Y)
{
	int PWM_out1=0;
	PWM_out1 = Vertical_Kp*(Expect_Angle-Angle)+Vertical_Kd*(gyro_Y-0);
	return PWM_out1;
}
/*

速度环

PWM_OUT=Kp*误差+Ki*误差的积分 

误差:期望速度-真实速度

误差的积分=误差累加

作用:消除稳态误差,加强系统的反应速度

极性问题:

速度环在平衡一类的基本上都是用正反馈,即积分误差,放大误差,先注释掉直立环,则极性正确的现象就是转动一下轮子,立刻就会加速到最大,因为我们速度理论值是0,我们转了一下,产生了误差,速度环不断放大误差,使速度环输出越大,就会让轮子不断加速到最大速度,若是负反馈,我们很难转动轮子

int Velocity(int Target,int left,int right)
{	
	static int Encoder_S,EnC_Err_Lowout_last,PWM_out3,Encoder_Err,EnC_Err_Lowout;
	float a = 0.7;
	Encoder_Err = left+right-Target;//速度偏差
	
	//对速度偏差进行一阶低通滤波
	EnC_Err_Lowout = a*EnC_Err_Lowout_last+(1-a)*Encoder_Err;
	EnC_Err_Lowout_last=EnC_Err_Lowout;
	
	//积分
	Encoder_S+=EnC_Err_Lowout;
	//限幅
	Encoder_S=I_Limit(Encoder_S);
	
	PWM_out3 = Velocity_Kp*EnC_Err_Lowout + Velocity_Ki*Encoder_S;
	
	return PWM_out3;	
}
int I_Limit(int Encoder_S)
{
	if(Encoder_S>10000)Encoder_S=10000;
	else if(Encoder_S<-10000)Encoder_S=-10000;
	else 
		Encoder_S=Encoder_S;
	return Encoder_S;
}

速度环必须滤波,因为速度会突变,突变的速度会对系统产生很大影响 

        滤波:编码器测到的数据(速度)是存在一些突变的,这些突变会导致系统的不稳定,所以我们要滤除这些突变,这就是滤波,类似于模电里面的“滤除电路中的高频信号”

一阶低通滤波:把权重给到上次滤波后的速度,另一个这次的速度为(1-a)倍,0.5<a<1

积分限幅:误差的积分在一直累加,若没有限幅,积分会越来越大,举个例子:小车倒下了,我们为了保护电机而关掉了电机,但是单片机的电源并没有关掉,误差一直在累加,很大很大了,当我们把小车放好并打开电机后,由于I的作用根本无法平衡。所以要限幅。

串级PID 

 串级,顾名思义,就是一个环的输出作为另一个环的输入,例如速度环的输出作为直立环的输入,或者直立环的输出作为速度环的输入

速度环输入:1.给定速度。2.速度反馈。

输出:角度值(直立环的期望速度输入)

直立环输入:1.给定角度(速度环输出)。2.角度反馈

输出:PWM(直接控制小车)

Vertical_out=Kp1*(Angle-Expect_Angle)+Kd* gyro_y

直立环输出=Kp1*(真实角度-期望角度+机械中值)+Kd*角度偏差的微分

Velocity_out =Kp2*(Encoder_ real- Encoder_ expect)+Ki*(Encoder_ real- Encoder_ expect)的积分

速度环输出=Kp2*(反馈编码器值-期望编码器值)+Ki*编码器偏差的积分

合并推导:Expect_Angle   =     Velocity_out

Vertical_out = Kp1*{ Angle-[ Kp2*(Encoder_ real- Encoder_ expect)+Ki*Σ(Encoder_ real- Encoder_ expect) ]}+Kd*gyro_y

          =Kp1*真实角度+ Kd*角度偏差的微分-Kp1* [Kp2*编码器偏差- Ki *编码器偏差的积分]

                         

三.使用到的外设 

      1.定时器输出比较-PWM

void PWM_Init_TIM1(uint16_t Psc,uint16_t Per)
{
	//开时钟,AFIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO,ENABLE);

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_8 | GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);//因为没有将所有的结构体内容全部配置,所以这里先初始化一下
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;//不分频----滤波器的采样频率,可以由内部时钟直接提供,
																													//也可以由内部时钟加一个时钟分频而来,
																													//分频系数就是由TIM_ClockDivision决定
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseInitStruct.TIM_Period = Per;//ARR的值
	TIM_TimeBaseInitStruct.TIM_Prescaler = Psc;//PSC的值
	//TIM_TimeBaseInitStruct.TIM_RepetitionCounter  重复计数器,这里不需要
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
	
	TIM_OCInitTypeDef  TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//初始化输出比较结构体
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式 
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //极性,有效电平为高电平
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OCInitStruct.TIM_Pulse = 0;//CCR
	TIM_OC1Init(TIM1,&TIM_OCInitStruct);
	TIM_OC4Init(TIM1,&TIM_OCInitStruct);
	
	TIM_CtrlPWMOutputs(TIM1,ENABLE);//高级定时器特有的,必须使能
	
	//使能ARR影子寄存器
	TIM_ARRPreloadConfig(TIM1,ENABLE);
	//使能输出比较预装载值寄存器
	TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);
	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);
	
	TIM_Cmd(TIM1,ENABLE);
}
	

2.定时器编码器模式 

//配置编码器
//编码器1-PA0/PA1-TIM2
//编码器2-PB6/PB7-TIM4
void Encoder_TIM2_Init()
{
	//开启GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//开启定时器时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//初始化定时器	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);//因为没有将所有的结构体内容全部配置,所以这里先初始化一下
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;//滤波分频系数
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 65535;//ARR的值
	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;//PSC的值
	//TIM_TimeBaseInitStruct.TIM_RepetitionCounter  重复计数器,这里不需要
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	//编码器特有的配置函数,配置模式
	TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

	//初始化输入捕获
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICStructInit(&TIM_ICInitStruct);//因为没有将所有的结构体内容全部配置,所以这里先初始化一下
	TIM_ICInitStruct.TIM_ICFilter = 10;// 滤波器,范围是 0x0 and 0xF
	TIM_ICInit(TIM2,&TIM_ICInitStruct);
	
	//清除定时器溢出中断标志位
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	//配置溢出中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	//设置计数器初始值为0
	TIM_SetCounter(TIM2,0);
	//开启定时器
	TIM_Cmd(TIM2,ENABLE);
}


void Encoder_TIM4_Init()
{
	//开启GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//开启定时器时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	//初始化定时器	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);//因为没有将所有的结构体内容全部配置,所以这里先初始化一下
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;//外部时钟滤波分频系数
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 65535;//ARR的值
	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;//PSC的值
	//TIM_TimeBaseInitStruct.TIM_RepetitionCounter  重复计数器,这里不需要
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);
	
	//编码器特有的配置函数
	TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

	//配置IC输入
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICStructInit(&TIM_ICInitStruct);//因为没有将所有的结构体内容全部配置,所以这里先初始化一下
	TIM_ICInitStruct.TIM_ICFilter = 10;// 滤波器
	TIM_ICInit(TIM4,&TIM_ICInitStruct);
	
	//清除定时器溢出中断标志位
	TIM_ClearFlag(TIM4,TIM_FLAG_Update);
	//配置溢出中断
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
	//设置计数器初始值为0
	TIM_SetCounter(TIM4,0);
	//开启定时器
	TIM_Cmd(TIM4,ENABLE);
}

3.编码器读取速度 

/*******************
编码器速度读取函数
	*入口参数:定时器2/4
*******************/
int Read_Speed(int TIMx)
{
	int value_1;
	switch(TIMx)
	{
		//int ? short
		case 2:value_1=(short)TIM_GetCounter(TIM2);TIM_SetCounter(TIM2,0);break; //先读取编码器的计数值,然后清零计数值,目的是直接获得速度,而不用计数值相减来算速度
		case 4:value_1=(short)TIM_GetCounter(TIM4);TIM_SetCounter(TIM4,0);break;
		default :value_1 = 0;
	}
	//这里TIM_GetCounter()函数的返回值是uint16_t ,而我们的返回值是有符号的int类型,所以这里强制类型转化一下
	return value_1;
}

4.电机驱动函数


//电机初始化函数
void Motor_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//复用推挽
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}


//限幅函数
void Limit(int *motorA ,int *motorB)
{
	if(*motorA > PWM_MAX)*motorA=PWM_MAX;
	if(*motorA < PWM_MIN)*motorA=PWM_MIN;
	if(*motorB > PWM_MAX)*motorB=PWM_MAX;
	if(*motorB < PWM_MIN)*motorB=PWM_MIN;
}

//绝对值函数
int abs(int p)
{
	int q;
	q = p>0?p:(-p);
	return q;
}

//电机驱动函数
//入口参数:PID运算完成后最终PWM值
void Load(int moto1,int moto2)
{
	//判断正反转
	if(moto1>0)Ain1=1,Ain2=0;
	else 			 Ain1=0,Ain2=1;
	//输入PWM的绝对值
	TIM_SetCompare1(TIM1,abs(moto1));
	
	if(moto2>0)Bin1=1,Bin2=0;
	else 			 Bin1=0,Bin2=1;
	TIM_SetCompare4(TIM1,abs(moto2));
}

//电机为10KHZ
//72000000/7200 = 10 000 HZ
#define PWM_MAX		 7200	
#define PWM_MIN		-7200

extern int MOTO1,MOTO2;
#define Ain1	PBout(14)
#define Ain2	PBout(15)

#define Bin1	PBout(13)
#define Bin2	PBout(12)

5.外部中断

dmp读取是用的中断读取,10ms读取一次,所以我们要配置MPU6050的外部中断

void MPU6050_EXTI_Init(void)
{

	//开启时钟
	//因为是复用功能,所以开启AFIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//GPIO_Mode_AF_PP;复用推挽
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
	
	//映射GPIO和外部中断
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5);
	
	//配置EXTI结构体
	EXTI_InitTypeDef  EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line = EXTI_Line5;//中断线
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;//使能中断线
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
	EXTI_Init(&EXTI_InitStruct);
	
}

void NVIC_Config()
{
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//外部中断
	NVIC_InitTypeDef  NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	
	//串口
	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
}


void EXTI9_5_IRQHandler(void)
写中断服务函数,里面做的是PID算法
void EXTI9_5_IRQHandler(void)
{		
	
	if(EXTI_GetITStatus(EXTI_Line5) == SET)//MPU6050外部中断是PB5
	{
		if(PBin(5) == 0)//引脚定义为外部上拉,而外部中断触发模式是下降沿,这里就是再进行下降沿的检测
		{
			
			EXTI_ClearITPendingBit(EXTI_Line5);
			
			//1.采集编码器数据和MPU6050的角度信息
			Encoder_Left  = -Read_Speed(2);//因为电机是相对安装的,即左右电机相差180度,为了让编码器输出极性一致,
			Encoder_Right = Read_Speed(4);//所以需要取反,或者调转一下极性也可以
										
			mpu_dmp_get_data(&Pitch,&Roll,&Yaw);		
			MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);
			MPU_Get_Accelerometer(&aacx,&aacy,&aacz);
			
			//2.将数据压入闭环控制中,计算输出控制量			
			Velocity_out = Velocity(Target_Speed,Encoder_Left,Encoder_Right);
			Vertical_out = Vertical(Velocity_out+Med_Angle,Pitch,gyroy);
			Turn_out = Turn(gyroz);	
			
			PWM_out = Vertical_out;
			
			//3.把控制量加载到电机上
			MOTO1 = PWM_out-Turn_out;//这里一加一减是因为两个电机是反向安装的,实际上就是同向加减了
			MOTO2 = PWM_out+Turn_out;
			Limit(&MOTO1,&MOTO2);
			
			Load(MOTO1,MOTO2);
		}
	}
}

四、小车

本人不会打板,所以选择了做一回杜邦线战神!

调了直立环和速度环,但是不理想,不能很好的回位

 

调试 

串级的PID调试时应该断掉两个环的连接

机械中值把平衡小车放在地面上,从前向后以及从后向前绕电机轴旋转平衡小车,两次的向另一边倒下的角度的中值,就是机械中值。

直立环

Kp极性:

极性错误:小车往哪边倒,车轮就往反方向开,会使得小车加速倒下。

极性正确:小车往哪边倒,车轮就往哪边开,以保证小车有直立的趋势。

Kp大小:

Kp一直增加,直到出现大幅低频震荡

Kd极性:

极性错误:拿起小车绕电机轴旋转,车轮反向转动,无跟随。

极性正确:拿起小车绕电机轴旋转,车轮同向转动,有跟随。

Kd大小:

Kd一直增加,直到出现高频震荡

直立环调试完毕后,对所有确立的参数乘以0.6作为最终参数。

原因:之前得到的参数都是Kp、Kd最大值,根据工程经验平衡小车的理想参数为最大参数乘以0.6求得。

结果:乘以0.6后,小车的抖动消失,但同时直立效果也变差。待下面加入速度环就能得到更好的性能。

在调试速度环参数极性时:需要去掉(注释掉)直立环运算

在调试速度环参数大小时:再次引入(取消注释)直立环运算

Kp&Ki

线性关系、Ki=(1/200)*Kp、仅调Kp即可。

Kp&Ki极性:

极性错误:手动转动其中一个车轮,另一车轮会以同样速度反向旋转——典型负反馈。

极性正确:手动转动其中一个车轮,两个车伦会同向加速,直至电机最大速度——典型正反馈。

Kp&Ki大小:

增加Kp&Ki,直至:小车保持平衡的同时,速度接近于零。且回位效果较好。

转向环

Kp极性:

极性错误:拿起小车,并将小车绕Z轴旋转,两车轮旋转的趋势与小车旋转趋势一致——典型正反馈。

极性正确:拿起小车,并将小车绕Z轴旋转,两车轮旋转的趋势与小车旋转趋势相反——典型负反馈。

Kp大小:

加大Kp,直至走直线效果较好,且无剧烈抖动。 

参考于b站up主天下行走的平衡车教学 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue的旅游推荐系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

自定义序列化

3.2.2.自定义序列化 RedisTemplate可以接收任意Object作为值写入Redis&#xff1a; 只不过写入前会把Object序列化为字节形式&#xff0c;默认是采用JDK序列化&#xff0c;得到的结果是这样的&#xff1a; 缺点&#xff1a; 可读性差内存占用较大 我们可以自定义RedisTempla…

Python 从0开始 一步步基于Django创建项目(3)使用Admin site管理数据模型

本文内容建立在《Python 从0开始 一步步基于Django创建项目&#xff08;2&#xff09;创建应用程序&数据模型》的基础上。 Django提供的admin site&#xff0c;使得网站管理员&#xff0c;能够轻松管理网站的数据模型。 本文首先创建‘管理员账户’&#xff0c;即超级用户…

springboot296基于个性化定制的智慧校园管理系统设计与开发

智慧校园管理系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统校园综合信息管理难度大&…

C++夯实基础

C在线学习笔记 第一阶段&#xff1a;基础 一、环境配置 1.1.第一个程序&#xff08;基本格式&#xff09; ​ #include <iosteam> using namespace std;int main(){cout<<"hello world"<<endl;system("pause"); }​ 模板 #include…

【比较函数坑点】D. Li Hua and Tree

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e5 5, inf 1e18, maxm 4e4 5; const int mod 1e9…

Qt creator构建DLL库

文章目录 一、构建DLL库二、隐式调用DLL库 一、构建DLL库 Qt creator创建DLL项目。 实现功能函数。 运行代码&#xff0c;debug目录下会有.dll和.lib文件。 二、隐式调用DLL库 QT新建控制台项目。将.lib文件和与之关联的头文件赋值到项目文件夹。 3. 添加头文件和外部依赖库…

创建vue3项目并集成cesium插件运行

创建vue3项目并集成cesium插件 一、vue项目创建 1、前期准备 node.js&npm或yarn 本地开发环境已经安装好。 参考安装 2、安装vue-cli&#xff0c;要求3以上版本 #先查看是否已经安装 vue -V#安装 npm install -g vue/cli4.5.17 示例&#xff1a;Idea工具 页面 Termin…

HE切片+qupath识别TIL和成纤维细胞文献(三阴性乳腺癌)

An Open-Source, Automated Tumor-Infiltrating Lymphocyte Algorithm for Prognosis in Triple-Negative Breast Cancer An Open-Source, Automated Tumor-Infiltrating Lymphocyte Algorithm for Prognosis in Triple-Negative Breast Cancer - PubMed (nih.gov) 试验设计 …

Matplotlib中英文使用不同字体的最优解

中英文使用不同字体&#xff0c;我们需要解决两个需求&#xff1a; 第一个需求&#xff1a;要用中文字体显示中文&#xff0c;不能全部都是框框。第二个需求&#xff1a;横纵坐标的数字用英文字体显示&#xff0c;英文用英语字体显示。 方法很简单&#xff0c;只需要添加一行…

RabbitMQ之Plugins插件----AMQP对接MQTT

1.启用插件 rabbitmq-plugins enable rabbitmq_mqtt 2.检查是否启动成功&#xff0c;打开rabbitmq后台 3.概念&#xff1a; AMQP是由交换器和queue队列组成的消息队列机制&#xff0c;MQTT是由订阅主题组成的消息机制 1.MQTT创建连接时会向rabbitmq创建一个自己的queue&…

SpringCloud之网关组件Gateway学习

SpringCloud之网关组件Gateway学习 GateWay简介 Spring Cloud Gateway是Spring Cloud的⼀个全新项目&#xff0c;目标是取代Netflix Zuul&#xff0c;它基于Spring5.0SpringBoot2.0WebFlux&#xff08;基于高性能的Reactor模式响应式通信框架Netty&#xff0c;异步⾮阻塞模型…

基于python+vue超市管理系统flask-django-php-nodejs

课题主要分为二大模块&#xff1a;即管理员模块和员工模块&#xff0c;主要功能包括&#xff1a;个人信息修改、员工信息、商品信息、商品进货、商品出库、商品销量等&#xff1b; 目录 摘 要 I Abstrac II 目录 III 1绪论 1 1.1 研究背景 3 1.1.1国内研究现状 3 1.1.2国外研究…

是德科技keysight DSOX3024T示波器

181/2461/8938产品概述&#xff1a; DSOX3024T 示波器 要特性与技术指标 使用电容触摸屏进行简洁的触控操作&#xff1a; •提高调试效率 •触控设计可以简化文档记录 •使用起来就像您喜欢的智能手机或平板电脑一样简单 使用 MegaZoom IV 技术揭示偶发异常&#xff1a; •超快…

图像处理ASIC设计方法 笔记12 图像旋转ASIC中心控制器状态机

P109 1 流水线图像旋转ASIC整体架构 中心控制器负责各个模块的状态控制和数据调度,接收到外部启动信号后,进人芯片初始化阶段,片上FIFO接收外部输入的图像旋转参数、接收完毕后,再利用接收到的旋转角度到查找表中找到对应的正弦和正切值。 中心控制器将接收到的行列信息…

鸿蒙Harmony应用开发—ArkTS-LazyForEach:数据懒加载

LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach&#xff0c;框架会根据滚动容器可视区域按需创建组件&#xff0c;当组件滑出可视区域外时&#xff0c;框架会进行组件销毁回收以降低内存占用。 接口…

Pink老师Echarts教学笔记

可视化面板介绍 ​ 应对现在数据可视化的趋势&#xff0c;越来越多企业需要在很多场景(营销数据&#xff0c;生产数据&#xff0c;用户数据)下使用&#xff0c;可视化图表来展示体现数据&#xff0c;让数据更加直观&#xff0c;数据特点更加突出。 01-使用技术 完成该项目需…

是德科技keysight 53230A频率计数器

181/2461/8938产品概述&#xff1a; Keysight(原Agilent) 53230A 通用频率计数器/计时器可满足您所有的频率和时间间隔测量需求。除了典型的频率和时间间隔测量&#xff0c;它还可执行连续/无间隙测量&#xff0c;以进行基本调制域分析。 53230A 配有可选的猝发测量软件。它可…

腾讯云GPU服务器介绍_GPU实例规格价格_AI_深度学习

腾讯云GPU服务器是提供GPU算力的弹性计算服务&#xff0c;腾讯云GPU服务器具有超强的并行计算能力&#xff0c;可用于深度学习训练、科学计算、图形图像处理、视频编解码等场景&#xff0c;腾讯云百科txybk.com整理腾讯云GPU服务器租用价格表、GPU实例优势、GPU解决方案、GPU软…

Linux进程地址空间详解

文章目录 前言一、程序地址空间二、感受虚拟地址的存在三、进程地址空间四、程序从磁盘加载到内存的过程4.1 物理地址和虚拟地址的区别 五、写时拷贝5.1 解释fork()函数有两个返回值 前言 我们在学习C/C的时候用到的地址是什么地址呢&#xff1f;虚拟地址&#xff1f;物理地址&…