[导读]:在嵌入式系统中经常需要采集模拟信号,采集模拟信号的信号链中难免引入干扰,那么如何滤除干扰呢?今天就来个一步一步描述如何设计部署一个IIR滤波器到你的系统。

何为IIR滤波器?

无限冲激响应(IIRInfinite Impulse Response)是一种适用于许多线性时不变系统的属性,这些系统的特征是具有一个冲激响应h(t),该冲激响应h(t)不会在特定点上完全变为零,而是无限期地持续。 这与有限冲激响应(FIRFinite Impulse Response)系统形成对比,在有限冲激响应(FIR)系统中,对于某个有限T,在时间t> T时,冲激响应确实恰好变为零。 线性时不变系统的常见示例是大多数电子和数字滤波器。 具有此属性的系统称为IIR系统或IIR滤波器。

这是常见的教科书式数学严谨定义,很多人看到这一下就蒙了,能说人话吗?

线性时不变系统理论俗称LTI系统理论,源自应用数学,直接在核磁共振频谱学、地震学、电路、信号处理和控制理论等技术领域运用。它研究的是线性、非时变系统对任意输入信号的响应。虽然这些系统的轨迹通常会随时间变化(例如声学波形)来测量和跟踪,但是应用到图像处理和场论时,LTI系统在空间维度上也有轨迹。因此,这些系统也被称为线性非时变平移,在最一般的范围理论给出此理论。在离散(即采样)系统中对应的术语是线性非时变平移系统。由电阻、电容、电感组成的电路是LTI系统的一个很好的例子。比如一个运放系统在一定频带范围内满足信号的时域叠加,输入一个100Hz和200Hz正弦信号,输出频率是这两种信号的线性叠加。

用数学对LTI系统描述:

线性:输入\(x_1(t)\),产生响应\(y_1(t)\),而输入\(x_2(t)\),产生响应\(y_2(t)\),那么缩放和加和输入\(a_1x_1(t)+a_2x_2(t)\),产生缩放、加和的响应\(a_1y_1(t)+a_2y_2(t)\),其中\(a_1\)和\(a_2\)是标量,对于任意的有:输入\(\sum_{k=0}^{N}a_kx_k(t)\),产生响应\(\sum_{k=0}^{N}a_ky_k(t)\)

时不变性:指如果将系统的输入信号延迟\(\tau\)秒,那么得到的输出响应也相应延时\(\tau\)秒。用数学描述,也即如果输入\(x_1(t)\),产生响应\(y_1(t)\),而输入\(x_1(t+\tau)\),产生响应\(y_1(t+\tau)\)。这么描述还是不易懂,来个图,有图有真相:

假定一个信号放大电路对100Hz正弦信号放大2倍:

则输出为:

而对200Hz的正弦信号,其放大倍数为1.7倍。(做过运放电路设计的朋友应该有经验,在其同频带其放大倍数往往并不平坦,也即幅频响应在频带内不平坦,这是比较常见的)。

也即输入为:

响应为:

那么如果输入100Hz和200Hz的时域叠加信号,则其输入为:

则其响应为:

由这些图可看出,输入信号的形状保持不变,输出为对应输入的线性时域叠加。

对于时不变,就不用图描述了,在一个真实电路中,如果输入延迟一定时间,则响应对应延迟相同时间输出。

上面这么多文字只是为了描述在什么场合可以使用IIR滤波器对信号进行数字滤波。总结而言,就是在线性时不变系统中适用。换言之,在大多数电路系统中我们都可以尝试采用IIR滤波器进行数字滤波。

那么究竟什么是IIR滤波器呢?从数字信号处理的书籍中我们能看到这样的Z变换信号流图:

表示延迟一拍,在数字系统中表示对于输入信号而言,即为上一次采样值,对于输出而言,即为上一次的输出值。

在时域中对于上述流图,用时域描述即为:

如果熟悉Z变换,则Z变换传递函数为:

上述数字滤波器,如果从编程的角度来看,x(n-1),表示上一次的信号,可能是来自ADC的上次采样,而y(n-1)则为上一次滤波器的输出值,对应就比较好理解x(n-N)就表示前第n次输入样本信号,而y(n-M)则为前第M次滤波器的输出。

说了这么多,只是为了更好的理解概念,只有概念理解正确,才能用争取。 概念理解这对工程师而言,非常之重要。

如何设计呢?

打开fdatool

MATLAB提供了非常容易使用的FDATool帮助我们设计数字滤波器,真正精彩的地方开始了,让我们拭目以待究竟如何一步一步设计并实施一个IIR滤波器。

首先打开MATLAB,在命令行中敲fdatool

弹出窗体就是fdatool了,如下:

在设计具体,有几个相关概念需要澄清:

Fs:采样率,单位为Hz,真实部署在系统中,请务必确保样本是按照恒定采样率进行采样,否则将得不到想要的效果。

Fpass: 通频带,单位为Hz,即系统中期望通过的最高频率。

Fstop: 截至频率,即幅频响应的-3dB处的频率,这个如不理解,请自行查阅相关书籍。

分贝dB: 这是一个无单位反应输出与输入倍数的一个术语。电学中分贝与放大倍数的转换关系为:

  • A(V)(dB)=20lg(Vo/Vi);电压增益,Vo 为输出电压,Vi为输入电压
  • A(I)(dB)=20lg(Io/Ii);电流增益,Io 为输出电流,Ii为输入电流
  • A(p)(dB)=10lg(Po/Pi);功率增益,Po 为输出功率,Pi为输入功率

滤波器类型:这里有Butterworth(巴特沃斯)、Chebyshev Type I,Chebyshev Type II、(切比雪夫)、Elipic 等可选。

  • 巴特沃斯 Butterworth,也被称作最大平坦滤波器。巴特沃斯滤波器的特点是通频带内的频率响应曲线最大限度平坦,没有纹波。
  • 切比雪夫 Chebyshev,是在通带或阻带上频率响应幅度等波纹波动的滤波器。切比雪夫滤波器在过渡带比巴特沃斯滤波器的衰减快,但频率响应的幅频特性不如后者平坦。
  • 椭圆 Elliptic,椭圆滤波器是在通带和阻带等波纹的一种滤波器。
  • ...这里就不一一介绍了,有兴趣可以去查信号处理书籍。

就其特点,这里对其中几种略作介绍:

  • 巴特沃斯具有最平坦的通带。
  • 椭圆滤波器衰减最快,但是通带、阻带都有波纹。
  • 切比雪夫滤波器衰减比巴特沃斯快,但比椭圆滤波器慢,波纹区域可选择。

假设我们需要设计一个IIR滤波器,采样率为32000Hz, 有用信号频率在2000Hz内,设计IIR滤波器对信号进行数字滤波。这里为节省算力,我们指定滤波器的阶数,也即传递函数中N/M中的最大值,一般而言N大于M。

这里指定阶数为8阶,类型指定为巴特沃斯型IIR滤波器,输入阶数8阶,采样率32000Hz,截至频率设置为2000Hz然后点击Design Filter如下图所示:

其相频响应曲线如下:

楚辞之外,我们还可以将幅频与相频曲线放在一个频率坐标上去看设计结果:

导出滤波器参数,这里我们选择,

选择ASCII码

然后就得到了一个文件,保存2kHz_LPF.fcf,文件名随你喜欢。

文件内容如下:

% Generated by MATLAB(R) 8.4 and the Signal Processing Toolbox 6.22.
% Generated on: 27-Mar-2020 21:27:06 % Coefficient Format: Decimal % Discrete-Time IIR Filter (real)
% -------------------------------
% Filter Structure : Direct-Form II, Second-Order Sections
% Number of Sections : 4
% Stable : Yes
% Linear Phase : No SOS Matrix:
1 2 1 1 -1.7193929141691948 0.8610574795347461
1 2 1 1 -1.5237898734101736 0.64933827386370635
1 2 1 1 -1.4017399331200424 0.51723237044751591
1 2 1 1 -1.3435020629061745 0.45419615396638446 Scale Values:
0.035416141341387819
0.031387100113383172
0.028873109331868367
0.027673522765052503

至此设计工作就结束了,马上进入滤波器的部署测试阶段。

部署测试滤波器

到这里,没有经验的朋友可能会说,这么一堆参数我该咋用呢?

需要自己去写前面描述的计算公式吗?当然你也可以这么做,这里就不写了,ARM的CMSIS库已经帮大家设计好了种类繁多的数字信号处理函数实现了,而且经过了测试,这里直接拿来用即可。有兴趣自己写也不难,只要理解Z传递函数概念内涵,非常容易实现。这里我们采用32位浮点实现函数:arm_biquad_cascade_df1_f32。

该函数位于:CMSIS\DSP\Source\FilteringFunctions\arm_biquad_cascade_df1_init_f32.c中

以及CMSIS\DSP\Source\FilteringFunctions\arm_biquad_cascade_df1_f32.c

我们来看一看这个函数:

arm_biquad_cascade_df1_init_f32.c:

/*
*作用 :初始化滤波器
*S :指向浮点SOS级联结构的实例。
*numStages:滤波器中二阶SOS的数量
*pCoeffs :滤波器参数指针,参数按下列顺序存储
* {b10, b11, b12, a11, a12, b20, b21, b22, a21, a22, ...}
*pState :历史状态缓冲区指针
*/
void arm_biquad_cascade_df1_init_f32(
arm_biquad_casd_df1_inst_f32 * S,
uint8_t numStages,
const float32_t * pCoeffs,
float32_t * pState)
{
/* Assign filter stages */
S->numStages = numStages; /* Assign coefficient pointer */
S->pCoeffs = pCoeffs; /* Clear state buffer and size is always 4 * numStages */
memset(pState, 0, (4U * (uint32_t) numStages) * sizeof(float32_t)); /* Assign state pointer */
S->pState = pState;
}

arm_math.h 定义了须用到的结构体,对于本例相关的结构体为arm_biquad_casd_df1_inst_f32

typedef struct
{
unsigned int numStages; /*2阶节的个数,应为2*numStages. */
float *pState; /*状态系数数组指针,数组长度为4*numStages*/
float *pCoeffs; /*系数数组指针, 数组的长度为5*numStages.*/
} arm_biquad_casd_df1_inst_f32;

滤波器具体滤波函数为arm_biquad_cascade_df1_f32

/**
* *S :指向浮点Biquad级联结构的实例.
* *pSrc :指向输入数据块。
* *pDst :指向输出数据块。
* blockSize:每次调用要处理的样本数。
* 返回值 :无.
*/
void arm_biquad_cascade_df1_f32(
const arm_biquad_casd_df1_inst_f32 * S,
float * pSrc,
float * pDst,
unsigned int blockSize)
{
float *pIn = pSrc; /*源指针 */
float *pOut = pDst; /*目的指针 */
float *pState = S->pState; /*状态指针 */
float *pCoeffs = S->pCoeffs; /*参数指针 */
float acc; /*累加器 */
float b0, b1, b2, a1, a2; /*滤波器参数 */
float Xn1, Xn2, Yn1, Yn2; /*滤波器状态变量*/
float Xn; /*临时输入 */
unsigned int sample, stage = S->numStages; /*循环计数 */ do
{
/* Reading the coefficients */
b0 = *pCoeffs++;
b1 = *pCoeffs++;
b2 = *pCoeffs++;
a1 = *pCoeffs++;
a2 = *pCoeffs++; Xn1 = pState[0];
Xn2 = pState[1];
Yn1 = pState[2];
Yn2 = pState[3]; sample = blockSize >> 2u; while(sample > 0u)
{
/* 读第一个输入 */
Xn = *pIn++; /* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
Yn2 = (b0 * Xn) + (b1 * Xn1) + (b2 * Xn2) + (a1 * Yn1) + (a2 * Yn2); /* Store the result in the accumulator in the destination buffer. */
*pOut++ = Yn2; /* 每次计算输出后,状态都应更新. */
/* 状态应更新为: */
/* Xn2 = Xn1 */
/* Xn1 = Xn */
/* Yn2 = Yn1 */
/* Yn1 = acc */ /* Read the second input */
Xn2 = *pIn++; /* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
Yn1 = (b0 * Xn2) + (b1 * Xn) + (b2 * Xn1) + (a1 * Yn2) + (a2 * Yn1); /* 将结果存储在目标缓冲区的累加器中. */
*pOut++ = Yn1; /* 每次计算输出后,状态都应更新. */
/* 状态应更新为: */
/* Xn2 = Xn1 */
/* Xn1 = Xn */
/* Yn2 = Yn1 */
/* Yn1 = acc */ /*读第三个输入 */
Xn1 = *pIn++; /* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
Yn2 = (b0 * Xn1) + (b1 * Xn2) + (b2 * Xn) + (a1 * Yn1) + (a2 * Yn2); /* 将结果存储在目标缓冲区的累加器中. */
*pOut++ = Yn2; /* 每次计算输出后,状态都应更新. */
/* 状态应更新为: */
/* Xn2 = Xn1 */
/* Xn1 = Xn */
/* Yn2 = Yn1 */
/* Yn1 = acc */
/* 读第四个输入 */
Xn = *pIn++; /* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
Yn1 = (b0 * Xn) + (b1 * Xn1) + (b2 * Xn2) + (a1 * Yn2) + (a2 * Yn1); /* 将结果存储在目标缓冲区的累加器中. */
*pOut++ = Yn1; /* 每次计算输出后,状态都应更新. */
/* 状态应更新为: */
/* Xn2 = Xn1 */
/* Xn1 = Xn */
/* Yn2 = Yn1 */
/* Yn1 = acc */
Xn2 = Xn1;
Xn1 = Xn; /* 递减循环计数器 */
sample--;
} /* 如果blockSize不是4的倍数,
*请在此处计算任何剩余的输出样本。
*不使用循环展开. */
sample = blockSize & 0x3u; while(sample > 0u)
{
/* 读取输入 */
Xn = *pIn++; /* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
acc = (b0 * Xn) + (b1 * Xn1) + (b2 * Xn2) + (a1 * Yn1) + (a2 * Yn2); /* 将结果存储在目标缓冲区的累加器中. */
*pOut++ = acc; /* 每次计算输出后,状态都应更新。 */
/* 状态应更新为: */
/* Xn2 = Xn1 */
/* Xn1 = Xn */
/* Yn2 = Yn1 */
/* Yn1 = acc */
Xn2 = Xn1;
Xn1 = Xn;
Yn2 = Yn1;
Yn1 = acc; /* d递减循环计数器 */
sample--;
} /* 将更新后的状态变量存储回pState数组中 */
*pState++ = Xn1;
*pState++ = Xn2;
*pState++ = Yn1;
*pState++ = Yn2; /*第一阶段从输入缓冲区到输出缓冲区. */
/*随后的numStages在输出缓冲区中就地发生*/
pIn = pDst; /* 重置输出指针 */
pOut = pDst; /* 递减循环计数器 */
stage--; } while(stage > 0u);
}

开始测试:

#include <stdio.h>
#include <math.h>
/*
SOS Matrix:
1 2 1 1 -1.7193929141691948 0.8610574795347461
1 2 1 1 -1.5237898734101736 0.64933827386370635
1 2 1 1 -1.4017399331200424 0.51723237044751591
1 2 1 1 -1.3435020629061745 0.45419615396638446 Scale Values:
0.035416141341387819
0.031387100113383172
0.028873109331868367
0.027673522765052503
做如下转换:
1.缩放
[1 2 1] * 0.035416141341387819
[1 2 1] * 0.031387100113383172
[1 2 1] * 0.028873109331868367
[1 2 1] * 0.027673522765052503
得到:
[0.035416141341387819 2*0.035416141341387819 0.035416141341387819]
[0.031387100113383172 2*0.031387100113383172 0.031387100113383172]
[0.028873109331868367 2*0.028873109331868367 0.028873109331868367]
[0.027673522765052503 2*0.027673522765052503 0.027673522765052503]
2.舍掉第四列参数
3.将后两列分别乘以-1,即:
0.035416141341387819 2*0.035416141341387819 0.035416141341387819 -1.7193929141691948 0.8610574795347461
0.031387100113383172 2*0.031387100113383172 0.031387100113383172 -1.5237898734101736 0.64933827386370635
0.028873109331868367 2*0.028873109331868367 0.028873109331868367 -1.4017399331200424 0.51723237044751591
0.027673522765052503 2*0.027673522765052503 0.027673522765052503 -1.3435020629061745 0.45419615396638446
这样就得到了滤波器系数组了
*/
#define IIR_SECTION 4 /*见前面设计输出为4个SOS块*/
static float iir_state[4*IIR_SECTION];/*历史状态缓冲区 */
const float iir_coeffs[5*IIR_SECTION]={
0.035416141341387819,2*0.035416141341387819,0.035416141341387819,1.7193929141691948,-0.8610574795347461, 0.031387100113383172,2*0.031387100113383172,0.031387100113383172,1.5237898734101736,-0.64933827386370635, 0.028873109331868367,2*0.028873109331868367,0.028873109331868367,1.4017399331200424,-0.51723237044751591, 0.027673522765052503,2*0.027673522765052503,0.027673522765052503,1.3435020629061745,-0.45419615396638446
};
static arm_biquad_casd_df1_inst_f32 S;
/*假定采样512个点*/
#define BUF_SIZE 512
#define PI 3.1415926
#define SAMPLE_RATE 32000 /*32000Hz*/
int main()
{
float raw[BUF_SIZE];
float raw_4k[BUF_SIZE];
float raw_out[BUF_SIZE]; float raw_noise[BUF_SIZE];
float raw_noise_out[BUF_SIZE]; arm_biquad_casd_df1_inst_f32 S;
FILE *pFile=fopen("./simulation.csv","wt+");
if(pFile==NULL)
{
printf("file opened failed");
return -1;
} for(int i=0;i<BUF_SIZE;i++)
{
/*模拟9000Hz输入信号 */
raw[i] = 0.5*1024.0/3*sin(2*PI*800*i/32000.0f)+rand()%50;
raw_4k[i] = 0.5*1024.0/3*sin(2*PI*4000*i/32000.0f);
/*模拟9000Hz +11000Hz叠加输入*/
raw_noise[i] = raw[i] + raw_4k[i];
} arm_biquad_cascade_df1_init_f32(&S, IIR_SECTION, (float *)&iir_coeffs[0], (float *)&iir_state[0]);
arm_biquad_cascade_df1_f32(&S, raw, raw_out, BUF_SIZE); for(int i=0;i<BUF_SIZE;i++)
{
fprintf(pFile,"%f,",raw[i]);
} fprintf(pFile,"\n");
for(int i=0;i<BUF_SIZE;i++)
{
fprintf(pFile,"%f,",raw_4k[i]);
}
fprintf(pFile,"\n");
for(int i=0;i<BUF_SIZE;i++)
{
fprintf(pFile,"%f,",raw_out[i]);
} /*重新初始化*/
arm_biquad_cascade_df1_init_f32(&S, IIR_SECTION, (float *)&iir_coeffs[0], (float *)&iir_state[0]);
arm_biquad_cascade_df1_f32(&S, raw_noise, raw_noise_out, BUF_SIZE); fprintf(pFile,"\n");
for(int i=0;i<BUF_SIZE;i++)
{
fprintf(pFile,"%f,",raw_noise[i]);
} fprintf(pFile,"\n");
for(int i=0;i<BUF_SIZE;i++)
{
fprintf(pFile,"%f,",raw_noise_out[i]);
} fclose(pFile); return 0;
}

利用csv文件,将模拟数据存储,直接用excel打开,将行数据生成曲线图如下:

  • 第一幅图,为800Hz信号混入随机噪声的波形
  • 第二幅图,为4000Hz信号,对假定系统为无用干扰信号
  • 第三幅图, 为800Hz 混入随机噪声过滤后,已经很好的还原有用信号频率
  • 第四幅图, 为800Hz信号混入随机噪声,同时叠加4000Hz干扰的波形,对系统而言,从时域中,明显可见,有用信号已经完全扭曲
  • 第五幅图,为800Hz信号混入随机噪声,同时叠加4000Hz干扰的输入,经过该低通滤波器后的波形,与第三幅图基本一样,已经非常好的滤除了干扰信号。

总结

  • IIR滤波器在线性时不变系统中可以很好的解决工程中一般噪声问题

  • 如果需要设计带通、高通滤波器其步骤基本类似,只是滤波器的参数以及SOS块个数可能不一样而已

  • 需要提醒的时,IIR的相频响应不线性,如果系统对相频响应有严格要求,就需要采用其他的数字滤波器拓扑形式了

  • 实际应用中,如果阶数不高时,现在算力强劲的单片机或者DSP以及可以直接使用浮点处理。

  • 如果对处理速度有严格的实时要求,需要在极短时间进行滤波处理,可以考虑降低阶数,或采用定点IIR滤波算法实现。也或者将文中函数进行汇编级优化。

文章出自微信公众号:嵌入式客栈,关注公众号,获取更新更多内容

手把手教系列之IIR滤波器设计的更多相关文章

  1. IIR滤波器设计(调用MATLAB IIR函数来实现)

    转载请注明文章来源 – http://blog.csdn.net/v_hyx ,请勿用于任何商业用途         对于滤波器设计,以前虽然学过相关的理论(现代数字信号处理和DSP设计),但一直不求 ...

  2. 手把手教你学习DSP_硬件设计

    <<手把手教你学习DSP>>顾卫刚,感觉写的不错,将感兴趣的部分摘录下俩. 1.A/D保护及矫正电路 TMS320X2812 AD输入为0-3V,用上述电路可以起到钳位作用.当 ...

  3. 《手把手教你》系列基础篇(九十五)-java+ selenium自动化测试-框架之设计篇-java实现自定义日志输出(详解教程)

    1.简介 前面宏哥一连几篇介绍如何通过开源jar包Log4j.jar.log4j2.jar和logback实现日志文件输出,Log4j和logback确实很强大,能生成三种日志文件,一种是保存到磁盘的 ...

  4. 《手把手教你》系列基础篇(九十六)-java+ selenium自动化测试-框架之设计篇-跨浏览器(详解教程)

    1.简介 从这一篇开始介绍和分享Java+Selenium+POM的简单自动化测试框架设计.第一个设计点,就是支持跨浏览器测试. 宏哥自己认为的支持跨浏览器测试就是:同一个测试用例,支持用不同浏览器去 ...

  5. 《手把手教你》系列基础篇(九十七)-java+ selenium自动化测试-框架设计篇-Selenium方法的二次封装和页面基类(详解教程)

    1.简介 上一篇宏哥介绍了如何设计支持不同浏览器测试,宏哥的方法就是通过来切换配置文件设置的浏览器名称的值,来确定启动什么浏览器进行脚本测试.宏哥将这个叫做浏览器引擎类.这个类负责获取浏览器类型和启动 ...

  6. 《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上卷(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath.xpath 的定位方法, 非常强大.  使用这种方法几乎可以定位到页面上的任意元素. ...

  7. 《手把手教你》系列技巧篇(十五)-java+ selenium自动化测试-元素定位大法之By xpath中卷(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath.xpath 的定位方法, 非常强大.  使用这种方法几乎可以定位到页面上的任意元素. ...

  8. 《手把手教你》系列技巧篇(十六)-java+ selenium自动化测试-元素定位大法之By xpath下卷(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath.xpath 的定位方法, 非常强大.  使用这种方法几乎可以定位到页面上的任意元素. ...

  9. 《手把手教你》系列技巧篇(十七)-java+ selenium自动化测试-元素定位大法之By css上卷(详细教程)

    1.简介 CSS定位方式和xpath定位方式基本相同,只是CSS定位表达式有其自己的格式.CSS定位方式拥有比xpath定位速度快,且比CSS稳定的特性.下面详细介绍CSS定位方式的使用方法.xpat ...

随机推荐

  1. C# 基础知识系列- 13 常见类库介绍(一)

    0. 前言 每篇一个前言,介绍一下这一篇的内容.之前的内容都是针对某些知识点进行的介绍,这篇内容介绍一下实际开发中常用的一些类和命名空间.这一篇是个连续剧,大概有个三四集.嗯,就是这样. 1. Sys ...

  2. 解决报错:JPA-style positional param was not an integral ordinal;

    org.hibernate.QueryException: JPA-style positional param was not an integral ordinal; nested excepti ...

  3. ORM之单表、多表操作

    参考1 参考2 表与表之间的关系: 一对一(OneToOneField):一对一字段无论建在哪张关系表里面都可以,但是推荐建在查询频率比较高的那张表里面 一对多(ForeignKey):一对多字段建在 ...

  4. SSL/TLS 漏洞“受戒礼”,RC4算法关闭

    SSL/TLS 漏洞"受戒礼" 一.漏洞分析 事件起因 2015年3月26日,国外数据安全公司Imperva的研究员Itsik Mantin在BLACK HAT ASIA 2015 ...

  5. ICML2016 TUTORIAL参会分享

    本次ICML会议的tutorial安排在主会前一天.这次tutorial内容非常丰富,有微软亚研的hekaiming(已经跳去facebook)介绍深度残差网络,也有deepmind的david si ...

  6. 在sun jdk 8镜像基础上构建maven 3的docker镜像

    2019独角兽企业重金招聘Python工程师标准>>> 在https://my.oschina.net/ytqvip/blog/1595054文章的sun jdk 8镜像基础上构建m ...

  7. C++类学习(2)

    Ⅰ:类概念 一:类的构成 class 类名 { public: 公有数据成员和成员函数:类的接口 protected: 保护数据成员和成员函数: private: 私有数据成员和成员函数: }://注 ...

  8. 数学--数论--中国剩余定理 拓展 HDU 1788

    再次进行中国余数定理 问题描述 我知道部分同学最近在看中国剩余定理,就这个定理本身,还是比较简单的: 假设m1,m2,-,mk两两互素,则下面同余方程组: x≡a1(mod m1) x≡ a2(mod ...

  9. 无向图求割点(找桥)tarjan

    本博客参考了李煜东的<算法竞赛进阶指南>,大家要是觉得这篇文章写的不错请大家支持正版.豆瓣图书 我在之前的博客中讲解了搜索序时间戳,这次我们讲讲追溯值的概念. 追溯值: 设subtree( ...

  10. Codeforce 270D Greenhouse Effect

    Emuskald is an avid horticulturist and owns the world's longest greenhouse - it is effectively infin ...