直流有刷电机多环控制(PID闭环死区和积分分离)

直流有刷电机多环控制

提高部分-第8讲 直流有刷电机多环控制实现(1)_哔哩哔哩_bilibili

PID模型

外环的输出作为内环的输入,外环是最主要控制的效果,主要控制电机的位置。改变位置可以改变速度,改变速度是受电流控制。

实验环境

【 !】功能简介:
按下KEY1使能电机,按下KEY2禁用电机,按下KEY3\KEY4可以调整目标值,以到达控制的效果.
可以通过上位机<野火多功能调试助手>----PID调试助手,查看现象或进行调试.
在PID调试助手中,打开开发板对应的串口,单击下方启动即可.
注意,部分例程中,上位机设置PID目标值时,未做幅值限制,若出现积分饱和为正常现象.
在电机未停止时重新开启电机,可能出现PID调整不准确的问题,电机会因为惯性保持运行,定时器会捕获不该捕获的脉冲.
F103性能相对有限,程序中处理的任务较多,使用上位机调试时可能出现指令不执行的情况,多发几次即可.
【 !】实验操作:
接线:
电机驱动板 5V\GND 					<----> 开发板 5V\GND
电机驱动板 PWM1\PWM2 				<----> 开发板 PE9\PE11
电机驱动板 SD 							<----> 开发板 PD15
电机驱动板 编码器输出A\B		<----> 开发板 PD12\PD13
电机驱动板 电压\电流检测 		<----> 开发板 PC1\PC0

注意:必须使用跳冒连接 J33 和 J34
下载本程序,复位开发板即可。
/********************************************************************************/

【*】 引脚分配
定时器TIM1输出PWM信号:
两路PWM输出到PE9\PE11上连接到驱动板上的PWM信号输入.
电机驱动板 PWM1\PWM2 <----> 开发板 PE9\PE11

电机使能引脚:
电机驱动板 SD <----> 开发板 PD15

编码器信号:
用于对电机进行测速
电机驱动板 编码器输出A\B		<----> 开发板 PC7\PC6

电压电流采集:
电机驱动板 信号检测 电压\电流 		<----> 开发板 PD12\PD13

/************************************************************************************/
【*】 时钟
A.晶振:
-外部高速晶振:25MHz
-RTC晶振:32.768KHz

B.各总线运行时钟:
-系统时钟 = SYCCLK = AHB1 = 72MHz
-APB2 = 72MHz 
-APB1 = 36MHz

C.浮点运算单元:
  不使用

定时器定时执行PID电机控制

定时器6,时钟72MHz,预分频因子1440,重装载值2500

定时器周期:

72000000 / 1440 / 2500 = 20

1 / 20 = 50ms 进入一次中断,计算一次PID

#ifndef __BASIC_TIM_H
#define	__BASIC_TIM_H

#include "stm32f1xx.h"

#define BASIC_TIM           		  TIM6
#define BASIC_TIM_CLK_ENABLE()   	__HAL_RCC_TIM6_CLK_ENABLE()

#define BASIC_TIM_IRQn				    TIM6_DAC_IRQn
#define BASIC_TIM_IRQHandler    	TIM6_DAC_IRQHandler

/* 累计 TIM_Period个后产生一个更新或者中断*/		
//当定时器从0计数到BASIC_PERIOD_COUNT-1,即为BASIC_PERIOD_COUNT次,为一个定时周期
#define BASIC_PERIOD_COUNT    (50*50)

//定时器时钟源TIMxCLK = 2 * APB1_CLK
//				如果APB1_CLK = 72MHz = TIMxCLK
//				否则TIMxCLK=APB1_CLK * 2
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)
#define BASIC_PRESCALER_COUNT   (1440)

/* 获取定时器的周期,单位ms */
//#define __HAL_TIM_GET_PRESCALER(__HANDLE__)      ((__HANDLE__)->Instance->PSC)    // Get TIM Prescaler.
//#define GET_BASIC_TIM_PERIOD(__HANDLE__)    (1.0/(HAL_RCC_GetPCLK2Freq()/(__HAL_TIM_GET_PRESCALER(__HANDLE__)+1)/(__HAL_TIM_GET_AUTORELOAD(__HANDLE__)+1))*1000)

/* 以下两宏仅适用于定时器时钟源TIMxCLK=72MHz,预分频器为:1440-1 的情况 */
#define SET_BASIC_TIM_PERIOD(T)     __HAL_TIM_SET_AUTORELOAD(&TIM_TimeBaseStructure, (T)*50 - 1)    // 设置定时器的周期(1~1000ms)
#define GET_BASIC_TIM_PERIOD()      ((__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure)+1)/50.0f)     // 获取定时器的周期,单位ms

extern TIM_HandleTypeDef TIM_TimeBaseStructure;
void TIMx_Configuration(void);

#endif /* __BASIC_TIM_H */

/**
  ******************************************************************************
  * @file    bsp_basic_tim.c
  * @author  STMicroelectronics
  * @version V1.0
  * @date    2015-xx-xx
  * @brief   基本定时器定时范例
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F103 开发板  
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :http://firestm32.taobao.com
  *
  ******************************************************************************
  */
  
#include "./tim/bsp_basic_tim.h"
#include "./usart/bsp_debug_usart.h"
#include "./protocol/protocol.h"

TIM_HandleTypeDef TIM_TimeBaseStructure;
 /**
  * @brief  基本定时器 TIMx,x[6,7]中断优先级配置
  * @param  无
  * @retval 无
  */
static void TIMx_NVIC_Configuration(void)
{
	//设置抢占优先级,子优先级
	HAL_NVIC_SetPriority(BASIC_TIM_IRQn, 2,0);
	// 设置中断来源
	HAL_NVIC_EnableIRQ(BASIC_TIM_IRQn);
}

/*
 * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
 * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
 * 另外三个成员是通用定时器和高级定时器才有.
 *-----------------------------------------------------------------------------
 * TIM_Prescaler         都有
 * TIM_CounterMode			 TIMx,x[6,7]没有,其他都有(基本定时器)
 * TIM_Period            都有
 * TIM_ClockDivision     TIMx,x[6,7]没有,其他都有(基本定时器)
 * TIM_RepetitionCounter TIMx,x[1,8]才有(高级定时器)
 *-----------------------------------------------------------------------------
 */
static void TIM_Mode_Config(void)
{
	// 开启TIMx_CLK,x[6,7] 
	BASIC_TIM_CLK_ENABLE(); 

	TIM_TimeBaseStructure.Instance = BASIC_TIM;
	/* 累计 TIM_Period个后产生一个更新或者中断*/		
	//当定时器从0计数到BASIC_PERIOD_COUNT - 1,即为BASIC_PERIOD_COUNT次,为一个定时周期
	TIM_TimeBaseStructure.Init.Period = BASIC_PERIOD_COUNT - 1;
	//定时器时钟源TIMxCLK = 2 * PCLK1  
	//				PCLK1 = HCLK / 2 
	//				=> TIMxCLK=HCLK/2=SystemCoreClock/2*2=72MHz
	// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)
	TIM_TimeBaseStructure.Init.Prescaler = BASIC_PRESCALER_COUNT - 1;	
  TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;           // 向上计数
  TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;     // 时钟分频

	// 初始化定时器TIMx, x[2,3,4,5]
	HAL_TIM_Base_Init(&TIM_TimeBaseStructure);

	// 开启定时器更新中断
	HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);	
}

/**
  * @brief  初始化基本定时器定时,默认50ms产生一次中断
  * @param  无
  * @retval 无
  */
void TIMx_Configuration(void)
{
	TIMx_NVIC_Configuration();	
  
	TIM_Mode_Config();
  
#if PID_ASSISTANT_EN
  uint32_t temp = GET_BASIC_TIM_PERIOD();     // 计算周期,单位ms 
  set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1);     // 给通道 1 发送目标值
#endif

}

/*********************************************END OF FILE**********************/

电机PID控制

外环环周期(电流环计算周期为定时器周期T,速度环为2T,位置环为3T)

采样周期要小于PID计算周期,这里采样周期应该是ADC电流采样、编码器等数据。

使得调节连续,不联系需要改变周期值。

位置环的输出设置为速度环的目标值

速度环的输出设置为电流环的目标值

/**
  ******************************************************************************
  * @file    bsp_motor_control.c
  * @author  fire
  * @version V1.0
  * @date    2019-xx-xx
  * @brief   电机控制接口
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F103 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :http://firestm32.taobao.com
  *
  ******************************************************************************
  */ 

#include ".\motor_control\bsp_motor_control.h"
#include "./usart/bsp_debug_usart.h"
#include <math.h>
#include <stdlib.h>
#include "./Encoder/bsp_encoder.h"
#include "./adc/bsp_adc.h"
#include "./tim/bsp_basic_tim.h"

static motor_dir_t direction  = MOTOR_FWD;     // 记录方向
static uint16_t    dutyfactor = 0;             // 记录占空比
static uint8_t    is_motor_en = 0;             // 电机使能

#define TARGET_CURRENT_MAX    200    // 目标电流的最大值 mA
#define TARGET_SPEED_MAX      200    // 目标速度的最大值 r/m

static void sd_gpio_config(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* 定时器通道功能引脚端口时钟使能 */
	SHUTDOWN_GPIO_CLK_ENABLE();
  
  /* 引脚IO初始化 */
	/*设置输出类型*/
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	/*设置引脚速率 */ 
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	/*选择要控制的GPIO引脚*/	
	GPIO_InitStruct.Pin = SHUTDOWN_PIN;
  
	/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
  HAL_GPIO_Init(SHUTDOWN_GPIO_PORT, &GPIO_InitStruct);
}

/**
  * @brief  电机初始化
  * @param  无
  * @retval 无
  */
void motor_init(void)
{
  Motor_TIMx_Configuration();     // 初始化电机 1
  sd_gpio_config();
}

/**
  * @brief  设置电机速度
  * @param  v: 速度(占空比)
  * @retval 无
  */
void set_motor_speed(uint16_t v)
{
  v = (v > PWM_PERIOD_COUNT) ? PWM_PERIOD_COUNT : v;     // 上限处理
  
  dutyfactor = v;
  
  if (direction == MOTOR_FWD)
  {
    SET_FWD_COMPAER(dutyfactor);     // 设置速度
  }
  else
  {
    SET_REV_COMPAER(dutyfactor);     // 设置速度
  }
}

/**
  * @brief  设置电机方向
  * @param  无
  * @retval 无
  */
void set_motor_direction(motor_dir_t dir)
{
  direction = dir;

  if (direction == MOTOR_FWD)
  {
    SET_FWD_COMPAER(dutyfactor);     // 设置速度
    SET_REV_COMPAER(0);              // 设置速度
  }
  else
  {
    SET_FWD_COMPAER(0);              // 设置速度
    SET_REV_COMPAER(dutyfactor);     // 设置速度
  }
}

/**
  * @brief  使能电机
  * @param  无
  * @retval 无
  */
void set_motor_enable(void)
{
  is_motor_en = 1;	
  MOTOR_ENABLE_SD();
  MOTOR_FWD_ENABLE();
  MOTOR_REV_ENABLE();
}

/**
  * @brief  禁用电机
  * @param  无
  * @retval 无
  */
void set_motor_disable(void)
{
  is_motor_en = 0;
  MOTOR_DISABLE_SD();
  MOTOR_FWD_DISABLE();
  MOTOR_REV_DISABLE();
}

/**
  * @brief  电机位置式 PID 控制实现(定时调用)
  * @param  无
  * @retval 无
  */
void motor_pid_control(void)
{
  static uint32_t louter_ring_timer = 0;      // 外环环周期(电流环计算周期为定时器周期T,速度环为2T,位置环为3T)
  int32_t actual_current = get_curr_val();    // 读取当前电流值
  if(actual_current > TARGET_CURRENT_MAX)
  {
	  actual_current = TARGET_CURRENT_MAX;
  }
  if (is_motor_en == 1)                  // 电机在使能状态下才进行控制处理
  {
    static int32_t Capture_Count = 0;    // 当前时刻总计数值
    static int32_t Last_Count = 0;       // 上一时刻总计数值
    float cont_val = 0;                  // 当前控制值
    
    /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD  */
    Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * ENCODER_TIM_PERIOD);
    
    /* 位置环计算 */
    if (louter_ring_timer++ % 3 == 0)
    {
      cont_val = location_pid_realize(&pid_location, Capture_Count);    // 进行 PID 计算

      /* 目标速度上限处理 */
      if (cont_val > TARGET_SPEED_MAX)
      {
        cont_val = TARGET_SPEED_MAX;
      }
      else if (cont_val < -TARGET_SPEED_MAX)
      {
        cont_val = -TARGET_SPEED_MAX;
      }
   
      set_pid_target(&pid_speed, cont_val);    // 设定速度的目标值
      
    #if defined(PID_ASSISTANT_EN)
      int32_t temp = cont_val;
      set_computer_value(SEND_TARGET_CMD, CURVES_CH2, &temp, 1);     // 给通道 2 发送目标值
    #endif
    }

    /* 速度环计算 */
    static int32_t actual_speed = 0;                 // 实际测得速度
    if (louter_ring_timer % 2 == 0)
    {
      /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数  */
      actual_speed = ((float)(Capture_Count - Last_Count) / ENCODER_TOTAL_RESOLUTION / REDUCTION_RATIO) / (GET_BASIC_TIM_PERIOD()*2/1000.0/60.0);
        
      /* 记录当前总计数值,供下一时刻计算使用 */
      Last_Count = Capture_Count;
      
      cont_val = speed_pid_realize(&pid_speed, actual_speed);    // 进行 PID 计算

      if (pid_speed.target_val >= 0)    // 判断电机方向
      {
        set_motor_direction(MOTOR_FWD);
      }
      else
      {
        cont_val = -cont_val;
        set_motor_direction(MOTOR_REV);
      }
   
      cont_val = (cont_val > TARGET_CURRENT_MAX) ? TARGET_CURRENT_MAX : cont_val;    // 电流上限处理
      set_pid_target(&pid_curr, cont_val);    // 设定电流的目标值
      
    #if defined(PID_ASSISTANT_EN)
      int32_t temp = cont_val;
      set_computer_value(SEND_TARGET_CMD, CURVES_CH3, &temp, 1);     // 给通道 3 发送目标值  
    #endif
    }
    
    /* 电流环计算 */
    cont_val = curr_pid_realize(&pid_curr, actual_current);    // 进行 PID 计算
    
    if (cont_val < 0)
    {
      cont_val = 0;    // 下限处理
    }
    else if (cont_val > PWM_MAX_PERIOD_COUNT)
    {
      cont_val = PWM_MAX_PERIOD_COUNT;    // 速度上限处理
    }

    set_motor_speed(cont_val);                                                 // 设置 PWM 占空比
    
  #if defined(PID_ASSISTANT_EN)
    set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count,  1);         // 给通道 1 发送实际值
//    set_computer_value(SEND_FACT_CMD, CURVES_CH2, &actual_speed,   1);         // 给通道 2 发送实际值
//    set_computer_value(SEND_FACT_CMD, CURVES_CH3, &actual_current, 1);         // 给通道 3 发送实际值
  #else
    printf("1.电流:实际值:%d. 目标值:%.0f.\n", Capture_Count, get_pid_target(&pid_location));      // 打印实际值和目标值
  #endif
  }
}

/*********************************************END OF FILE**********************/

PID设置和整定

  1. 先整定内环,再整定外环。
  2. 先注释位置环和速度环,只保留电流和PID,进行整定。
  3. 然后,再解开速度环,整定速度环PID,最后解开位置环,整定位置环PID。

手撕PID(带死区、积分分离、不完全微分)_pid死区程序-CSDN博客

闭环死区

带死区的PID控制算法就是检测偏差值,若是偏差值达到一定程度,就进行调节。若是偏差值较小,就认为没有偏差。用公式表示如下:

其中的死区值得选择需要根据具体对象认真考虑,因为该值太小就起不到作用,该值选取过大则可能造成大滞后。

带死区的PID算法,对无论位置型还是增量型的表达式没有影响,不过它是一个非线性系统。

除以上描述之外还有一个问题,在零点附近时,若偏差很小,进入死去后,偏差置0会造成积分消失,如是系统存在静差将不能消除,所以需要人为处理这一点。

一个水龙头,选择角度设置为了3.5度,但是它只能一度一度的旋转,导致系统再3度和4度之间波动,使得系统不稳定。设置闭环死区,把这个误差可忽略即可。

积分分离

在过程的启动、结束或大幅度增减设定值时,短时间内系统输出有很大偏差,会造成PID运算的积分累积,引起超调或者振荡。为了解决这一干扰,人们引入了积分分离的思想。其思路是偏差值较大时,取消积分作用,以免于超调量增大;而偏差值较小时,引入积分作用,以便消除静差,提高控制精度。

具体的实现步骤是:根据实际情况,设定一个阈值;当偏差大于阈值时,消除积分仅用PD控制;当偏差小于等于阈值时,引入积分采用PID控制。则控制算法可表示为:

偏差很大的差值,直接在积分时忽略,防止积分饱和。

不完全微分PID

微分项有引入高频干扰的风险,但若在控制算法中加入低通滤波器,则可使系统性能得到改善。方法之一就是在PID算法中加入一个一阶低通滤波器。这就是所谓的不完全微分,其结构图如下:

微分部分表达式为:
其中 其中α的取值在0和1之间,有滤波常数和采样周期确定

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

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

相关文章

Odrive源码分析(四) 位置爬坡算法

Odrive中自带一个简单的梯形速度爬坡算法&#xff0c;本文分析下这部分代码。 代码如下&#xff1a; #include <cmath> #include "odrive_main.h" #include "utils.hpp"// A sign function where input 0 has positive sign (not 0) float sign_ha…

电视大全 1.3.8|汇聚多频道资源,秒切换流畅播放

电视大全TV版是一款功能丰富的TV端直播软件&#xff0c;由悠兔电视的同一开发者打造。最新版本更新了更多频道&#xff0c;包括央视、卫视和地方频道等&#xff0c;提供了多线路流畅播放体验&#xff0c;并支持节目回看、线路选择、开机自启等功能。该应用免登录且无购物频道&a…

JAVAweb学习日记(二)JavaScript

一、概念 二、JavaScript引入方式 三、JavaScript书写语法 输出语句&#xff1a; 变量&#xff1a; 数据类型、运算符、流程控制语句&#xff1a; 数据类型&#xff1a; 运算符&#xff1a; 字符串如果是 数字字符构成&#xff0c;先把读到的数字转为数字类型&#xff0c;后续…

深圳龙岗戴尔dell r730xd服务器故障维修

深圳龙岗一台DELL POWEREDGE R730XD服务器系统故障问题处理&#xff1a; 1&#xff1a;客户工厂年底产线整改&#xff0c;时不时的会意外断电&#xff0c;导致服务器也频繁停机&#xff0c; 2&#xff1a;多次异常停机后导致服务器开机后windows server系统无法正常启动了&…

Ansible 批量管理华为 CE 交换机

注&#xff1a;本文为 “Ansible 管理华为 CE 交换机” 相关文章合辑。 使用 CloudEngine - Ansible 批量管理华为 CE 交换机 wsf535 IP 属地&#xff1a;贵州 2018.02.05 15:26:05 总体介绍 Ansible 是一个开源的自动化运维工具&#xff0c;AnsibleWorks 成立于 2012 年&a…

2024年Python最新下载安装教程,附详细图文,持续更新

大家好&#xff0c;我是Python老安&#xff0c;今天为大家带来的是Windows Python3下载、安装教程&#xff0c;适用于 Python3 所有版本&#xff0c;包括 Python3.7,Python33.8,Python33.10 等版本。希望对大家有所帮助 Python目前已支持所有主流操作系统&#xff0c;在Linux,…

《点点之歌》“意外”诞生记

世界是“点点”的&#xff0c;“点点”是世界的。 (笔记模板由python脚本于2024年12月23日 19:28:25创建&#xff0c;本篇笔记适合喜欢诗文的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 …

网络安全检测

实验目的与要求 (1) 帮助学生掌握木马和入侵的防护和检测方法、提高学习能力、应用能力和解决实际问题的能力。 (2) 要求学生掌握方法, 学会应用软件的安装和使用方法, 并能将应用结果展示出来。 实验原理与内容 入侵检测是通过对计算机网络或计算机系统中若干关键点收集信…

c++------------------函数

函数定义 语法格式 函数定义包括函数头和函数体。函数头包含返回类型、函数名和参数列表。函数体是用花括号{}括起来的代码块&#xff0c;用于实现函数的功能。例如&#xff0c;定义一个计算两个整数之和的函数&#xff1a; int add(int a, int b) {return a b; }这里int是返回…

Java WEB:从起源到现代的传奇之旅

Java Web 起源于上世纪 90 年代&#xff0c;随着网络和浏览器的飞速发展&#xff0c;Java 为应对动态处理网页的需求&#xff0c;推出了 Servlet 技术。 1. Servlet 出现之前 在 Servlet 出现之前&#xff0c;用户请求主要是静态资源&#xff0c;如 html、css 等。此时的网络…

社区管理系统:实现社区信息数字化管理的实践

3.1可行性分析 开发者在进行开发系统之前&#xff0c;都需要进行可行性分析&#xff0c;保证该系统能够被成功开发出来。 3.1.1技术可行性 开发该社区管理系统所采用的技术是vue和MYSQL数据库。计算机专业的学生在学校期间已经比较系统的学习了很多编程方面的知识&#xff0c;同…

DataV的安装与使用(Vue3版本)

1、DataV(vue3)地址&#xff1a;DataV Vue3TSVite版 | DataV - Vue3 2、使用 npm install kjgl77/datav-vue3 安装 3、全局引入。 4、此时就可以按需使用了~

隐藏指定文件/文件夹和自动提示功能消失解决方案

一. 隐藏指定文件/文件夹 Idea中隐藏指定文件或指定类型文件 Setting → File Types → Ignored Files and Folders输入要隐藏的文件名&#xff0c;支持*号通配符回车确认添加 二. 自动提示功能消失解决方案 指定SpringBoot配置文件 File → Project Structure → Facets选…

springboot474基于web的垃圾分类回收系统(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统垃圾分类回收系统信息管理难度大&#xff0c;容错率低&am…

最优二叉搜索树【东北大学oj数据结构10-4】C++

题面 最优二叉搜索树是由 n 个键和 n1 个虚拟键构造的二叉搜索树&#xff0c;以最小化搜索操作的成本期望值。 给定一个序列 Kk1​,k2​,...,kn​&#xff0c;其中 n 个不同的键按排序顺序 &#xff0c;我们希望构造一个二叉搜索树。 对于每个关键 ki​&#xff0c;我们有一个…

jsp-servlet开发

STS中开发步骤 建普通jsp项目过程 1.建项目&#xff08;非Maven项目&#xff09; new----project----other----Web----Dynamic Web Project 2.下载包放到LIB目录中,如果是Maven项目可以自动导包&#xff08;pom.xml中设置好&#xff09; 3.设置工作空间&#xff0c;网页…

easyExcel导出大数据量EXCEL文件,前端实现进度条或者遮罩层

需求&#xff1a;页面点击导出&#xff0c;先按照页面条件去数据库查询&#xff0c;然后将查询到的数据导出。 问题&#xff1a;由于查询特别耗时&#xff0c;所以点击之后页面会看上去没有反应 方案1&#xff1a;就在点击之后在页面增加了一个进度条&#xff0c;等待后端查询…

新版Android Studio 2024.1.2版本,如何通过无线wifi连接手机实现交互

1、首先&#xff0c;先确定手机是否启动了开发者选项 在我的设备 -> 全部参数 -> MIUI版本点击6下 &#xff08;有的手机是 关于手机 -> 查看手机版本 &#xff09; 2、在设置中搜索 开启开发者选项 3、进入开发者选项后&#xff0c;在 调试 中选择 无线调试并选择…

CEF127 编译指南 MacOS 篇 - 编译 CEF(六)

1. 引言 经过前面的准备工作&#xff0c;我们已经完成了所有必要的环境配置。本文将详细介绍如何在 macOS 系统上编译 CEF127。通过正确的编译命令和参数配置&#xff0c;我们将完成 CEF 的构建工作&#xff0c;最终生成可用的二进制文件。 2. 编译前准备 2.1 确认环境变量 …

扩散模型经典问题:在Image-to-Image或Image-to-Video任务中,如何尽可能地保持住原始输入Image的特征?

AIGC算法工程师 面试八股文 2025年版本 在Image-to-Image或Image-to-Video任务中,如何尽可能地保持住原始输入Image的特征?你知道有哪些经典方法?这些方法各有什么优缺点? 目录 经典条件扩散模型 垫图法 Adapter方法 ControlNet方法 UNet中的ReferenceNet DiT中的Re…