前言

在有感电机控制中,获取电机转速是非常重要的步骤,转速获取越准确,控制电机时越方便,抛开霍尔不谈,这里讨论电机编码器。

目前常见的电机编码器按种类分为绝对值编码器和增量编码器,绝对值编码器相对较为方便,编码器直接通过通讯接口输出绝对位置信息,MCU接收角度信息便可。而增量编码器就需要MCU测量编码器输出的脉冲信号,通过计算脉冲信号的频率,从而计算实际转速,同时通过A B两相的相位差获取旋转方向。通过这样的方式测量转速一般有三种方法,下面就这三种方法展开介绍,并分析每个方法的优劣势。

一、测频率法(M法)

M法测速又称“定时测角法”,指在一定周期T内,测量编码器输出的脉冲个数M1,然后计算出转速。
n = 60 ∗ M 1 / ( p T ) n = 60*M_1/(pT) n=60∗M1​/(pT)

首先已知电机旋转一周,编码器会产生P个脉冲信号,P由编码器线数和倍频数的乘积决定,例如2500线4倍频编码器,则P = 2500*4 = 10000。在一定时间周期T内,测量编码器输出的脉冲个数M1,则编码器输出频率为:
f = M 1 / T f = M_1/T f=M1​/T
用频率f除以一圈脉冲数p就可以得到电机转速n:
n = f / p ( r / s ) n = f/p (r/s) n=f/p(r/s)
电机转速单位为r/min,所以还需要再乘以60:
n = f / p ∗ 60 = 60 ∗ M 1 P T n = f/p *60 = \dfrac {60*M_1}{PT} n=f/p∗60=PT60∗M1​​
电机一圈脉冲数P和周期T一般是已知常数,那电机的转速和单位时间内的脉冲计数M1成正比,试想,当单位时间内捕获到M1个脉冲,刚好未捕捉到M1+1个脉冲,此时误差最大,最大误差为一个脉冲,所以误差率为:
δ m a x = 1 M 1 ∗ 100 % \delta _{max} = \dfrac {1}{M_1}*100\% δmax​=M1​1​∗100%
测速误差率与脉冲个数成反比关系,转速越高 M1 值越大,当转速很低时,M1 值很小,误差率会变大,因此 M 法适合高速测量

二、测周期法(T法)

T 法又称“定角测时法”,是测量编码器相邻两个脉冲之间的时间间隔来计算转速,也被称为周期法。实际使用中通过一个高频时钟脉冲的个数 M2 来计算编码器两个脉冲之间的时间间隔。

另高频脉冲频率为f0,两个相邻脉冲之间的时间间隔Tt = M2/f0,电机的转速可以表示为:
n = 60 p T t = 60 f 0 p M 2 n = \dfrac {60}{pTt} = \dfrac {60f_0}{pM_2} n=pTt60​=pM2​60f0​​
T法测速误差来源于高频脉冲数量,最多可能产生一个高频脉冲的误差,当产生了一个脉冲误差时,最大误差率为:
δ m a x = 60 f 0 p ( M 2 − 1 ) − 60 f 0 p M 2 60 f 0 p M 2 ∗ 100 % = 1 M 2 − 1 ∗ 100 % \delta _{max} = \dfrac {\dfrac {60f_0}{p(M_2-1)}-\dfrac {60f_0}{pM_2}}{\dfrac {60f_0}{pM_2}}*100\% = \dfrac {1}{M_2-1}*100\% δmax​=pM2​60f0​​p(M2​−1)60f0​​−pM2​60f0​​​∗100%=M2​−11​∗100%
这里可以预料到的是,转速越快,间隔时间Tt越短,高频脉冲个数M2越少,当丢失一个高频脉冲数,就会对转速造成很大影响。当转速越慢,单位时间的M2越多,误差越小。因此T法测速更适合低转速应用

三、M/T法测速

以采样周期为基准,在采样时间T内,同时计算编码器输出的脉冲数量M1和高频脉冲数量M2,同时尽量保持两个计数时间的的严格同步,最大限度减小误差。

设电机旋转一圈编码器输出脉冲数为p,高频脉冲频率为fc,T时间内,高频脉冲计数为M2,编码器输出脉冲数为M1,则此时T时间内电机旋转圈数:
n = M 1 p n = \dfrac {M_1}{p} n=pM1​​
一秒电机旋转圈数为:
n = M 1 p T n = \dfrac {M_1}{pT} n=pTM1​​
每分钟的转速为:
n = 60 ∗ M 1 p T n = 60*\dfrac {M_1}{pT} n=60∗pTM1​​
T可通过高频脉冲计数原理求得:
T = M 2 f c T = \dfrac {M_2}{f_c} T=fc​M2​​
此种方式在低速和高速都有很高的分辨率,最适合宽速度检测范围使用。

四、如何在GD32单片机上实现上述过程

这里本人从M法测速开始,发现低转速速度误差太大,于是升级了M/T法测速,这里仅介绍这两种的实现,T法就不做实现,其实M/T能实现,T法就一定不会出问题啦。下文将逐步讲解我的开发历程。

1.试验资料

a.GD32E230C8T6
b.LME2500FE 2500线四倍频磁编码器
c.纸飞机串口助手
d.伺服电机+驱动器

2.M法测速实现及试验结果

在设计初期,使用定时器2的通道0输入捕获来获取编码器的输出脉冲M1,同时使用定时器0作为时间T。
首先将定时器2设置为输入捕获,同时开启通道0的中断,每捕获到一个脉冲则进入一次中断,将标志位+1;定时器0作为普通中断,开启溢出中断,定时检测捕获数量。计算完成存入数组,在主函数转换为速度,通过串口发送到PC进行显示。

void timer2_configuration(void)
{gpio_configuration();timer_ic_parameter_struct timer_icinitpara;timer_parameter_struct timer_initpara;/* enable the TIMER clock */rcu_periph_clock_enable(RCU_TIMER2);timer_deinit(TIMER2);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 0;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 65535;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_init(TIMER2, &timer_initpara);timer_channel_input_struct_para_init(&timer_icinitpara);timer_icinitpara.icpolarity  = TIMER_IC_POLARITY_BOTH_EDGE;timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;timer_icinitpara.icfilter    = 0x0;timer_input_pwm_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara);timer_input_trigger_source_select(TIMER2, TIMER_SMCFG_TRGSEL_CI0FE0);timer_slave_mode_select(TIMER2, TIMER_SLAVE_MODE_RESTART);timer_master_slave_mode_config(TIMER2, TIMER_MASTER_SLAVE_MODE_ENABLE);timer_auto_reload_shadow_enable(TIMER2);timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);timer_interrupt_enable(TIMER2, TIMER_INT_CH0);timer_enable(TIMER2);
}void timer_configuration(void)
{timer_ic_parameter_struct timer_icinitpara;timer_parameter_struct timer_initpara;rcu_periph_clock_enable(RCU_TIMER0);timer_deinit(TIMER0);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 71;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 1999;//1MStimer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER0, &timer_initpara);timer_auto_reload_shadow_enable(TIMER0);timer_interrupt_flag_clear(TIMER0,TIMER_INT_FLAG_UP);timer_interrupt_enable(TIMER0,TIMER_INT_FLAG_UP);timer_enable(TIMER0);
}void TIMER2_IRQHandler(void)
{if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_CH0)){timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);readvalue2++;}
}
void TIMER0_BRK_UP_TRG_COM_IRQHandler(void)
{if(SET == timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP)){timer_interrupt_flag_clear(TIMER0, TIMER_FLAG_UP);if(readvalue2 > readvalue1){count = (readvalue2 - readvalue1); }else{count = ((0xFFFFU - readvalue1) + readvalue2); }TIM_NUM++;readvalue1=readvalue2;if(TIM_NUM>999&TIM_NUM<1999){Speed_num[TIM_NUM-1000] = count+1;                        }if(TIM_NUM>1999){timer_disable(TIMER0);}}
}

在定时器0的中断响应函数中,对TIMER0的当前捕获值和上一次捕捉只进行运算,得出单位时间T内的脉冲数量,并将值保存进Speed_num数组,保存到1000个TIMER0失能中断,主函数开始处理数据并发送到上位机。

    while(1){if(TIM_NUM>1999){                        for(uint16_t p=0;p<999;p++){kalman_height = Speed_num[p]*60/10000*0.02;//                                                kalman_height=kalmanFilter(&KFP_height,kalman_height);printf("{Speed:%f}\n",kalman_height);                                        }TIM_NUM = 0;timer_enable(TIMER0);        }}

其中,M1 = Speed_num[p],P = 2500*4 = 10000,T = 20ms =0.02s。

开启伺服电机驱动器,设置速度3000转,先使用驱动器读取一下速度波动,如下图一所示:

图一:伺服驱动读取速度波动-3000rp/m
再通过串口助手获取单片机M法测速速度波动,如下图二所示:

图二:M法测速测得速度波动-3000rp/m
因为两张图的采样频率不同,显示的效果也会不同,而且驱动器会进行数据滤波,但是还是可以看出速度分布在3000附近,下面我们将速度数据卡尔曼滤波(后文所有的图均默认进行滤波处理),如下图三所示。

图三:M法卡尔曼滤波速度-3000rp/m
由上图三和图一进行比较,可以发现我们使用GD32的M法测速在3000转时表现较好,基本和驱动器测得数据较为吻合,说明方法是没问题的,于是我们降低转速,继续对比。

图四:伺服驱动读取速度波动-2000rp/m


图五:M法卡尔曼滤波速度-2000rp/m

图六:伺服驱动读取速度波动-1000rp/m

图七:M法卡尔曼滤波速度-1000rp/m
从图四-图七,对比就会发现,在2000rp/m时,速度已经发生了偏移,速度点以2002rp/m为中心分布,在1000rp/m时,速度点以2004rp/m为中心分布,速度越低,产生的速度误差越大。

3. M/T法测速实现及试验结果

初期试验:

一开始我使用三个定时器,定时器2继续使用M法的捕获功能,捕获M1的值,使用定时器14产生高频脉冲,每当计数器到达就触发中断,获取M2的值,最后在定时器0中定时获取M1和M2的值,在主函数打印。此方法在3500rp/m以下均无问题,但是速度再提高,编码器输出的频率增高,导致中断处理不过来了,M1不再随着转速的提高而提高,速度显示也就无法提高了。后来想到这块MCU的定时器是有正交译码器的。如此便不会受中断的影响。

GD32E230之正交译码器的M/T法测速。

介于本篇文章篇幅已经很长了, 就不介绍定时器的正交译码器功能了,该功能网络上资料很多,这里不赘述,我们从代码入手:

int rmp;
int m1,m2;
void gpio_configuration(void)
{/* enable the GPIOA clock */rcu_periph_clock_enable(RCU_GPIOA);/*configure PA6(TIMER2 CH0) as alternate function*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);        gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_7);
//        gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
}
/**定时器2  用于捕获编码器输出脉冲数*/
void timer2_configuration(void)
{gpio_configuration();timer_ic_parameter_struct timer_icinitpara;timer_parameter_struct timer_initpara;/* enable the TIMER clock */rcu_periph_clock_enable(RCU_TIMER2);/* deinit a TIMER */timer_deinit(TIMER2);/* initialize TIMER init parameter struct */timer_struct_para_init(&timer_initpara);/* TIMER2 configuration */timer_initpara.prescaler         = 0;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 65535;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_init(TIMER2, &timer_initpara);/* TIMER2 configuration *//* initialize TIMER channel input parameter struct */timer_channel_input_struct_para_init(&timer_icinitpara);/* TIMER2 CH0 PWM input capture configuration */timer_icinitpara.icpolarity  = TIMER_IC_POLARITY_RISING;timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;timer_icinitpara.icfilter    = 0x0;timer_input_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara);timer_input_capture_config(TIMER2, TIMER_CH_1, &timer_icinitpara);timer_quadrature_decoder_mode_config(TIMER2, TIMER_ENCODER_MODE2, TIMER_IC_POLARITY_BOTH_EDGE, TIMER_IC_POLARITY_BOTH_EDGE);timer_slave_mode_select(TIMER2, TIMER_ENCODER_MODE2);/* auto-reload preload enable */timer_auto_reload_shadow_enable(TIMER2);/* clear channel 0 interrupt bit */timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
//    /* channel 0 interrupt enable */timer_interrupt_enable(TIMER2, TIMER_INT_UP);/* TIMER2 counter enable */timer_enable(TIMER2);
}
/**定时器0  用于周期获取脉冲数和高频时钟脉冲***/
void timer_configuration(void)
{timer_ic_parameter_struct timer_icinitpara;timer_parameter_struct timer_initpara;/* enable the TIMER clock */rcu_periph_clock_enable(RCU_TIMER0);/* disable a TIMER */timer_deinit(TIMER0);/* initialize TIMER init parameter struct */timer_struct_para_init(&timer_initpara);/* TIMER2 configuration */timer_initpara.prescaler         = 7199;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 9;//1Stimer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER0, &timer_initpara);/* auto-reload preload enable */timer_auto_reload_shadow_enable(TIMER0);/* clear channel 0 interrupt bit */timer_interrupt_flag_clear(TIMER0,TIMER_INT_FLAG_UP);/* channel 0 interrupt enable */timer_interrupt_enable(TIMER0,TIMER_INT_FLAG_UP);/* TIMER2 counter enable */timer_enable(TIMER0);
}/****定时器14  用于产生高频时钟脉冲****/
void timer14_configuration(void)
{timer_ic_parameter_struct timer_icinitpara;timer_parameter_struct timer_initpara;/* enable the TIMER clock */rcu_periph_clock_enable(RCU_TIMER14);/* disable a TIMER */timer_deinit(TIMER14);/* initialize TIMER init parameter struct */timer_struct_para_init(&timer_initpara);/* TIMER2 configuration */timer_initpara.prescaler         = 1;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 359;//624us采样一次timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER14, &timer_initpara);/* auto-reload preload enable */timer_auto_reload_shadow_enable(TIMER14);/* clear channel 0 interrupt bit */timer_interrupt_flag_clear(TIMER14,TIMER_INT_FLAG_UP);/* channel 0 interrupt enable */timer_interrupt_enable(TIMER14,TIMER_INT_FLAG_UP);/* TIMER2 counter enable */timer_enable(TIMER14);
}void TIMER2_IRQHandler(void)
{if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP)){timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);}
}
void TIMER14_IRQHandler(void)
{if(SET == timer_interrupt_flag_get(TIMER14, TIMER_INT_FLAG_UP)){timer_interrupt_flag_clear(TIMER14, TIMER_FLAG_UP);m2++;}
}
int m1_data[point_num],m2_data[point_num];
void TIMER0_BRK_UP_TRG_COM_IRQHandler(void)
{if(SET == timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP)){timer_interrupt_flag_clear(TIMER0, TIMER_FLAG_UP);m1 = timer_counter_read(TIMER2);if(TIM_NUM<point_num){m1_data[TIM_NUM] = m1;m2_data[TIM_NUM] = m2;TIM_NUM++;m1 = 0;m2 = 0;}else{timer_disable(TIMER0);        timer_disable(TIMER2);        timer_disable(TIMER14);                }}
}

TIMER2作为高级定时器,拥有正交译码器的功能,我们将其通道0和通道1接到编码器的A和B相,初始化TIMER2为译码器模式。

  timer_quadrature_decoder_mode_config(TIMER2, TIMER_ENCODER_MODE2, TIMER_IC_POLARITY_BOTH_EDGE, TIMER_IC_POLARITY_BOTH_EDGE);timer_slave_mode_select(TIMER2, TIMER_ENCODER_MODE2);

主要就是这两个函数,其他的几乎和普通定时器设置差不多。

TIMER14用于产生一个100K的高频时钟脉冲,并使能溢出中断,每产生一个脉冲则将M2的值累加1;

定时器0 用于周期获取脉冲数和高频时钟脉冲数,并使能溢出中断,中断到达后读取TIMER2的计数值,该值的变化量与编码器输出脉冲相同,计算两次之间的差值即为每次采样脉冲间隔的编码器脉冲数。中断中将M1和M2的值存入数组,在主函数进行速度计算并输出:

    while(1){if(TIM_NUM>point_num-2){                        for(uint16_t p=5;p<point_num-2;p++){m1calc = m1_data[p]-m1_data[p+1];if(m1calc<0){m1calc = m1calc+65535;                                                }Speed_num[p] = (float)(60*m1calc*100000)/(m2_data[p]*10000);kalman_height = kalmanFilter(&KFP_height,Speed_num[p]);
//                                                printf("{Speed:%d,%d,%d}\n",(int)(Speed_num[p]+0.5),m1calc,m2_data[p]);printf("{Speed:%d}\n",(int)(kalman_height+0.5));                                        }TIM_NUM = 0;start_flag = 0;timer_enable(TIMER0);        timer_enable(TIMER2);        timer_enable(TIMER14);        }}

案例中:m1calc为M1,100000为fc,即编码器输出脉冲数。m2_data[p]为M2,即高频脉冲计数。kalmanFilter()函数为卡尔曼滤波器,此处不便公开。

最终效果如下:

在3000RP/M表现基本无差异:

图八:M/T法卡尔曼滤波速度-3000rp/m
在低速表现优异

图九:M/T法卡尔曼滤波速度-1000rp/m

图十:伺服驱动读取速度波动-100rp/m


图十一:M/T法卡尔曼滤波速度-100rp/m
高速表现同样没问题

图十二:伺服驱动读取速度波动-5000rp/m

图十三:M/T法卡尔曼滤波速度-5000rp/m

五、总结

M/T法测速在增量型编码器中还是很常用的测速方式,再结合相应的滤波算法,可以得到非常精准的速度信息,同时MCU的译码器模式可同时获取到电机的旋转方向,可自行探索。上文中的编码器未经校准,精度表现一般,于本次实验结果无关。

【基于GD32E230的定时器级联M/T法电机测速】相关推荐

  1. 基于MSP430f5529 编码电机测速 接收脉冲数 PWM调速 CCS编译器 代码分析

    前言:2022年TI杯大学生电子设计竞赛,小车跟随行驶系统(C题)要求:设计一套小车跟随行驶系统,采用TI的MCU,由一辆领头小车和一辆跟随小车组成,要求小车具有循迹功能,且速度在0.3~1m/s可调 ...

  2. STM32定时器编码器模式实现直流有刷电机测速(HAL库)

    前言 最近在做一个单片机大作业,要用到直流有刷,在这里把学习编码器的知识记录一下,学习参考资料: 正点原子DMF407电机控制专题教程_V1.0 编码器测速原理 我所使用的编码器是市面上常见的磁电增量 ...

  3. 51单片机电机测速程序c语言,基于51单片机光电编码器测速.doc

    基于51单片机光电编码器测速 PAGE PAGE 2 课程设计报告 课程名称: 微机原理课程设计 题 目: 基于51单片机的光电编码器测速 摘要 光电编码器是高精度位置控制系统常用的一种位移检测传感器 ...

  4. 基于nginx配置的WordPress网站防御17ce等测速网站攻击指南

    本文原文:基于宝塔面板nginx配置的WordPress网站防御攻击指南-奇它博客 免插件实现WordPress网站屏蔽后台搜索词广告 WordPress网站批量更换文章固定链接(百度收录自动301跳 ...

  5. PLC高精度定时器(T法测速功能块)

    S7-200 SMART PLC时间间隔指令BGN_ITIME,和CAL_ITIME采用的是系统自带的1ms高精度定时器,PLC里只能调用一次.T法测速和M法测速应用时,都需要高精度时序定时器的支持( ...

  6. STM32定时器捕获编码器模式测速和方向测不准问题

    ** STM32定时器捕获编码器模式测速和方向测不准问题 问题概述 关于STM32编码器模式电机测速的资料网上一抓一大把,却发现真的拿过来用还是有问题的,比如刚刚做了个东西,是个个头比较大的麦克纳姆轮 ...

  7. TF之LoR:基于tensorflow利用逻辑回归算LoR法实现手写数字图片识别提高准确率

    TF之LoR:基于tensorflow利用逻辑回归算LoR法实现手写数字图片识别提高准确率 目录 输出结果 设计代码 输出结果 设计代码 #TF之LoR:基于tensorflow实现手写数字图片识别准 ...

  8. OpenCV Using Python——基于SURF特征提取和金字塔LK光流法的单目视觉三维重建 (光流、场景流)...

    https://blog.csdn.net/shadow_guo/article/details/44312691 基于SURF特征提取和金字塔LK光流法的单目视觉三维重建 1. 单目视觉三维重建问题 ...

  9. 强化学习:7基于直接策略搜索的强化学习⽅法 之 策略梯度

    这是强化学习第三篇基于直接策略搜索的强化学习⽅法下的基于策略梯度.

最新文章

  1. 今天收到上海某公司的全英文笔试题(some question of interview )
  2. 如何使用 ClickHouse 每天玩转千亿数据,纯PPT干货
  3. 使用tracee编写规则追踪系统安全事件
  4. Maven 配置环境变量后无法立刻生效-原因是黑窗口是配置前打开的,重新打开即可
  5. react 动态路 嵌套动子路由_2020年,我是如何从一名Vueer转岗到React阵营
  6. window.onbeforeunload() 事件调用ajax的解决方法
  7. how does gateway framework treat default system flag in customizing
  8. Machine Learning:十大机器学习算法
  9. cmd 命令操纵文件管理器、创建(删除)多级文件夹
  10. 工厂和反射的使用方法
  11. mysql系列之4.mysql字符集
  12. 数据结构与算法之PHP排序算法(桶排序)
  13. (Adobe Premiere Pro CS4)[ISO]《Adobe非线性视频编辑软件》
  14. SQL Server 2019 安装教程(详细免费,自定义安装)
  15. 三角函数 弧度角度换算
  16. 金融危机背景下大学生就业
  17. C++primer plus第六版课后编程题答案14.3(仅供参考)
  18. 数据结构之图:邻接矩阵和邻接表、深度优先遍历和广度优先遍历
  19. 做题两大解题思想 by zyz on 2021/4/11
  20. 苹果打字怎么换行_iPhone实用小技巧,安卓转苹果的同学看完收获尤其大

热门文章

  1. [Vulhub] Weblogic 漏洞复现
  2. [BZOJ5267]特工
  3. 一看就懂的ReactJs入门教程(精华版)
  4. 途牛旅游网产品调研【产品经理】
  5. 企业备份方案设计干货参考:典型场景、典型问题及案例
  6. Python面向树莓派使用RPi.GPIO库
  7. 关于计算机网络中超网聚合问题的计算
  8. 放入一些平时收藏的网址(如果支持chrome式的书签管理或折叠式菜单进行分类就好了)
  9. springboot注解@Repeatable
  10. 卡诺图化简法注意事项+竞争冒险知识点总结