1、问题

  控制电机转动需要用正弦波驱动,但是处理器无法直接产生正弦波。

2、解决思路

  将需要产生的正弦波分成若干等分,每一段有一个有效值,让每一段的有效值与一个方波的有效值相等,算出每段方波的占空比,这样就能用PWM波等效正弦波。

3、计算有效值的方法

  有效值的定义是:将要计算的电压施加在电阻两端,某段时间内电阻产生的热量如果与另一不变的直流电施加在该电阻产生的热量相等,那么要计算的电压的有效值就等于这个不变直流电的电压值。

  对于恒定电压来说,电阻产生的热量等于功率乘以时间,即W=P*t,单位是焦耳。P=U2/R,所以恒定电压在时间t内,电阻R产生的热量W=(U2t)/R,将这个式子推广到任意情况,则电阻R产生的热量是功率在时间t内的积分。也就是:  

  

   先来热个身,我们知道国内市电的电压是220V,频率是50Hz,即周期是0.02s,它的最大值是根号2倍的220V,约为310V,那么这个根号2倍的220V是怎么得出来的呢?

  首先交流电电电压的表达式:

  

   将这个电压施加到电阻R上,得到功率随时间的变化关系为:

  

   那么在时间0~t内,电阻发出的热量为:

  

   同样的,一个恒定电压U0在时间0~t内产生的热量也是功率从0~t的积分,但是因为这个电压是不变的,所以可以简单的写成

  

   因为二者产生的热量相等,所以:

  

   左边的式子是一个t的函数,它的原函数可以在下面这个网站中算出来:

  https://zh.numberempire.com/integralcalculator.php

  我计算的结果如下图:

  

   取最小正周期计算,可得以下等式:

  

   这就是为什么我们常说市电是220V,最大值是310V的原因。

4、实际应用计算

  后面的计算参考了这篇文章:https://zhuanlan.zhihu.com/p/546573703,但是这篇文章忽略了计算过程。

  实际上我们要产生的正弦波的赋值和周期都是任意的:

  

  其中V0是电压峰值,t是时间,T是周期。

   将正弦波的一个周期分成N等份,每一份所占的时间为T/N,那么第k份(k从0开始算)电压所产生的热量为:

  

   因为方波产生的电压要么就是最大值V0,要么就是最小值0,并且方波的有效值就等于平均值,假设方波的占空比为duty(k),那么方波在这段时间产生的热量为:

  

   让这两个式子相等:

  

   化简过程如下:

  

   最后得出结论:

  

   k∈[0,N-1],k∈Z。

  可见,某段占空比与周期是没有关系的,只与细分的点数N,以及当前所在的点数k有关,N取的越大,精度越高。

5、得到具体的占空比数值

  在实际应用中,因为占空比和周期没有关系,所以每个周期内的每个点的占空比都是固定的数,既然如此,就不需要在运行时去计算每个点的占空比,可以提前将这些占空比计算出来,存放到一个数组中,减少CPU的计算量。

  下面这段代码用于计算将周期等分成N个点时每段的占空比:

  

 1 #include <stdio.h>
 2 #include <math.h>
 3 #include <stdlib.h>
 4 
 5 #define pi 3.1415926
 6 
 7 double duty (int k, int N)
 8 {
 9     double dy = 0.5 - N / (4 * pi) * sin(2 * pi / N) * cos(2*pi*(2*k+1)/N);
10     
11     return dy;
12 }
13 
14 int main(int argc, int* argv[])
15 {
16     int i;
17     int N;
18     double* p;
19     
20     while (1) {
21         printf("请输入要等分的点数:\n");
22         scanf("%d", &N);
23         
24         p = malloc(N * sizeof(double));
25         if (p == NULL) {
26             return 0;
27         }
28         
29         for (i = 0; i < N; i++) {
30             p[i] = duty(i, N);
31             printf("duty[%d] = %f\t", i, p[i]);
32             if (i % 5 == 0) {
33                 printf("\n");
34             }
35         }
36         printf("\n");
37         
38         printf("double duty[%d] = {", N);
39         for (i = 0; i < N; i++) {
40             printf("%f", p[i]);
41             if (i < N-1) {
42                 printf(", ");
43             } else {
44             printf("};");
45             }
46         }
47         printf("\n");
48         
49         free(p);
50     }
51 }

  附,程序运行结果:一个周期等分成100个点(四分之一周期是25个点),和一个周期64个点(四分之一周期16个点)的每个点的占空比: