电机应用开发-直流有刷电机位置环控制实现

news2024/12/28 3:40:06

目录

直流有刷电机位置环控制实现

硬件设计

直流电机位置环控制-位置式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写位置式PID算法

主体功能

直流电机位置环控制-增量式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写增量式PID算法

主体功能


直流有刷电机位置环控制实现

可以以开始为参考,记录正转多少圈或反转多少圈。如一圈脉冲为1920,刚开始目标值为0,输入目标值脉冲为1920后正转一圈,再输入目标值脉冲为1920*2后正转一圈,然后输入目标值脉冲为0即可反转两圈。 

硬件设计

可选:L298N电机驱动板、野火MOS搭建的驱动板。

直流电机位置环控制-位置式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写位置式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算
TIM_HandleTypeDef TIM_TimeBaseStructure;
 
/**
  * @brief  初始化基本定时器定时,默认50ms产生一次中断
  * @param  无
  * @retval 无
  */
void TIMx_Configuration(void)
{
    HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 3);
    HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
	
	__TIM6_CLK_ENABLE();
	
	TIM_TimeBaseStructure.Instance = TIM6;
    TIM_TimeBaseStructure.Init.Period = 50 * 50 - 1;
    TIM_TimeBaseStructure.Init.Prescaler = 1680 - 1;
    TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
 
    // 开启定时器更新中断
    HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
	
    uint32_t temp = (__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure) + 1) / 50.0;  // 计算周期,单位ms
    set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1);  					// 给通道 1 发送目标值
}
 
/**
  * @brief  定时器更新事件回调函数
  * @param  无
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim == (&TIM_TimeBaseStructure))
    {
        motor_pid_control();	// 每50ms执行一次PID运算
    }
}

配置定时器1输出PWM控制电机
TIM_HandleTypeDef  DCM_TimeBaseStructure;
 
/**
  * @brief  初始化控制通用定时器
  * @param  无
  * @retval 无
  */
void Motor_TIMx_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_OC_InitTypeDef  TIM_OCInitStructure;
 
	__HAL_RCC_GPIOA_CLK_ENABLE();
    __TIM1_CLK_ENABLE();
 
	// PA8--PWM_TIM_CH1
	GPIO_InitStruct.Pin 		= GPIO_PIN_8;
    GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate 	= PWM_TIM_GPIO_AF;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// PA9--PWM_TIM_CH2
    GPIO_InitStruct.Pin 		= GPIO_PIN_9;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// TIM1 66.7us一次周期
    DCM_TimeBaseStructure.Instance = TIM1;
    DCM_TimeBaseStructure.Init.Period = 5600 - 1;
    DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
    DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
 
    /*PWM模式配置*/
    TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.Pulse = 0;
    TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
    TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
    TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
}
 
/**
  * @brief  设置TIM通道的占空比
	* @param  channel		通道	(1,2,3,4)
	* @param  compare		占空比
	*	@note 	无
  * @retval 无
  */
void TIM1_SetPWM_pulse(uint32_t channel, int compare)
{
    switch (channel)
    {
        case TIM_CHANNEL_1:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
            break;
 
        case TIM_CHANNEL_2:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
            break;
 
        case TIM_CHANNEL_3:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
            break;
 
        case TIM_CHANNEL_4:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
            break;
    }
}

配置定时器3读取编码器的计数值
TIM_HandleTypeDef  DCM_TimeBaseStructure;
 
/**
  * @brief  初始化控制通用定时器
  * @param  无
  * @retval 无
  */
void Motor_TIMx_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_OC_InitTypeDef  TIM_OCInitStructure;
 
	__HAL_RCC_GPIOA_CLK_ENABLE();
    __TIM1_CLK_ENABLE();
 
	// PA8--PWM_TIM_CH1
	GPIO_InitStruct.Pin 		= GPIO_PIN_8;
    GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate 	= PWM_TIM_GPIO_AF;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// PA9--PWM_TIM_CH2
    GPIO_InitStruct.Pin 		= GPIO_PIN_9;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// TIM1 66.7us一次周期
    DCM_TimeBaseStructure.Instance = TIM1;
    DCM_TimeBaseStructure.Init.Period = 5600 - 1;
    DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
    DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
 
    /*PWM模式配置*/
    TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.Pulse = 0;
    TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
    TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
    TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
}
 
/**
  * @brief  设置TIM通道的占空比
	* @param  channel		通道	(1,2,3,4)
	* @param  compare		占空比
	*	@note 	无
  * @retval 无
  */
void TIM1_SetPWM_pulse(uint32_t channel, int compare)
{
    switch (channel)
    {
        case TIM_CHANNEL_1:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
            break;
 
        case TIM_CHANNEL_2:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
            break;
 
        case TIM_CHANNEL_3:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
            break;
 
        case TIM_CHANNEL_4:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
            break;
    }
}

编写位置式PID算法
typedef struct
{
    float target_val;	// 目标值
    float actual_val;	// 实际值
    float err;       	// 定义偏差值
    float err_last;  	// 定义上一个偏差值
    float Kp,Ki,Kd;  	// 定义比例、积分、微分系数
    float integral;  	// 定义积分值
}_pid;					// 位置式PID
_pid pid;
 
/**
  * @brief  PID参数初始化
	*	@note 	无
  * @retval 无
  */
void PID_param_init(void)
{
    /* 初始化参数 */
    pid.target_val 	= 100.0;
    pid.actual_val 	= 0.0;
    pid.err 		= 0.0;
    pid.err_last 	= 0.0;
    pid.integral 	= 0.0;
    pid.Kp 			= 5.0;
    pid.Ki 			= 2.0;
    pid.Kd 			= 0.0;
	
    float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
    set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3);     // 给通道 1 发送 P I D 值
}
 
/**
  * @brief  设置目标值
  * @param  val		目标值
	*	@note 	无
  * @retval 无
  */
void set_pid_target(float temp_val)
{
    pid.target_val = temp_val;	// 设置当前的目标值
}
 
/**
  * @brief  获取目标值
  * @param  无
	*	@note 	无
  * @retval 目标值
  */
float get_pid_target(void)
{
    return pid.target_val;		// 设置当前的目标值
}
 
/**
  * @brief  设置比例、积分、微分系数
  * @param  p:比例系数 P
  * @param  i:积分系数 i
  * @param  d:微分系数 d
	*	@note 	无
  * @retval 无
  */
void set_p_i_d(float p, float i, float d)
{
    pid.Kp = p;    // 设置比例系数 P
    pid.Ki = i;    // 设置积分系数 I
    pid.Kd = d;    // 设置微分系数 D
}
 
/**
  * @brief  PID算法实现
  * @param  actual_val:实际值
  *	@note 	无
  * @retval 通过PID计算后的输出
  */
float PID_realize(float actual_val)
{
    /*计算目标值与实际值的误差*/
    pid.err = pid.target_val - actual_val;
	
    /*误差累积*/
    pid.integral += pid.err;
	
    /*PID算法实现*/
    pid.actual_val = pid.Kp * pid.err + pid.Ki * pid.integral + pid.Kd * (pid.err - pid.err_last);
	
    /*误差传递*/
    pid.err_last = pid.err;
	
    /*返回当前实际值*/
    return pid.actual_val;
}
 
/**
  * @brief  电机位置式 PID 控制实现(定时调用)
  * @param  无
  * @retval 无
  */
void motor_pid_control(void)
{
    if (is_motor_en == 1)     					// 电机在使能状态下才进行控制处理
    {
        float cont_val = 0;                     // 当前控制值
        int32_t Capture_Count = 0;              // 当前时刻总计数值
 
        /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD  */
        Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
 
		cont_val = PID_realize(Capture_Count);  // 进行 PID 计算

        if (cont_val > 0)    					// 判断电机方向
        {
            set_motor_direction(MOTOR_FWD);
        }
        else
        {
            cont_val = -cont_val;
            set_motor_direction(MOTOR_REV);
        }

        cont_val = (cont_val > 5500 * 0.48) ? 5500 * 0.48 : cont_val; 			// 速度上限处理
        set_motor_speed(cont_val);                                              // 设置 PWM 占空比
        set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count, 1);       // 给通道 1 发送实际值
    }
}

主体功能
#define CIRCLE_PULSES    (16 * 4 * 30)    // 编码器一圈可以捕获的脉冲,4倍物理分辨率 * 减速电机减速比

int main(void)
{
    int32_t target_location = CIRCLE_PULSES;

    初始化
	
    set_computer_value(SEND_STOP_CMD, CURVES_CH1, NULL, 0);    				// 同步上位机的停止按钮状态
    set_computer_value(SEND_TARGET_CMD, CURVES_CH1, &target_location, 1);   // 给通道 1 发送目标值

    while (1)
    {
        /* 接收数据处理 */
        receiving_process();

        /* 扫描KEY1 */
        if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)
        {
            set_computer_value(SEND_START_CMD, CURVES_CH1, NULL, 0);        // 同步上位机的启动按钮状态
            set_pid_target(target_location);    							// 设置目标值
            set_motor_enable();                 							// 使能电机
        }

        /* 扫描KEY2 */
        if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)
        {
            set_motor_disable();     										// 停止电机
            set_computer_value(SEND_STOP_CMD, CURVES_CH1, NULL, 0);         // 同步上位机的停止按钮状态
        }

        /* 扫描KEY3 */
        if (Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON)
        {
            /* 增加一圈 */
            target_location += CIRCLE_PULSES;
            set_pid_target(target_location);
            set_computer_value(SEND_TARGET_CMD, CURVES_CH1,  &target_location, 1);     // 给通道 1 发送目标值
        }

        /* 扫描KEY4 */
        if (Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON)
        {
            /* 减少一圈 */
            target_location -= CIRCLE_PULSES;
            set_pid_target(target_location);
            set_computer_value(SEND_TARGET_CMD, CURVES_CH1,  &target_location, 1);     // 给通道 1 发送目标值
        }
    }
}

直流电机位置环控制-增量式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写增量式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算

同上。

配置定时器1输出PWM控制电机

同上。

配置定时器3读取编码器的计数值

同上。

编写增量式PID算法
typedef struct
{
	float target_val;	// 目标值
	float actual_val;	// 实际值
	float err;       	// 定义当前偏差值
	float err_next;  	// 定义下一个偏差值
	float err_last;  	// 定义最后一个偏差值
	float Kp, Ki, Kd;	// 定义比例、积分、微分系数
}_pid;					// 速度环PID
_pid pid;
 
/**
  * @brief  PID参数初始化
	*	@note 	无
  * @retval 无
  */
void PID_param_init()
{
    /* 初始化参数 */
    pid.target_val 	= 100;
    pid.actual_val 	= 0.0;
    pid.err 		= 0.0;
    pid.err_last 	= 0.0;
    pid.err_next 	= 0.0;
    pid.Kp 			= 6;
    pid.Ki 			= 2.5;
    pid.Kd 			= 0;
 
    float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
    set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3);     // 给通道 1 发送 P I D 值
}
 
/**
  * @brief  设置目标值
  * @param  val		目标值
	*	@note 	无
  * @retval 无
  */
void set_pid_target(float temp_val)
{
    pid.target_val = temp_val;    // 设置当前的目标值
}
 
/**
  * @brief  获取目标值
  * @param  无
	*	@note 	无
  * @retval 目标值
  */
float get_pid_target(void)
{
    return pid.target_val;    // 设置当前的目标值
}
 
/**
  * @brief  设置比例、积分、微分系数
  * @param  p:比例系数 P
  * @param  i:积分系数 i
  * @param  d:微分系数 d
	*	@note 	无
  * @retval 无
  */
void set_p_i_d(float p, float i, float d)
{
    pid.Kp = p;    // 设置比例系数 P
    pid.Ki = i;    // 设置积分系数 I
    pid.Kd = d;    // 设置微分系数 D
}
 
/**
  * @brief  PID算法实现
  * @param  actual_val:实际值
	*	@note 	无
  * @retval 通过PID计算后的输出
  */
float PID_realize(float actual_val)
{
    /*计算目标值与实际值的误差*/
    pid.err = pid.target_val - actual_val;
	
    /*PID算法实现*/
    pid.actual_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 pid.actual_val;
}
 
/**
  * @brief  电机增量式 PID 控制实现(定时调用)
  * @param  无
  * @retval 无
  */
void motor_pid_control(void)
{
    if (is_motor_en == 1)     					// 电机在使能状态下才进行控制处理
    {
        float cont_val = 0;              		// 当前控制值
        int32_t Capture_Count = 0;  			// 当前时刻总计数值
 
        /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD  */
        Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
 
        cont_val = PID_realize(Capture_Count);  // 进行 PID 计算
 
        if (cont_val > 0)    					// 判断电机方向
        {
            set_motor_direction(MOTOR_FWD);
        }
        else
        {
            cont_val = -cont_val;
            set_motor_direction(MOTOR_REV);
        }
 
        cont_val = (cont_val > 5500 * 0.48) ? 5500 * 0.48: cont_val;     	// 速度上限处理
        set_motor_speed(cont_val);                                       	// 设置 PWM 占空比
        set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count, 1); 	// 给通道 1 发送实际值
    }
}

主体功能

同上。

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

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

相关文章

Python爬虫-获取汽车之家新车优惠价

前言 本文是该专栏的第10篇,后面会持续分享python爬虫案例干货,记得关注。 本文以汽车之家新车优惠价为例,获取各车型的优惠价,示例图如下: 地址:aHR0cHM6Ly9idXkuYXV0b2hvbWUuY29tLmNuLzAvMC8wLzQyMDAwMC80MjAxMDAvMC0wLTAtMS5odG1sI3B2YXJlYWlkPTIxMTMxOTU= 需求:获…

Fragment 调用PopupWindow 不显示这么回事

问题就在于 这个 tvCategory,页面刚创建就初始化 PopupWindow导致 取到的值为0 应该监听tvCategory 渲染完再去初始化PopupWindow

leetcode:合并两个有序链表

题目描述 题目链接:21. 合并两个有序链表 - 力扣(LeetCode) 题目分析 这个算法思路很简单:就是直接找小尾插 定义一个tail和head,对比两个链表结点的val,小的尾插到tail->next,如果一个链表…

git 更换远程仓库地址三种方法总结分享

因为公司更改了 gitlab 的网段地址,发现全部项目都需要重新更改远程仓库的地址了,所以做了个记录,说不定以后还会用到呢。 一、不删除远程仓库修改(最方便) # 查看远端地址 git remote -v # 查看远端仓库名 git rem…

四大开关电源故障检测方法分享

开关电源作为常见的电力设备已经被广泛应用到各个领域,但是在使用过程中会出现短路、输出电压不稳定等故障,那么开关电源的常见故障有哪些? 要如何检测? 开关电源常见故障的检测方法 一、开关电源过载或短路 1.检查输出负载,确保其符合开关…

C#,数值计算——多项式插值与外推插值(Poly2D_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Object for two-dimensional polynomial interpolation on a matrix.Construct /// with a vector of x1 values, a vector of x2 values, a matrix of tabulated /// func…

从0开始学习JavaScript--JavaScript模块化编程

JavaScript模块化编程是现代前端开发中的核心概念之一。通过模块化&#xff0c;能够将复杂的代码分割成独立的模块&#xff0c;提高代码的可维护性、可扩展性&#xff0c;同时实现代码的复用。本文将深入探讨JavaScript模块化的各个方面&#xff0c;包括模块的定义、导入导出、…

马养殖场建设VR模拟实训教学平台具有灵活性和复用性

为保障养殖场生物安全&#xff0c;避免疫病传播&#xff0c;学生出入养殖场受时间和地域的限制&#xff0c; 生产实习多以参观为主&#xff0c;通过畜牧企业技术人员的讲解&#xff0c;学生被动了解生产过程。为了解决畜牧养殖实训难的问题&#xff0c;借助VR技术开展畜牧养殖虚…

一起学docker系列之七docker容器卷技术

目录 1 为什么使用容器数据卷&#xff1f;2 数据卷的特点和优势3 使用数据卷的方法3.1 创建容器并挂载数据卷3.2 容器间数据卷的共享与继承 4 数据卷的权限设置5 注意事项5.1 解决权限问题5.2 路径自动创建 结语 对于容器化应用程序的数据管理和持久化&#xff0c;Docker 数据卷…

LeetCode | 622. 设计循环队列

LeetCode | 622. 设计循环队列 OJ链接 思路&#xff1a; 我们这里有一个思路&#xff1a; 插入数据&#xff0c;bank往后走 删除数据&#xff0c;front往前走 再插入数据&#xff0c;就循环了 那上面这个方法可行吗&#xff1f; 怎么判断满&#xff0c;怎么判断空&#xff1…

mysql:修改密码的几种方式

背景 当我们 brew install mysql 新安装 mysql 的时候&#xff0c;是没有密码的&#xff0c;我们可以直接通过 mysql -u root 连接上。但是密码还是要设置的&#xff0c;一是为了安全&#xff0c;二是有些数据库软件如 Sequel 连接都是必须要密码的&#xff0c;接下来我们来看…

详解深度学习中的图神经网络GNN

引言 图神经网络GNN是深度学习的一个分支。 深度学习的四个分支对应了四种常见的数据格式&#xff0c;前馈神经网络FNN处理表格数据&#xff0c;表格数据可以是特征向量&#xff0c;卷积神经网络CNN处理图像数据&#xff0c;循环神经网络RNN处理时序数据&#xff0c;图神经网…

thinkphp6 不支持:redis错误

起因&#xff1a; 使用 redis 时候&#xff0c;thinkphp 报错。 解决方法&#xff1a; 打开 php.ini 文件&#xff0c;增加 extensionphp_redis.dll 即可

C/C++内存管理(2):`new`和`delete`的实现原理

new和delete操作自定义类型 class Stack { public:Stack(int capacity 3):_top(0), _capacity(capacity){cout << "Stack(int capacity 3)" << endl;_a new int[capacity];}~Stack(){cout << "~Stack()" << endl;delete _a;_to…

I Doc View在线文档预览系统RCE漏洞(QVD-2023-45061)

0x01 产品简介 iDocView是一个在线文档解析应用&#xff0c;旨在提供便捷的文件查看和编辑服务。 0x02 漏洞概述 漏洞成因 本次漏洞出现在在线文档解析应用中的远程页面缓存功能。具体问题在于该应用未能对用户输入的URL进行充分的安全验证&#xff0c;从而导致存在安全隐患…

如何开发洗鞋店用的小程序

随着人们生活水平的提高&#xff0c;洗护行业是越来越细分化了&#xff0c;从最开始的干洗店包含洗护行业的所有服务到现在有专门为洗鞋开的店&#xff0c;如果开发一款洗鞋店用的小程序&#xff0c;可以实现用户在家下单直接有人上门取鞋的话&#xff0c;应该如何去开发呢&…

在数组的指定位置插入指定元素值numpy.insert()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 在数组的指定位置插入指定元素值 numpy.insert() [太阳]选择题 请问以下代码中最后输出结果是&#xff1f; import numpy as np arr np.array([1, 2, 3]) print("【显示】arr ",…

tp8 使用rabbitMQ(2)工作队列

代码的参数说明在 第一小节的代码中&#xff0c;如果需要可移步到第一节中查看 工作队列 工作队列&#xff08;又称&#xff1a;任务队列——Task Queues&#xff09;是为了避免等待一些占用大量资源、时间的操作。当我们把任务&#xff08;Task&#xff09;当作消息发送到队列…

【nowcoder】BM2 链表内指定区间反转

题目 题目分析&#xff1a; # 代码实现&#xff1a; package BMP2;class ListNode {int val;ListNode next null;public ListNode(int val) {this.val val;} } public class BM2 {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法…

一个最简单的工业通讯数据分析例子

1.背景 对工业设备的通讯协议进行分析可以帮助我们更好地理解其工作原理和相关技术&#xff0c;并且有助于以下几个方面&#xff1a; 1. 优化工业设备的通讯效率&#xff1a;了解通讯协议的细节可以帮助我们找到通讯效率低下的原因并进行优化&#xff0c;提高设备的通讯效率和…