- 易迪拓培训,专注于微波、射频、天线设计工程师的培养
可视化WIFI遥控搬运机器人之服务器搭建
可视化WIFI遥控搬运机器人之硬件部分设计
http://www.cntronics.com/rf-art/80025732
本设计主要研究基于WIFI网络的可视化无线遥控搬运机器人,利用WIFI网络高速传输实时视频图像采集,通过机器人安装的传感器实现数据采集。采用WIFI网络通讯使得控制端多样化,可用手机,电脑等具备WIFI功能的设备进行控制。此外,还可将机器人接入Internet实现更远距离的控制。本设计在S3C6410平台上移植了Linux操作系统用于接收命令并对硬件设备进行控制,其中移植了MJPGstreamer作为视频服务器,移植了BOA服务器作为WEB服务器。本文将从硬件设计,驱动程序编写,服务器移植,服务程序编写,Android应用程序编写,Web应用程序编写等方面来讲述本设计的功能实现。
功能框图
总体设计及硬件选型和电路部分:可视化WIFI遥控搬运机器人(1):硬件部分
3 服务器搭建
3.1 服务器端功能框图
图3-1 服务器端功能框图
3.2 Linux系统移植
核心板采用友善之臂公司提供的TINY6410,此核心板已提供Bootloader,Linux系统,文件系统。使用时只需要根据实际的需要裁减Linux系统即可,本设计采用的Linux内核版本为Linux2.6.38,编译平台为Ubuntu12.04,交叉编译器为arm-linux-gcc-4.5.1。
3.3 驱动编写与移植
3.3.1 直流电机驱动
由于S3C6410只带有2路PWM输出,而夹持器部分需要两路PWM脉宽调制控制伺服舵机,因此直流电机部分采用定时器2来模拟PWM调制。设置定时器2每100ms进一次中断,在定时器中进行1~100计数,因此PWM周期为10S,并有100个脉宽比可调,满足直流电机调速控制。
[page]3.3.2 伺服电机驱动
伺服电机需要采用脉宽调制,通过调节20ms周期内的占空比可以指定伺服电机的旋转角度,其对应关系如下表:
表3-1 伺服电机占空比与旋转角度的对应关系
由于舵机的控制要求较高,本设计采用S3C6410自带的PWM进行控制.。设置PWM0和PWM1的周期为20ms,通过调节PWM0和PWM1的占空比来控制伺服电机进行工作。
3.3.3 摄像头驱动
ZC301为免驱的UVC视频设备,为了实现视频的采集需要在编译内核时选择上V4L2支持。
3.3.4 USB WIFI 驱动
本设计中采用的这款USB无线网卡采用RTL8188芯片,为使该设备能够正常工作需要进行驱动程序移植。
①从RTL官网获得RTL8188的最新驱动程序,本文采用的是RTL819xSU_usb_linux_v2.6.6.0.20120405.tar.gz。
②在Ubuntu中利用命令tar -zxvf 将驱动包解压。
③进入驱动目录并修改Makefile
④由于驱动默认移植平台是I386_PC,而我们需要将其移植到S3C平台上,故需要做如下修改:
说明目标平台:
将:CONFIG_PLATFORM_I386_PC = y
改为:CONFIG_PLATFORM_I386_PC = n
将:CONFIG_PLATFORM_ARM_S3C = n
改为:CONFIG_PLATFORM_ARM_S3C = y
指定交叉编译器以及内核路径信息:
ifeq ($(CONFIG_PLATFORM_ARM_S3C), y)
EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN
ARCH := arm
CROSS_COMPILE := /opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-
KVER := 2.6.48_$(ARCH)
KSRC := /root/tiny6410/linux-2.6.38
Endif
⑤执行Make命令得到8712u.ko即为所需驱动,将该文件移动到目标平台加载即完成了USB WIFI驱动程序的移植。
3.3.5 ADC驱动
电源电量以及FSR压力传感器数据测量需要ADC驱动的支持。由于系统中已含有该驱动,故只需要在编译内核时选择上即可。
3.4 AP热点搭建
3.4.1 AP热点简介
AP是(Wireless) Access Point的缩写,即(无线)访问接入点。无线设备可以通过它来接入到无线网络。RTL8188支持AP热点模式,采用这种方式可以让机器人成为AP热点,然后用带WIFI功能的设备来进行连接。
3.4.2 Hostapd简介
Hostapd即Host Access Point,其是Linux系统中无线访问接入点的守护进程。它可以将无线网卡设置为AP模式,并且支持多种加密方式,提供了设备接入的身份验证。在实际的使用期间,我们需要对其配置文件进行相应的修改。本设计中采用Hostapd来结合RTL8188网卡完成AP热点的搭建。
[page]3.4.3 DHCP简介
DHCP(Dynamic Host Configuration Protocol)是一种动态主机配置协议。它主要采用UDP协议来为接入到网络中的设备分配IP地址以及进行一些参数配置。本设计中通过配置其配置文件并启用该服务来为接入机器人的WIFI设备分配IP地址,有效的避免了多个设备接入时的地址冲突问题。
3.4.4 Hostapd移植
①下载最新版的Hostapd源码,本文使用的是Hostapd-2.0。
②将hostapd-2.0.tar.gz进行解压,并进入到hostapd目录
③对目录下的.config做如下修改:
注释掉:
CONFIG_DRIVER_HOSTAP=y
CONFIG_DRIVER_WIRED=y
CONFIG_DRIVER_MADWIFI=y
取消下面这项的注释:
CONFIG_DRIVER_NL80211=y
④修改Makefile 指定交叉编译器:
CC=arm-linux-gcc
⑤执行Make命令,编译得到hostapd及hostapd_cli
⑥将编译得到的可执行文件复制到目标平台,即完成了Hostapd的移植
⑦按需要更改hostapd.config文件,本设计关键部分配置如下:
interface=wlan0
ctrl_interface=/var/run/hostapd
ssid=CarControl
channel=6
wpa=2
wpa_passphrase=12345678
...
driver=rtl871xdrv
beacon_int=100
hw_mode=g
ieee80211n=1
wme_enabled=1
ht_capab=[SHORT-GI-20][SHORT-GI-40]
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
max_num_sta=8
wpa_group_rekey=86400
⑧hostapd -B hostapd.config 启动Hostapd服务
3.4.5 DHCP移植
Linux2.6.38内核中已含有DHCP支持,使用DHCP只需要修改DHCP配置文件udhcpd.conf,其中最关键部分如下:
# The start and end of the IP lease block
start 192.168.2.2
end 192.168.2.30
即修改了自动分配IP地址的范围,由于采用局域网,需要将IP地址设置为同一网段,机器人采用的IP地址为192.168.2.1,因此将IP地址分配范围作如上设置。启动hostapd后需要执行udhcpd命令启动DHCP服务,从而当WIFI设备接入机器人时能自动获取到IP地址。
3.5 视频服务器搭建
3.5.1 V4L2简介
V4L2(即Video for linux 2)是Linux 内核中针对UVC免驱视频设备的编程框架,它提供了一系列通用的接口来实现Linux中对视频设备的访问,其编程模式如下:
图 3-2 V4L2编程模式
Linux2.6.38内核中自带了该驱动,在使用时只需要在编译内核时将V4L2选项勾选上即可。
[page]3.5.2 LIBJPEG简介
Libjpeg是一个包含了JPEG图像的编码,解码等功能的开源库,其完全采用C语言来进行编写。
3.5.3 Mjpgstreamer简介
MJPGstreamer是主要运行在Linux系统上的一款运用多线程技术的轻量级视频服务器软件。它是一款采用C语言进行开发的开源软件,其代码简洁,注释清晰,组件功能明确,衔接清晰,可以移植到不同的计算机平台。整个程序主要以模块化的方法来进行构建,每个功能模块又被称为组件(plug-in),用户可以根据自己的需要来选择输入组件和输出组件。它可以实现从一个单一的输入组件获取到图像数据来通过多个输出组件将图像进行输出[ ]。下图为Mjpgstreamer的组件:
图 3-3 MJPG-streamer组件
本设计选用input_uvc作为输入组件来使用V4L2从摄像头获取图像数据,经JPEG库对数据进行编码之后,通过选用output_http作为输出组件来输出图像数据。output_http组件实现了一个符合HTTP1.0标准的web服务器,用户可以使用HTTP协议获取视频信息。
3.5.4 libjpeg移植
移植libjpeg库主要是用于Mjpgstreamer采集数据时压缩编码,移植步骤如下:
①下载libjpeg源码,本文采用jpeg-9a。
②将jpeg-9a.tar.gz解压,并进入源码根目录。
③执行如下命令配置编译,生成编译时所需要的Makefile文件。
./configure --prefix=/root/h264/app/jpeg --exec-prefix=/root/h264/app/jpeg --enable-shared --enable-static
命令中prefix是最后安装时库存放的目录,shared是编译成动态库,static是编译成静态库。
④修改Makefile文件,指定编译时所需要的交叉编译工具和环境:
CC = arm-linux-gcc -std=gnu99
AR = arm-linux-ar
CPP = arm-linux-gcc -std=gnu99 -E
⑤执行make命令编译代码
⑥执行make install命令产生libjpeg库,存放于/root/h264/app/jpeg目录下。
⑦将libjpeg库移动到目标平台,完成libjpeg移植。
3.5.5 Mjpgstreamer移植
MJPGstreamer作为本设计中的视频采集服务器,其移植过程如下:
①下载Mjpgstreamer源代码,本设计采用mjpg-streamer-r63.tar.gz。
②解压mjpg-streamer-r63.tar.gz,并进入代码根目录。
③修改plugins/input_uvc/Makefile:
指定交叉编译器:
CC = arm-linux-gcc
指定libjpeg库:
input_uvc.so: $(OTHER_HEADERS) input_uvc.c v4l2uvc.lo jpeg_utils.lo dynctrl.lo $(CC) $(CFLAGS) -ljpeg -L/root/h264/app/jpeglib/lib -o $@ input_uvc.c v4l2uvc.lo jpeg_utils.lo dynctrl.lo
④修改主目录以及plugins目录下所有子目录的Makefile,指定交叉编译器:
CC = arm-linux-gcc
⑤执行make命令,生成可执行文件mjpg_streamer。
⑥将源码目录中的start.sh和目录www拷贝到目标平台完成Mjpgsteramer移植。
⑦执行如下命令可启用Mjpgstreamer服务:
./mjpg_streamer -i "./input_uvc.so -r 320x240 -f 25 " -o "./output_http.so -w www"
命令中指定输入组件为input_uvc,并且配置采集分辨率为320*240,帧数为25fps。指定输入组件为output_http,并且http服务器目录为www。
[page]3.6 BOA服务器搭建
3.6.1 BOA服务器简介
由于Mjpgstreamer服务器只能传输视频信息,而本设计需要接收客户端的控制命令并且得返回机器人的传感器数据。因此得移植支持CGI应用脚本的服务器。BOA服务器是一个可运行在unix或linux下的非常小巧的单任务Web服务器,并且支持CGI脚本,广泛应用于嵌入式领域[ ]。本设计通过编写CGI脚本来完成服务器与客户端的数据交换。
3.6.2 BOA服务器移植
BOA服务器的移植需要以下步骤:
①下载BOA服务器源码,本文采用boa-0.94.13.tar.gz。
②解压boa-0.94.13.tar.gz,并进入到源码根目录。
③执行命令./configure生成Makefile文件。
④修改Makefile文件,指定编译时所需要的交叉编译器:
CC=gcc改成CC = arm-linux-gcc
CPP = gcc –E改成CPP = arm-linux-gcc –E
⑤执行make命令生成boa可自行文件
⑥将boa以及boa.conf移动到目标平台,即可完成BOA服务器移植。
⑦为了适应本设计的需求,得修改boa.conf文件来配置boa服务器。本设计作出如下修改:
Port 80
User root
Group root
ErrorLog /dev/console
AccessLog /dev/null
ServerName CarControl
DocumentRoot /www
DirectoryIndex index.html
KeepAliveMax 1000
KeepAliveTimeout 10
MimeTypes /etc/mime.types
DefaultType text/plain
CGIPath /bin
AddType application/x-httpd-cgi cgi
此配置设置了服务器的端口号,权限,服务器目录连接数等参数。
3.7 服务器端程序设计
3.7.1 CGI脚本简介
图 3-4 CGI程序应用原理
3.7.2 命名管道简介
命名管道是一种实现无关进程间通信的通信机制(IPC)。其本质上为一个文件,因此通讯更加稳定。命名管道遵循与先进先出原则,并且是半双工的,数据只能单向传输,若要实现双向传输就得使用两个管道。命名管道含有读端和写端,并且支持阻塞读。本设计中利用命名管道的特性,可以实现CGI程序与命令服务程序之间的数据交换。
[page]3.7.3 控制命令设计
本设计控制命令简单,因此客户端与服务器间数据通讯主要采用HTTP GET方法,服务器CGI应用程序可以在环境变量QUERY_STRING中获取字符串形式的控制命令。本设计中采用“标志+参数”的方式设置控制命令,单个命令字的总长度为5字节,具体如下:
直流电机控制指令:
L0000~0200
R0000~0200
其中L,R分别代表左和右,将此命令参数减去100,负数为后退,0为停止,正数为前进。其绝对值越大速度越快。
伺服电机控制命令:
C0250~1250
S0250~1250
其中C,S分别代表夹持和旋转命令,参数250~1250代表脉冲宽度用以调整伺服电机旋转位置(0°~180°)。
获取电量命令:
POWER
当CGI接收到此命令时将会把电源的电压值返回给客户端。
数据返回格式如下:
POW:0~1023
PRE:0~1023
其中POW,PRE分别代表电量,压力,参数0~1023为从ADC中采集到的数据。
3.7.4 CGI程序流程图
CGI程序主要负责从客户端获得命令字然后通过命名管道将控制命令发送给服务程序进行处理,并且调用驱动程序读取机器人传感器数据信息返回给客户端。CGI程序流程图如下:
图 3-5 CGI程序流程图
如图3-5所示,本设计的CGI脚步先判断客户端的命令是否为索取电压值得命令,如果是的话就读取电源电压数据并将数据返回,否则就将命令字写入到命名管道供服务程序来进行读取,并且读取压力传感器的数据将其返回给客户端。
3.7.5 CGI程序编写
CGI脚本的部分代码如下:
int main()
{
...
buff = getenv("QUERY_STRING");//获得指令
sscanf(buff, "%s", cmd);
...
/*读取电压值,并返回给客户端*/
if(strcmp(cmd,"POWER")==0)
{adc_fd=open("/dev/adc",0);//打开ADC驱动
if(adc_fd<0)
fprintf(stdout, "open adc_device failen");
else
{if(ioctl(adc_fd,ADC_SET_CHANNEL,0) < 0)
fprintf(stdout, "ioctl adc_device failen");
else{len = read(adc_fd, adc, 4);//读取ADC
if (len > 0)
{adc[len] = ''''''''?'''''''';
fprintf(stdout, "POW:%s",adc);//将电压值返回
}
}
close(adc_fd);
}
}
else
{
cmd_fd=open(FIFO_CMD,O_WRONLY|O_NONBLOCK,0);//与服务程序通过命名管道通讯
/* 向管道写入数据 */
if((nwrite=write(cmd_fd,cmd,11))==-1)
fprintf(stdout, "write cmd failen");
close(cmd_fd);
/*读取压力值,并返回给客户端*/
adc_fd=open("/dev/adc",0);//打开ADC驱动
if(adc_fd<0)
fprintf(stdout, "open adc_device failen");
else
{
if(ioctl(adc_fd,ADC_SET_CHANNEL,1) < 0)
fprintf(stdout, "ioctl adc_device failen");
else
{
len = read(adc_fd, adc, 4);//读取ADC
if (len > 0)
{
adc[len] = ''''''''?'''''''';
fprintf(stdout, "PRE:%s",adc);//返回压力值
}
}
close(adc_fd);
}
}
return 0;
}
[page]3.7.6 服务程序流程图
服务程序主要完成机器人初始化,读取电量值并将电量值通过LED来进行提示,读取命名管道获得命令字并将其解析执行。主要的流程图如下:
图 3-6 服务程序流程图
如图3-6所示,本设计中的服务程序采用多进程程序设计方式,其子进程每60S采集一次电源电量信息并更新电量指示灯显示,主进程采用阻塞读的方式读取命名管道来等待客户端发送命令,获得命令之后对命令进行解析并调用驱动程序来执行相应的命令,从而实现对机器人的控制。
3.7.7 服务程序编写
服务程序部分代码如下:
/* 创建子进程,用于每60S获取电源电压值 */
if(fork()==0)
{
while(1)
{
if(ioctl(adc_fd,ADC_SET_CHANNEL,0) < 0)
{perror("ioctl ADC device:");
exit(1);
}
char buffer[5];
int len = read(adc_fd, buffer, sizeof buffer -1);//读取ADC
if (len > 0)
{
buffer[len-1] = ''''''''?'''''''';
printf("POW VALUE:%sn",buffer);
getpower=(float)StrToInt(buffer);
getpower=9.9*getpower/1024.0;
if(power==0)
power=getpower;
if(getpower<=power && power-getpower<0.2)
{
power=getpower;
if(power>8.1)//根据电量来用LED显示
Show(5);
...
}
}
sleep(60);//1min
}
}
/* 主进程,用于获取命令并处理 */
else{
while(1)
{
memset(buff,0,sizeof(buff));
cmd_fd=open(FIFO_CMD,O_RDONLY);//readonly 阻塞
if(cmd_fd==-1)
{
perror("open");
exit(1);
}
if((nbytes=read(cmd_fd,buff,sizeof(buff)))>4)//读取FIFO_CMD管道
{
buff[nbytes]=''''''''?'''''''';
//指令处理
receive_do(buff);
}
close(cmd_fd);
}
}
上述代码中receive_do函数主要负责解析命令,并进行处理。其代码如下:
void receive_do(char buffer[])
{
int c,i;
int tmp=0;
c = (int)(nbytes+1)/5;
for(i=0;i {
tmp=(buffer[1+i*5]-''''''''0'''''''')*1000+(buffer[2+i*5]-''''''''0'''''''')*100+(buffer[3+i*5]-''''''''0'''''''')*10+(buffer[4+i*5]-''''''''0'''''''');//解析接收到的指令数据
switch(buffer[i*5])//处理指令
{
case ''''''''L'''''''':ioctl(dc_fd,LEFT_SET,tmp);
break;
case ''''''''R'''''''':ioctl(dc_fd,RIGHT_SET,tmp);
break;
case ''''''''C'''''''':ioctl(servo_fd,CLAMP_SET,tmp);
break;
case ''''''''S'''''''':ioctl(servo_fd,SPIN_SET,tmp);
break;
}
}
}
CGI即公共网关接口(Common GatewayInterface)它是一种WWW技术。CGI实质是运行在WEB服务器上面为客户端HTML页面提供接口的一个脚本程序。它可以通过标准输入(STDIN)来从WEB服务器获得数据,经处理之后可以通过标准输出(STDOUT)来将数据返回给WEB服务器,从而实现对客户端数据的接收处理。本设计采用这种方式实现机器人控制命令的接收,以及返回机器人传感器数据信息。CGI程序应用原理如下图所示: