一、前言
标准的直流伺服电机系统包括了一个直流电动机,一组变速齿轮组(齿轮箱),一个编码器(信号反馈发生源)和电子驱动器。
直流伺服电机中的增量式编码器是将角度位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示角位移的大小。编码器有通常为A相、B相、Z相输出,编码器轴每旋转一圈,A相和B相都发出相同的脉冲个数,但是A相和B相之间存在一个90°(电气角的一个周期为360°)的电气角相位差。可以根据这个相位差来判断编码器旋转的方向是正转还是反转。编码器轴每旋转一圈,Z相在一个固定的位置发出一个脉冲,所以可以把它作为复位相或零位相来使用。
本次项目通过电机驱动板控制一个直流伺服电机模块的运行,并针对增量式编码器设计一个基于LabVIEW与Arduino联机通信的转速虚拟仪表。该仪表能显示出电机输出轴的实时转速和转向。
二、转速表的硬件组成
转速虚拟仪表除了上位PC计算机和LabVIEW虚拟仪器面板,还有下位机的电控系统,它是由Arduino控制器、DFRobot Mini Encoder Kit编码器套件和900mA锂聚合物电池,这三部分组成。如图1所示。

图1转速虚拟仪下位机的电控系统
上图,Arduino控制器有三层电路板层叠而成,底下的板子是Arduino UNO,中间的板子是L298 Shield2A大电流双路直流电机驱动板,最上面的板子是Xbee V5传感器扩展板。这三个板子形成的电控套件,又称为智能车三件套。Mini Encoder Kit编码器套件有N20微型直流减速电机和正交编码板组成,图2所示。

图2 Mini Encoder Kit编码器套件
L298 Shield驱动板是兼容Arduino的一款大电流电机驱动,只需4根线就可以控制两台直流电机的转向和转速。这个驱动板有下图四个部分,需要学习掌握,如3所示。

图3 L298 Shield2A大电流双路直流电机驱动板
一是控制方式选择:分PWM模式和PLL模式。2种方式区别在于,PWM模式调速信号控制的是L298的E1和E2使能端,PLL模式调速信号控制的是L298的M1和M2转向端。如下图所示,PLL和PWM都标注出了对应的控制引脚。出厂默认设置为PWM模式,短路帽插在PWM端上。
二是电机端口:蓝色接线柱M1和M2分别可以接2个电机,旁边的插针1,2,3,4和蓝色接线柱功能一致。
三是外接电源端口:电机额定电压或电流大于Arduino自带稳压电路的最大输入时,可以使用外接电源单独给电机供电,VIN接电源正,GND接电源地。
四是外接电源与板内VIN电源选择端口:当2个短路帽插到右边时(VIN)时,电机电源就是使用Arduino板上VIN端输入的电源。当短路帽插到左边时(PWRVIN),则是外部电源供电。
我这次实验,采用的是外接电源,900mA锂聚合物电池给电机驱动供电,而Arduino UNO控制板的供电是利用USB线中的5V电压,两套电源分开供电,互不干扰。
图1所示,Mini Encoder Kit编码器套件与Arduino智能车三件套的连线只需6根线,编码器套件的N20微型电机的两根驱动线连在L298驱动板的电机端口中M1+和M1-蓝色接线柱上,它的正交编码板的VCC、GND连在XBee传感器扩展板的相应电源端子上,千万不要把正负极接反,否则板子烧掉。OUTA、OUTB脉冲信号线连在扩展板的数字信号端子2和3上。Arduino UNO具有中断机制的端子正好是数字信号端子2和3。
硬件安装和连线结束后,剩下的工作就是软件编制了。
四、转速表的下位机Arduino程序设计
Arduino下位计算机的程序任务:接受上位计算机的正转、反转、停止以及调速字节指令,并通过L298P直流电机驱动板,驱动伺服电机按照上位机指令运行。采集伺服电机中编码器的两路正交脉冲信号,并处理成电机转速和转向信息,然后把这些信息,再上传给上位机处理和显示。
Arduino如何处理编码器的脉冲信号,这就需要了解增量式编码器的工作原理了。这次实验的编码器与商用编码器的结构不同,但原理是一致的。它由车轮上的十二个齿以及正交编码板组合而成。
正交编码板是通过两个红外反射传感器测量直径为42mm轮子上的凸起小齿而得到A、B两相脉冲波。A相脉冲波的下跳沿触发中断时,会执行Arduino中断子程序,计数一个脉冲。如果计得1秒钟时间内的脉冲数,就可以换算出电机输出轴的转速了。
编码器A、B脉冲波形接近90度的相位差,可通过A、B两相波形的超前与滞后判断轮子是正转还是反转。图4所示,如果编码器轴正转,当A相脉冲输出下跳沿时,B相脉冲为低电平输出;如果编码器轴反转,当A相脉冲输出下跳沿时,B相脉冲则为高电平。由于编码器轴与电机输出轴是同步连接的,所以A相信号的下跳沿触发Arduino控制器中断时,在中断子程序中,判断出此时的B相信号是低电平还是高电平,即可确定当前电机输出轴是正转还是反转。下面的Arduino中断子程序中判断电机正反转的程序段,我用蓝色字体标识出来了。

图4电机正转或反转的编码器A、B相脉冲波形图
根据上述Arduino计算机程序任务和编码器信号处理方法,编写程序如下:
//把编码器正交编码板OUTA信号连接到Arduino控制器的数字端口2,
//数字端口2是Arduino的外部中断0的端口。
#define PinA 2 //外部中断0
//把编码器正交编码板OUTB信号连接到Arduino控制器的数字端口3,
//数字端口3是Arduino的外部中断1的端口。
#define PinB 3 //外部中断1
int E1 =5; //L298P直流电机驱动板的电机使能端口连接到数字接口5
int M1 =4; //L298P直流电机驱动板的电机转向端口连接到数字接口4
//定义暂存数组,用于存储上位机发出的3个电机控制字节
//第一个字节用于控制电机是否启动;
//第二个字节用于控制电机是正转还是反转;
//第三个字节用于提供给电机PWM调数值。
byte val[3];
int count = 0; //定义编码器码盘的计数值(此编码器转一圈发出12个脉冲)
int rpm = 0; //定义每分钟(min)转速(r/min)
int rpm_HIGH; //定义转速rpm分解的两个高低字节
int rpm_LOW;
byte flag; //定义上传给上位机的用于判断电机正转还是反转的标志字节
unsigned long time = 0, old_time = 0; //时间标记
unsigned long time_delay = 0; // 时间标记
//初始化
void setup()
{
Serial.begin(9600); //启动串口通信,波特率9600b/s
pinMode(M1, OUTPUT); //L298P直流电机驱动板的控制端口设置为输出模式
pinMode(E1, OUTPUT);
pinMode(PinA,INPUT); //伺服电机正交编码板的OUTA和OUTB信号端设置为输入模式
pinMode(PinB,INPUT);
//定义外部中断0的中断子程序Code( ),中断触发为下跳沿触发
//当编码器的正交编码板OUTA脉冲信号发生下跳沿中断时,
//将自动调用执行中断子程序Code( )。
attachInterrupt(0, Code, FALLING);
}
//主程序
void loop()
{
if (Serial.available()>2) //如果Arduino控制板的读缓冲区中存在上位机下达的字节
{
for(int i=0; i<3; i++)//for循环次数与上位机下达的字节数一致
{val[i] = Serial.read(); //读出Arduino控制板读缓冲区的字节
delay(5);
}
if(val[0]==0x11) //如果读出的第一个字节为启动标志字节0x11
{
if(val[1]==0xAA) //如果读出的第二个字节为正转标志字节0xAA
{
digitalWrite(M1,HIGH); //电机正转
analogWrite(E1,val[2]); //电机以第三个字节中PWM调速值的转速转动
count = 0;
old_time= millis();
}
else if(val[1]==0xBB) //如果读出的第二个字节为反转标志字节0xBB
{
digitalWrite(M1,LOW); //电机反转
analogWrite(E1,val[2]); //电机以第三个字节中PWM调速值的转速转动
count = 0;
old_time= millis();
}
}
else //如果读出的第一个字节不是启动标志字节0x11
{
digitalWrite(E1, LOW); //电机停止
count = 0;
old_time= millis();
}
}
time = millis();//以毫秒为单位,计算当前时间
//计算出每一秒钟编码器码盘计得的脉冲数,
if(abs(time - old_time) >= 1000) //如果计时时间已达1秒
{
detachInterrupt(0); // 关闭外部中断0
old_time= millis(); // 记录每次测速时的时间节点
//把每一秒钟编码器码盘计得的脉冲数,换算为当前转速值
//此编码器码盘为12个齿。
rpm =(float)count*60/12;
rpm_HIGH=rpm/256;//把转速值分解为高字节和低字节
rpm_LOW=rpm%256;
Serial.print(flag,BYTE); //向上位计算机上传电机转向标志字节
Serial.print(rpm_HIGH,BYTE);//向上位计算机上传电机当前转速的高字节
Serial.print(rpm_LOW,BYTE); //向上位计算机上传电机当前转速的低字节
count = 0; //把脉冲计数值清零,以便计算下一秒的脉冲计数
attachInterrupt(0, Code,FALLING); // 重新开放外部中断0
}
}
// 编码器码盘计数中断子程序
void Code()
{
//如果编码器的正交编码板OUTA脉冲信号下跳沿中断时,
//正交编码板OUTA为低电平,则当前电机转向为正转。
if(digitalRead(PinB)==LOW)
{
flag=0x7F;//电机当前转向为正转,则转向标志为0x7F
}
//如果编码器码盘的正交编码板OUTA脉冲信号下跳沿中断时,
//正交编码板OUTA为高电平,则当前电机转向为反转。
else
{
flag=0xFF; //电机当前转向为反转,则转向标志为0xFF
}
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于5ms时,计一次有效计数
if((millis()-time_delay) > 5)
//当编码器正交编码板OUTA脉冲信号下跳沿每中断一次,
count += 1; // 编码器码盘计数加一
time_delay=millis();
}
为了防止把噪音干扰脉冲误当做编码器脉冲,上面程序中引入了软件防干扰算法,由于干扰脉冲的周期一般极短,而我这套实验装置,电机转速最快时,编码器脉冲周期大约为15.6ms,所以我设计了编码器脉冲下跳沿中断的间隔时间,需大于5ms,才计一次有效计数,从而达到屏蔽干扰的目的。这个算法用粉红色字体进行了标注。
Arduino下位机程序循环接受上位机下达的三个字节命令,它们分别包含着电机启停、电机转向和电机转速信息。第一个字节是0x11,表示启动,如果是0x22,表示停止;第二个字节是0xAA,表示正转,如果是0xBB,表示反转;第三个字节是0到255的电机PWM调数值。这三个字节从上位机接受下来后,会存储在包含三个元素的数组里,后续程序段再一个个从数组里提取出它们,分析处理并控制着电机的运行。Arduino UNO接受上位机下达的字节命令的通信程序段,我用绿色字体标识出来了。
电机运行时,编码器实时将电机的转速和转向采样到Arduino UNO控制板,Arduino程序将运算获得电机每分钟转速(r/min),它的最高转速会超过数值255的,所以当Arduino下位机向上位PC机发送转速字节信息前,要把转速分解成高8位字节和低8位字节,然后上位机LabVIEW程序会再把上传的两个字节合并为电机转速并显示出来。Arduino程序在传送转速的两个字节前,还有一个字节需传送,这个字节就是电机的转向标志字节,LabVIEW会读懂它,并以指示灯的形式表示出当前电机的转向。
电机转速换算和发送字节命令的Arduino程序段,我在上面程序中用红色字体进行了标识。
四、转速表的LabVIEW虚拟仪表软件设计
转速表虚拟测量仪由上位机Labview仪表面板和下位机电机测控器组成,现在谈谈上位机仪表面板的人机交互程序设计,可以看看图5,我为这个作品设计的前面板,蛮酷的。
这个前面板外形控件看起来很棒,是Labview软件自带的吗?不是,您需要下载“VI封装管理器”软件VIPM,VIPM是管理Labview附加组件的Labview官方软件,Labview附加组件有些像Arduino第三方库文件。我用这个软件下载和安装了一个NI主题风格的控件库组件。VIPM下载地址:http://www.jki.net/vipm。
在Windows桌面上,点击VIPM图标,运行程序。在其菜单栏的“Window”菜单项里,选择Show LabVIEW Tools Network,见图5,如果这时您的电脑已经连上了互联网,就会紧跟着出现一个更直观的VI附加组件的介绍和安装窗口。(所有图片双击,均可放大看!)

图5 VI Package Manage管理器软件
选择Sort By为“Rating”,再一页页地翻看,找到下图我用矩形框标注的NI主题定制套件(NI Themed Custom Suite)的控件库组件,点击下载,并安装。见图6所示。

图6 NI Themed Custom Suite控件库组件安装
安装完后,Labview的控件工具库面板里就会多出这个NI主题风格的控件库组件,控件库包括:仪表盘、按钮、开关、图表等。有了这个控件库组件,您再用它设计的人机交互面板会比以前更酷,我喜欢通过某种“利器”放大自己能力的感觉。

图7 NI主题风格套件的控件提取
如果希望从NI主题风格的控件库中,提取一个带有NI公司LOGO的仪表盘,方法是:在LabVIEW前面板的空白处,点击鼠标右键,就会如图7所示,出现一个控件工具库,从库中选择“NI Themed Custom Suite”子库,再从子库中找到“NI Themed Gauge”,把它用鼠标左键拖到前面板,就可以看到如图8所示的转速表仪表盘了。

图8 转速表虚拟仪器前面板
前面板中,用于调节电机转速的旋钮、用来指示电机正反转的LED灯和用来控制电机启停的按钮也是从“NI Themed Custom Suite”子库中一个个找出来,分别布置到前面板合适位置的。
基于Windows的软件有一个操作技巧就是右键快捷菜单,当你要了解和设置软件的控件或者函数时,只要在它们上面点击鼠标右键,就会出现快捷菜单。如果现在我需要针对“NI Themed Gauge”控件,让它显示出一圈由绿变红的数值梯度条,同时显示实时数字,那就点击鼠标右键吧,看看随之出现的快捷菜单,能不能帮助到你。办法是:把快捷菜单的“显示项”中的“梯度”和“数字显示”打上√即可,见图9所示。
同理,如何设置“NI Themed Gauge”仪表盘的数据类型、标尺范围,只要点击相应的快捷菜单选项,在随之出现的子菜单或者对话窗口中,设置正确参数,就能完成任务。

图9 前面板控件参数设置
LabVIEW前面板的人机界面设计比较酷,再来看支撑它的后台框图程序如何设计。框图程序如图10所示。

图10 框图程序
要编写串口通信程序,不仅要具备Labview软件,还必须另外安装NI_VISA串口通讯协议驱动。安装完NI_VISA,在框图程序界面空白处右击鼠标,出现函数工具库面板,再点击“仪表I/O”→“串口”,就会出现串口函数VI子面板,如下图。

图11 VISA串口函数VI子面板
图10所示的图形化编程设计是采用NI_VISA串口Serial函数来完成PC机与Arduino之间的串口通信的。VISA中的Serial函数库里包含VISA串口配置函数VISA Configure Serial Port、VISA写入函数VISA Write、VISA读取函数VISA Read和VISA关闭函数VISA Close。
VISA串口配置函数VISA Configure Serial Port的作用是完成串口参数的初始化设置,
包括了串口资源名称,波特率,奇偶校验、数据比特、是否启用终止符等。
由于VISA写入函数VISA Write只接受字符串输入,所以Labview向Arduino下位机写入电机的启停字节、正反转字节和调速字节,要通过如图10所示的“创建数组”函数和“字节数组至字符串转换”函数,组成字符串,输入到VISA Write函数的写入缓冲区中。VISA Write函数会把写入缓冲区内容通过串口发送到Arduino控制板中。
VISA读取函数VISA Read从串口读取下位机发送的字符串,每次读取字节数我设置为3,它的输出通过“字符串至字节数组转换”函数和“索引数组”函数,把字符串转换为3个无符号字节数据,这3个字节数据分别包含了一个字节的电机正反转标识和两个字节的电机转速信息。
由“索引数组”函数的索引0生成的字节数据通过“条件结构”函数,把标识了电机正反转的字节转换“反转灯”布尔量真假值,从而使电机反转时,在前面板中对应的指示灯会亮,电机正转时,则指示灯熄灭。“索引数组”函数的索引1和2生成的两个字节就是Arduino上传的电机实时转速的高字节和低字节,它们经过算术运算,就可以得到电机每分钟转速值,并会立即显示在前面板的“NI Themed Gauge”仪表盘上。
VISA关闭函数VISA Close的作用是当程序停止之前,必须要把使用的串口设备关闭,若不关闭,其他程序就不能使用这个被占用的串口设备。
事件结构是一个功能非常强大的编程工具。这个LabVIEW程序中,前面板中的“正转”、“反转”、“停止”和“程序退出”这四个按钮就被设置成为事件源,当它们被按下时,会分别引发事件,并通过框图程序中相应的事件结构分支,执行相应命令。例如,如图10所示,按下前面板的“正转”按钮,在它的事件分支中,会把电机控制的三个字节写入到串口缓冲区中。
详细的事件结构的运行机制和编程技巧可以参见相关文章,网址:http://www.eefocus.com/zhang700309/blog/13-03/292196_ead67.html。
这个作品的LabVIEW程序请下载:http://yunpan.cn/Q5cv6qnkjprPr。
五、结束语
LabVIEW是美国国家仪器公司推出的高效图形化虚拟仪器开发平台。自1986年诞生以来,LabVIEW一直致力于简化编程的复杂性,为那些未接受过计算机程序设计培训的人提供一种测量控制系统的开发平台。LabVIEW可以与遵循GPIB、VXI、TCP/IP和RS-232协议的硬件设备实现上下位机的通信,并通过这种方式把PC计算机资源与仪器硬件的测量、控制和数据采集能力结合起来,组建出具有一定功能的虚拟仪器系统。
如果希望组建一种低成本的虚拟仪器系统,我认为通过VISA串口函数的通信,把LabVIEW软件与Arduino单片机进行结合是一种很好的方法。本文介绍的编码器转速表就是这种虚拟仪器组建方法的一个典型案例,按照这样的方法,还可以开发出更多更有意思的虚拟仪器。