嵌入式智能小车自动循迹初探
2018-11-16李英杰
李英杰
摘 要:此嵌入式智能小车的组成主要包括ARM硬件体系下的控制系统、电机驱动电路、红外探测电路、超声波避障电路等。系统以嵌入式Linux微控制器为核心,通过红外探测电路传感器采集不同的信号做出判断,继而改变电机的运动方向和运动速度。实现小车的前进、后退、左转、右转、自动循迹和避障功能,摄像头舵机的水平和垂直方向上的转动。利用Qt Creator生成客户端工程,通过PC端的WiFi连接,控制智能小车的运动,同时小车上的摄像头可以将实时的画面传回来。
关键词:嵌入式Linux微控制器;自动循迹;避障;无线网络连接控制;Qt Creator
在当前的环境中,随着科技的进步,智能化车辆或者与智能化车辆相关的产品已经开始作为各式各样自动控制系统中的重要设备之一,这其中主要包括了物流配送或者交通运输等系统。而机器人在复杂地形中行进时自动循迹和避障是一项必不可少也是最基本的功能。因此,自动避障系统和自动循迹的研发就应运而生。我们的自动避障小车就是基于这一系统开发而成的。 意义随着科技的发展,对于未知空间和人类所不能直接到达的地域的探索逐步成为热门,这就使机器人的自动避障有了重大的意义。小车可以通过传感器来获取当前道路状况,然后将传感器获取到的数据传输到处理器,处理器再结合小车当前的行驶状态,迅速地进行计算,对小车的行驶的方向和行车的速度进行快速的调整改变,进而对目标道路进行迅速准确的跟踪。
1 术语定义
server_video:C视频服务器(mjpeg图片)
client_video:QT视频客户端
通信协议:
client:发送“pic”
server:回复“XXXXlen”(xxxx代表图片的数据量,单位为字节)
client:根据接收到的数据量進行图片的循环接收,接收完毕,显示在客户端
小车IP:192.168.1.1
小车控制端端口:2001
小车视频端端口:8888
小车控制命令:
停止 FF 00 00 00 FF
前进 FF 00 01 00 FF
后退 FF 00 02 00 FF
左转 FF 00 03 00 FF
右转 FF 00 04 00 FF
可以参考如下格式保存命令
static unsigned char cmd[5] = {0xff,0x00,0x01,0x00,0xff}; //up
static unsigned char cmd[5] = {0xff,0x00,0x02,0x00,0xff}; //down
static unsigned char cmd[5] = {0xff,0x00,0x03,0x00,0xff}; //left
static unsigned char cmd[5] = {0xff,0x00,0x04,0x00,0xff}; //right
static unsigned char cmd[5] = {0xff,0x00,0x00,0x00,0xff}; //stop
波特率:9600 无校验 8位数据位 1位停止位
2 系统概述
编写能使小车运动和摄像等程序。用已有的小车模型,调试好WiFi模块和其硬件和软件,利用这样的方法来实现电脑端来通过路由器在无线传输的方式对小车进行控制,从而驱动WiFi小车的运动和摄影等一系列指令。
具体要求如下:
a)控制客户端数据发送给小车。
b)前进:四个车轮能够同时朝着前进的方向进行运动,保证速度一致;
c)后退:四个车轮能够同时朝着后退的方向进行运动,保证速度一致;
d)左转:左边的两个轮子静止,而右边的两个轮子前进
e)右转:右边的两个轮子静止,而左边的两个轮子前进
f)通过无线网对小车摄像头采集的信息来传送。
g)服务器图片的存储和链接客户端并将图片发送给客户端。
h)连续接受图片并且打印成连续照片显示。
3 总体设计
3.1 系统框架
3.2 结构体描述
部分一:
参数说明:参数类型为V4L2的能力描述类型struct v4l2_capability;
返回值说明:执行成功时,函数返回值为0;函数执行成功后,struct v4l2_capability结构体变量中的返回当前视频设备所支持的功能:例如支持视频捕获功能V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING等
Struct v4l2_capability
{
_u8 driver[16];
_u8 card[32];
_u8 bus_info[32];
_u32 version;
_u32 capabilities;
_u32 reserved[4];
};
Capabilities 常用值;
V4L2_CAP_VIDEO_CAPTURE
部分二:
设置视频设备的视频数据格式,例如设置视频图像数据的长、宽,图像格式(JPEG、YUYV格式);
参数说明;参数类型为V4L2的视频数据格式类型struct v4l2_format;
返回值说明:执行成功是,函数返回值为0;
struct v4l2_format
{
enum v4l2_buf_type type;
union
{
struct v4l2_pix_format pix;
struct v4l2_window win;
struct v4l2_vbi_format vbi;
__u8 raw_data[200];
} fmt;
};
struct v4l2_pix_format{
_u32 width;
_u32 height;
_u32 pixelformat;
Enum v4l2 field field;
_u32 bytesperline;
_u32 sizeimage;
Enum v4l2 _colorspace colorspace;
_u32 priv;
};
部分三:
功能:请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存),V4L2是频频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBBUFS控制命令字申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。
参数说明:参数类型为V4L2的申请缓冲区数据结构体类型struct v4l2_requestbuffers;
返回值说明:执行成功时,函数返回值为0;V4L2驱动层分配好了视频缓冲区。接下来可以为视频捕获分配内存:
struct v4l2_requestbuffers req;
if (ioctl(fd, VIDIOC_REQBUFS, &req;) == -1) {
return -1;
}
v4l2_requestbuffers 结构如下:
struct v4l2_requestbuffers
{
__u32 count;
enum v4l2_buf_type type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
enum v4l2_memory memory;//V4L2_MEMORY_MMAP 或V4L2_MEMORY_USERPTR
__u32 reserved[2];
};
部分四:
使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:
typedef struct VideoBuffer {
void *start;
size_t length;
} VideoBuffer;
VideoBuffer* buffers = calloc( req.count, sizeof(*buffers) );
struct v4l2_buffer buf;
for (numBufs = 0; numBufs < req.count; numBufs++) {
memset( &buf;, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf;) == -1)
{
return -1;
}
buffers[numBufs].length = buf.length;
buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
MAP_SHARED,fd, buf.m.offset);
if (buffers[numBufs].start == MAP_FAILED) {
return -1;
}
if (ioctl(fd, VIDIOC_QBUF, &buf;) == -1) {
return -1;
}
}
部分五:
V4L2有一個数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:
struct v4l2_buffer buf;
memset(&buf;,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0;
if (ioctl(cameraFd, VIDIOC_DQBUF, &buf;) == -1)
{
return -1;
}
if (ioctl(cameraFd, VIDIOC_QBUF, &buf;) == -1) {
return -1;
}
4 视频采集模块
4.1 视频采集介绍
使用摄像头采集视频数据,将采集到的数据保存到相应的数据结构之中。视频采集模块通过建立视频驱动模块,处理应用程序和设备驱动之间的帧缓冲区的同步。例如,一个视频显示驱动必须始终显示视频数据,直到应用程序给它另一帧视频数据去显示,它才会将上一帧缓冲区返回给应用程序。执行这种同步最有效的方式就是定义一个单独的API调用来负责缓冲区交换。同样一个视频捕获驱动必须始终返回最新捕获的视频数据,这要求驱动在下一次缓冲区交换发生之前至少保留一个帧缓冲区。该模块具有采集压缩速度快、图象质量好、产生的JPEG文件小、接口通信速率高等特点,适合需要嵌入式图象采集的各种应用,特别是无线或低速率网络通信下的图象应用。
4.2视频采集相关接口
//初始化视频采集设备
int camera_init(char *devpath, unsigned int *width, unsigned int *height, unsigned int *size)
//开启视频监控
int camera_start(int fd);
//采集视频信息
int camera_dqbuf(int fd, void *buf, unsigned int *size, unsigned int *index);
//停止视频采集
int camera_stop(int fd);
//关闭视频监控
int camera_exit(int fd);
//控制数据转发
QString str=this->ui->lineEdit->text();
QByteArray arr; QDataStream dst(&arr;,QIODevice::ReadWrite
dst< this->socket->write(arr); 5 客戶端显示模块 5.1 功能描述 客户端显示模块主要是将服务器中的数据信号经过处理传送到linux显示设备上来,实现视频流的显示,来使客户及使用者实现数据的采集功能和以此进行数据分析。 5.2 流程图 5.3 相关代码 int init_lcd(char *pathname); int tcp_client_init(); int tcp_client_connect(int sockfd,char *ip,const int port); int tcp_client_recv(int sockfd,void *buf,int count); int showrgb(int fd,unsigned,int width,int height); int close_lcd(int fd); 5.4 主要思想 每次获取图片之前,先进性图片数据的解析,存放到一个定义的字符串数组中,之后进行读操作。在进行都操作时,先读取前24个字节利用strstr函数得到第一次出现len的地址将其值赋值为/0,就可以取出/0之前的数据得到此次接收图片的大小,然后根据大小读取数据,最终当收到一副图片的数据时完成显示,因为视频相当于多幅图片的连续播放,故定义一个定时器使图片循环显示。 5.5 运行 使用Qt运行客户端程序,并将IP设置为:192.168.1.1(路由板IP),端口号设置为:8888 点击START按钮,我们就可以看到摄像头采集到的视频信息。 参考文献: [1] 梁明亮,孙逸洁.嵌入式智能小车的设计与实现[J].制造业自动化,第34卷11期 [2] 董宗祥,石红瑞,杨杰.嵌入式测控智能小车的设计与实现 [J].计算机计量与控制,2010.18(2) [3] 邢晓敏,杨正祥.嵌入式智能小车设计[J] .自动化应用.2017(05) [4] 陈俊如.基于Android控制台的智能小车系统设计[J] . 科技创新与应用.2018(08)