基于正点原子电机实验的pid调试助手代码解析(速度环控制)
这里写目录标题
- 下位机与PID调试助手传输的原理
- 代码讲解(基于正点原子)
- 解析数据接受和数据发送的底层函数
- 数据接受
- 数据帧格式
- 环形数组以及怎么找到它的帧头位置
- crc校验
- 数据发送
- 数据上传函数
通过前两节文章,我已经了解了基本的pid算法,现在在完成了电机编码测速,pid控制电机转速的前提,我们来解析一下下位机是如何pid调试助手进行数据传递的.
下位机与PID调试助手传输的原理
首先用c#写一个PID调试助手,然后拟定好传递数据的通信协议,然后下位机配置好串口,下位机使用串口发送指令给上位机解析(按照通信协议),上位机发送数据,下位通过串口接受到上位机传来的指令,进行解析。
代码讲解(基于正点原子)
正点原子的代码使F4系列的,我修改成了stm32F103ZET6的板子代码,
现在我们先来讲一讲函数怎么调用.
首先看到主函数里的程序。
/******************************************************************************************************* @file main.c* @author 正点原子团队(ALIENTEK)* @version V1.0* @date 2021-10-16* @brief 直流有刷电机速度环PID控制 实验* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 F407电机开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com/forum.php* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com******************************************************************************************************/#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/DC_MOTOR/dc_motor.h"
#include "./BSP/ADC/adc.h"
#include "./BSP/TIMER/dcmotor_tim.h"
#include "./BSP/PID/pid.h"
#include "./DEBUG/debug.h"extern uint8_t g_run_flag;void lcd_dis(void);int main(void)
{uint8_t key;uint16_t t;uint8_t debug_cmd = 0;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */delay_init(168); /* 延时初始化 */usart_init(115200); /* 串口1初始化,用于上位机调试 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按键 */pid_init(); /* 初始化PID参数 */atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */dcmotor_init(); /* 初始化电机 */gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */#if DEBUG_ENABLE /* 开启调试 */debug_init(); /* 初始化调试 */debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) *//* 同步数据(选择第1组PID,目标速度地址,P,I,D参数)到上位机 */debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);#endifg_point_color = WHITE;g_back_color = BLACK;lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);while (1){key = key_scan(0); /* 按键扫描 */if(key == KEY0_PRES) /* 当key0按下 */{g_run_flag = 1; /* 标记电机启动 */g_speed_pid.SetPoint += 30;if (g_speed_pid.SetPoint == 0){dcmotor_stop(); /* 停止则立刻响应 */g_motor_data.motor_pwm = 0;motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机方向、转速 */}else{dcmotor_start(); /* 开启电机 */if (g_speed_pid.SetPoint >= 300) /* 限速 */{g_speed_pid.SetPoint = 300;}
#if DEBUG_ENABLEdebug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif}}else if(key == KEY1_PRES) /* 当key1按下 */{g_run_flag = 1; /* 标记电机启动 */g_speed_pid.SetPoint -= 30;if (g_speed_pid.SetPoint == 0){dcmotor_stop(); /* 停止则立刻响应 */g_motor_data.motor_pwm = 0;motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机方向、转速 */}else{dcmotor_start(); /* 开启电机 */if (g_speed_pid.SetPoint <= -300) /* 限速 */{g_speed_pid.SetPoint = -300;}
#if DEBUG_ENABLEdebug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif}}else if(key == KEY2_PRES) /* 当key2按下 */{dcmotor_stop(); /* 停止电机 */pid_init(); /* 重置pid参数 */g_run_flag = 0; /* 标记电机停止 */g_motor_data.motor_pwm = 0;motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */#if DEBUG_ENABLEdebug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD); /* 同步数据到上位机 */
#endif}#if DEBUG_ENABLE/* 查询接收PID助手的PID参数 */debug_receive_pid(TYPE_PID1, (float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral, (float *)&g_speed_pid.Derivative);debug_set_point_range(300, -300, 300); /* 设置目标速度范围 */debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */if (debug_cmd == BREAKED) /* 电机刹车 */{dcmotor_stop(); /* 停止电机 */pid_init(); /* 重置pid参数 */g_run_flag = 0; /* 标记电机停止 */g_motor_data.motor_pwm = 0;motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);} else if (debug_cmd == RUN_CODE) /* 电机运行 */{ dcmotor_start(); /* 开启电机 */g_speed_pid.SetPoint = 30; /* 设置目标速度:30 RPM */g_run_flag = 1; /* 标记电机启动 */debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */}
#endift++;if(t % 20 == 0){lcd_dis(); /* 显示数据 */LED0_TOGGLE(); /*LED0(红灯) 翻转*/
#if DEBUG_ENABLEdebug_send_speed(g_motor_data.speed); /* 发送速度 */
#endif}delay_ms(10);}
}/*** @brief 数据显示函数* @param 无* @retval 无*/
void lcd_dis(void)
{char buf[32];/* 显示占空比(正负号为电机方向) */sprintf(buf, "PWM_Duty:%.1f%c ", (float)(g_motor_data.motor_pwm * 100 / 8400), '%');lcd_show_string(10, 150, 200, 16, 16, buf, g_point_color);/* 设置的目标速度 */sprintf(buf, "Set Speed :%3d RPM ", (int16_t)g_speed_pid.SetPoint);lcd_show_string(10, 180, 200, 16, 16, buf, g_point_color); /* 电机实际速度 */sprintf(buf, "Speed :%3d RPM ", (int16_t)g_motor_data.speed);lcd_show_string(10, 210, 200, 16, 16, buf, g_point_color);
}
#if DEBUG_ENABLE /* 开启调试 */debug_init(); /* 初始化调试 */debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) *//* 同步数据(选择第1组PID,目标速度地址,P,I,D参数)到上位机 */debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);#endif
首先初始化调试,就是将用来存放参数的结构体变量清零。
接着上传下电机类型
上传电机状态,
把此时Kp,Ki,Kd的值发给pid调试助手。
上传数据的函数。
这里我只上传了电机的速度,差不多200ms上传一次。
发送波形函数
这里我上传实际速度,目标速度,占空比值,每50ms上传一次
这里我上传三个通道,
通道1上传实际速度,
通道2上传目标速度
通道3上传占空比
接受上位机发送数据函数
这里设定目标范围,已经设定目标的变化上限。
读取上位机指令。
判断指令是否是刹车,然后执行相关操作。
判断指令是否是运行,然后执行相关操作。
在主函数的while循环里一直调用这些函数。
解析数据接受和数据发送的底层函数
数据接受
通过串口来接收上位机发来的数据包(中断接收),只要上位机一发送数据,就会产生串口中断,然后在接受中断回调函数里族逐个字节来接收数据,并解析数据。
数据帧格式
/*** @brief 上位机数据解析* @param *data:接收的数据(地址)* @note 利用环形缓冲区来接收数据,再存进相应的结构体成员中* @retval 无*/
void debug_handle(uint8_t *data)
{uint8_t temp[DEBUG_REV_MAX_LEN];uint8_t i;if (debug_rev_p >= DEBUG_REV_MAX_LEN) /* 超过缓冲区(数组)最大长度 */{debug_rev_p = 0; /* 地址偏移量清零 */}debug_rev_data[debug_rev_p] = *(data); /* 取出数据,存进数组 */if (*data == DEBUG_DATA_END) /* 判断是否收到帧尾 */{if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD) /* 数据包长度为5个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 2; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + i) % DEBUG_REV_MAX_LEN]; /* 取出帧头、数据类别,5个字节的数据包没有数据域 */}#if EN_CRC /* 进行CRC校验 */if (crc16_calc(temp, 2) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 3) % DEBUG_REV_MAX_LEN]))
#endif{if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 1) % DEBUG_REV_MAX_LEN] == CMD_GET_ALL_DATA) /* 判断数据类别是否为:获取全部参数 */{debug_upload_data(&g_debug, TYPE_STATUS); /* 发送电机状态 */debug_upload_data(&g_debug, TYPE_SPEED); /* 发送速度值 */debug_upload_data(&g_debug, TYPE_HAL_ENC); /* 发送霍尔、编码器位置 */debug_upload_data(&g_debug, TYPE_VBUS); /* 发送电压 */debug_upload_data(&g_debug, TYPE_AMP); /* 发送电流 */debug_upload_data(&g_debug, TYPE_TEMP); /* 发送温度 */debug_upload_data(&g_debug, TYPE_SUM_LEN); /* 发送总里程 */debug_upload_data(&g_debug, TYPE_BEM); /* 发送反电动势 */debug_upload_data(&g_debug, TYPE_MOTOR_CODE); /* 发送电机类型 */for (i = TYPE_PID1; i < TYPE_PID10; i++){debug_upload_data(&g_debug, i); /* 发送PID参数 */}}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD) /* 数据包长度为6个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 3; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + i) % DEBUG_REV_MAX_LEN]; /* 取出帧头、数据类别、数据域 */}
#if EN_CRC /* 进行CRC校验 */if (crc16_calc(temp, 3) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 3) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 4) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 1) % DEBUG_REV_MAX_LEN]) /* 判断数据类别 */{case CMD_SET_CTR_CODE: /* 下发控制指令 */debug_rev.Ctrl_code = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];break;case CMD_SET_CTR_MODE: /* 下发控制模式 */debug_rev.Ctrl_mode = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];break;}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD) /* 数据包长度为7个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 4; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + i) % DEBUG_REV_MAX_LEN]; /* 取出帧头、数据类别、数据域 */}
#if EN_CRC /* 进行CRC校验 */if (crc16_calc(temp, 4) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 4) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 5) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 1) % DEBUG_REV_MAX_LEN]) /* 判断数据类别 */{case CMD_SET_SPEED: /* 设定电机速度 */*(debug_rev.speed) = (int16_t)((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN]);break;case CMD_SET_TORQUE: /* 设定转矩 */*(debug_rev.torque) = (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN];break;}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD) /* 数据包长度为17个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 14; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + i) % DEBUG_REV_MAX_LEN]; /* 取出帧头、数据类别、数据域 */}
#if EN_CRC /* 进行CRC校验 */if (crc16_calc(temp, 14) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 14) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 15) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN]) /* 判断数据类别 */{case CMD_SET_PID1:case CMD_SET_PID2:case CMD_SET_PID3:case CMD_SET_PID4:case CMD_SET_PID5:case CMD_SET_PID6:case CMD_SET_PID7:case CMD_SET_PID8:case CMD_SET_PID9:case CMD_SET_PID10:for (i = 0; i < 12; i++) /* 接收设定的PID参数 */{g_debug.pid[debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN] - CMD_SET_PID1].pid.pidi8[i] = \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 2 + i) % DEBUG_REV_MAX_LEN];}break;}}}}debug_rev_p ++;
}
首先先来说一下上位机的解析函数
上位机数据的解析函数是用环形数组的形式来存放,这样不会产生内存碎片,通过串口不断接受数组,然后把接受到的数组按照环形的顺序赋予给数值,并判断是否是帧尾,如果是帧尾,则接着判断是几字节的包,并且首字节是否是帧头,这里四种四节的包,分别是5字节,6字节,7字节,17字节。所以可以看到代码里进行了四个if语句判断分别是什么字节,并且首字节是否为帧头,然后分别进行crc校验,如果校验正确,则把表示数组类别和数据域的包给取出来,然后根据通信协议,将参数存放结构体变量进行对应赋值。
四个if语句
环形数组以及怎么找到它的帧头位置
例如数组大小为17的数组,当它的地址偏移量到达17的时候,则重新从0开始赋值。
我们现在已经知道帧尾的位置了,接下来如何找到帧头的位置呢?
这就和上面的四个if语句对应上了,例如我们要数据包长度为5的帧头,
我们就可以用((当前的地址偏移量+数组长度-(数据包长度-1))% 数组长度)来找到帧头的位置。
crc校验
本例程数据通过crc校验进行验证。
CRC定义:
CRC即循环冗余校验码,是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
取出帧头、数据类别、数据域进行crc校验
代码:
数据发送
数据上传函数
构建数组包:
先定义一个数组 upload_data[37],为什么是37呢,因为最长的数据包是37byte。
判断数据类别并进行指令存储:
根据upload_type的值来确定数据类别,然后进行把数据域值赋给upload_data数组。
判断是否未数据类别错误,如果不是,则进行crc校验,然后把指令发送给pid调试助手。
相关文章:

基于正点原子电机实验的pid调试助手代码解析(速度环控制)
这里写目录标题 下位机与PID调试助手传输的原理代码讲解(基于正点原子)解析数据接受和数据发送的底层函数数据接受数据帧格式环形数组以及怎么找到它的帧头位置crc校验 数据发送数据上传函数 通过前两节文章,我已经了解了基本的pid算法,现在在完成了电机…...

报表设计器Stimulsoft 2023.2提供深色主题和 Monoline 图标包
Stimulsoft Reports 是一款报告编写器,主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署,如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等,在你的应用程序中嵌入报告设计器…...

文本三剑客之——sed编辑器
sed编辑器 sed编辑器sed基础语法sed查询sed删除sed 替换sed 插入 sed编辑器 sed是文本处理工具,依赖于正则表达式,可以读取文本内容,工具指定条件对数据进行添加、删除、替换等操作,被广泛应用于shell脚本,以完成自动…...

华为OD机试真题 Java 实现【贪心的商人】【2023Q1 100分】
一、题目描述 商人经营一家店铺,有number种商品,由于仓库限制每件商品的最大持有数量是item[index],每种商品的价格在每天是item_price[item_index][day],通过对商品的买进和卖出获取利润,请给出商人在days天内能获取到的最大利润。 注:同一件商品可以反复买进和卖出;…...

《数据结构与算法C++版》实验二-链表实验
一、实验内容 实验目的 1、实现线性表的链式存储结构(链表)。 2、熟悉 C++程序的基本结构,掌握程序中的头文件、实现文件和主文件之间的 相互关系及各自的作用。 3、熟悉链表的基本操作方式,掌握链表相关操作的具体实现。 实验内容 对链式存储结构的线性表进行一些基本操作…...

【2023华为OD笔试必会25题--C语言版】《06 简单的自动曝光》——数组
本专栏收录了华为OD 2022 Q4和2023Q1笔试题目,100分类别中的出现频率最高(至少出现100次)的25道,每篇文章包括原始题目 和 我亲自编写并在Visual Studio中运行成功的C语言代码。 仅供参考、启发使用,切不可照搬、照抄,查重倒是可以过,但后面的技术面试还是会暴露的。✨✨…...

Science Advances:宋艳课题组发现经颅近红外激光刺激可提升人类工作记忆
图1. 新闻稿封面 工作记忆——在几秒钟内主动“记住”有用信息的能力——在许多高级认知活动中起着至关重要的作用。由于工作记忆能力的个体差异可以预测流体智力和广泛的认知功能,这使得提高工作记忆能力成为干预和增强的有吸引力的目标。 美国食品及药品管理局声…...

Linux系统crash后定位方法-PCIE举例
crash解释 在Linux操作系统中,"crash"通常是指一种用于分析系统崩溃(crash)的工具或方法。当系统发生崩溃时,可能会产生一些关键信息,如错误日志、内存转储文件等。使用crash工具可以分析这些信息ÿ…...

瑞吉外卖 - 启用与禁用员工账号功能(8)
某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…...

【MySQL】索引
记录MySQL学习笔记,大部分图片来自黑马程序员MySQL教程。 文章目录 概述索引结构BTree为什么InnoDB使用BTree索引结构? 索引分类索引语法SQL性能分析1、查看执行频次2、慢查询日志3、profile详情4、explain执行计划 索引使用最左前缀法则索引失效情况1、…...

JavaScript全解析——express
express 的基本使用 ●express 是什么? ○是一个 node 的第三方开发框架 ■把启动服务器包括操作的一系列内容进行的完整的封装 ■在使用之前, 需要下载第三方 ■指令: npm install express 1.基本搭建 // 0. 下载: npm install express// 0. 导入 const express express()…...

【JavaScript数据结构与算法】字符串类(计算二进制子串)
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃个人状态: 在校大学生一枚,已拿多个前端 offer(…...

TCP连接不释放,应用产生大量CLOSE_WAIT状态TCP
一、起源 23年元旦期间,大家都沉浸在一片祥和的过节气氛当中。 “滴滴滴”,这头同事的电话响起,具体说些什么我也没太在意,但见同事接完电话之后展现出了一副懊恼夹杂着些许不耐烦的表情。 我不解问道:“怎么了&…...

Spring基础核心概念理解(常见面试题:什么是IoC?什么是DI?什么是Spring?)
目录 IoC 和 SpringIoC DI Spring IoC 和 SpringIoC IoC是控制反转的意思,它意味着控制权(依赖对象)的反转,将控制权进行反转,它是一种思想. 举个例子,理解一下什么是控制反转 现在有三个对象A,B,C. A的创建依赖于B,B的创建依赖于C,当我们想要创建A的时候创建B,同理也要…...

牛客小白月赛 D.遗迹探险 - DP
题目描述 小Z是一名探险家。有一天,小Z误入了一个魔法遗迹。以下是该遗迹的具体组成: 1. 在 x 轴和 y 轴构成的平面上,满足在 1≤x≤n,1≤y≤m 的区域中(坐标(x,y)表示平面上的第x行的第y列),每个整数坐标 (x,y) 都有…...

前端架构师-week6-require源码解析
require 源码解析——彻底搞懂 npm 模块加载原理 require 的使用场景 加载模块类型 加载内置模块:require(fs)加载 node_modules 模块:require(ejs)加载本地模块:require(./utils)支持文件类型 加载 .js 文件加载 .mjs 文件加载 .json 文件…...

作为 IT 行业的过来人,你有什么话想对后辈说的?
作为 IT 行业的过来人,我想对后辈们说,要不断学习和探索新技术,但同时也要注意保持专注和耐心。在这个快速变化的时代,技术更新换代太快,可能会让人感到焦虑和无助,但只要有耐心并专注于自己所做的事情&…...

表数据编辑(数据库)
目录 一、插入数据 1.插入单个元组: INSERT…VALUES语句 2.插入子查询的结果: INSERT…SELECT语句 3.使用SELECT…INTO语句进行数据插入 二、修改数据 1、数据修改语句:UPDATE 2、修改给定表的所有行 3、基于给定表修改某…...

考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Python代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

RocketMQ整理
RocketMQ在阿里云上的商业版本,集成了阿里内部一些更深层次的功能及运维定制。开源版本,功能上略有缺失,但大体上是一样的。 使用Java开发,便于深度定制。最早叫MetaQ。消息吞吐量虽然依然不如Kafka,但是却比RabbitMQ高很多。在阿里内部,RocketMQ集群每天处理的请求数超过…...

Springboot +Flowable,会签、或签简单使用(二)
一.简介 **会签:**在一个流程中的某一个 Task 上,这个 Task 需要多个用户审批,当多个用户全部审批通过,或者多个用户中的某几个用户审批通过,就算通过。 例如:之前的请假流程,假设这个请假流程…...

将核心交换机配置为NTP服务器
AR配置外源NTP 1.配置ntp <XQ-R1220>sys [XQ-R1220]ntp-service unicast-server 120.25.115.20 #阿里云ntp [XQ-R1220]ntp-service unicast-server 203.107.6.88 #阿里云ntp 2.查看ntp状态 <XQ-R1220>display ntp status clock sta…...

application.properties文件注释
这是一个常用的Spring Boot配置文件 在这里,我们可以配置应用程序的各种属性 服务器端口号 server.port8080 数据库配置 spring.datasource.urljdbc:mysql://localhost:3306/test spring.datasource.usernameroot spring.datasource.password123456 spring.datasou…...

MySql查询报错this is incompatible with sql_mode=only_full_group_by
错误示例 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘yiliaohaocai_new.a.id’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_modeonly_full_group_by 原因 SQL …...

VMware Workstation 网络备忘 + 集群规模
概述 在虚拟机中部署服务,进行IP规划,进行相关的前期准备 3 张网卡 2个不同的网段 1个NAT 概述截图 NAT 截图 VMnet0 截图 VMnet1 截图 总结: 网卡(网络适配器)名称IP网段备注NATens33192.168.139.0VMnet0ens34VMne…...

被裁现状,给找工作的同学一些建议
2022 到 2023 国内知名互联网公司腾讯、阿里、百度、快手、滴滴、京东、阿里、爱奇艺、知乎、字节跳动、小米等公司均有裁员,其中有不少公司,在过去年的一整年,进行了多轮裁员,以下是网传的一张 “2022 年裁员企业名单”。 这些裁…...

编程到底难在哪里?
编程是一门非常有挑战性的技术,能够让人们使用计算机来完成各种任务。它不仅需要掌握各种计算机语言和框架,还需要在实际应用中充分发挥自己的专业知识和创造力。 然而,对于初学者来说,在编程过程中遇到的难点可能是多方面的。以…...

C++ 仿函数(一)
目录 一、仿函数是什么? 二、仿函数的特点 1.仿函数在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值 2.仿函数超出普通函数的概念,可以有自己的状态 编辑3.仿函数可以作为参数传递。 三、谓词 一元谓词示例&a…...

MATLAB连续LTI系统的时域分析(十)
目录 1、实验目的: 2、实验内容: 1、实验目的: 1)掌握利用MATLAB对系统进行时域分析的方法; 2)掌握连续时间系统零输入响应的求解方法; 3)掌握连续时间系统零状态响应、冲激响应和…...

HBuilderX使用
HBuilderX使用(Vue前后端分离) 概述:DCloud开发者后台 DAccount Service 1、官网下载开发工具:HBuilderX-高效极客技巧 注意:安装目录路径中不能出现中文特殊字符,否则会造成项目无法编译。比如C:/Progr…...