杨民峰 孙洪迪
(北京工业职业技术学院信息工程学院,北京 100042)
随着物联网技术的发展及日趋成熟,人们对车辆信息准确把握的需求已经不仅仅涉及自身车辆安全、管理的范畴,更与用户体验联系紧密[1]。目前车辆的定位监控系统一般采用C/S模式,用户需要下载客户端才能使用。这种传统的C/S系统通常通过TCP协议进行通信,利用Socket编程技术实现客户端与服务器端的双向通信,这会导致长期占有连接资源[2]。而基于Web的GPS监控系统,利用HTTP协议可以实现 C/S的双向通信[3],用户只需打开浏览器,结合在线地图就能实时查看车辆位置,使用起来更加方便、简洁。
笔者研究与实现的基于Web的GPS监控系统,通过在车辆上安装定制的车载终端,并把车载终端内置的唯一编号作为车辆标识,可以实时获取并在地图上标注车辆的地理位置,同时可以查询车辆的行驶速度、方向、里程、油耗等信息。这些功能的实现是在GPS监控设备完善的基础上,结合定位技术和移动通信技术,将车辆的运行状况等信息及时反馈给系统平台,系统平台将获取的信息进行处理,从而实现车辆的实时信息显示。该系统分为2个部分:(1)基于北斗/GPS定位的远程监控车载终端机;(2)车辆数据处理服务器系统。远程监控车载系统结构图如图1所示。
图1 远程监控车载系统结构图
该系统设计目的是基于WebSocket技术,设计实现一个B/S架构的、具有高实时性和稳定性的车联网监控系统,使用户能够通过随身携带的计算机或智能移动设备的Web浏览器,随时掌握车辆的行驶状况和位置信息。系统Web页面要实现对车辆的实时监控功能,浏览器客户端就需要及时地获取服务器端最新上传的车辆数据信息,保持客户端与服务器端的数据信息同步[4]。为了保持这种同步性,引入WebSocket服务器推送技术。
远程监控车载终端利用卫星定位实时采集车辆位置信息,然后通过4G网络,使用UDP通信协议将数据传输给服务器,服务器端对收到的车辆信息分析处理,得到车辆坐标、行驶速率、行驶状态等相关信息,然后利用WebSocket推送技术,在Web页面中的地图界面显示[5]。远程监控车载系统数据处理过程图如图2所示。
图2 远程监控车载系统数据处理过程图
远程监控车载终端机负责收集并发送采集的车辆信息,每个车载终端机内置1个唯一编号作为车辆的身份ID。该终端机系统主要负责对接收的数据进行响应、处理和控制,通过北斗/GPS模块,得到车辆的经纬度信息,并按通信协议,每5 ms向数据服务器发送1次数据。数据包括:车辆编号、车辆经度、车辆纬度、车辆速度等信息。远程监控车载终端机包括:电源处理及转换模块、卫星定位模块、通信与接口模块以及数据存储模块。嵌入式系统硬件功能的实现,除了需要基本形式的微控制器、存储器设备、输入输出设备外,还需要能实现不同通信、处理等功能的不同的丰富外设[6]。其中,硬件平台采用嵌入式系统结构,硬件电路分为北斗兼容型多频功能模块和功能底板电路系统(实时接收、分发、存储部分及控制2个部分)。硬件框架如图3所示。
图3 硬件系统框架图
2.2.1 UDP服务端
在数据处理服务器中,UDP服务端负责接收车辆终端传回的原始数据,并对数据进行解析处理,临时保存在 1 个 Map类型的对象中。Java用DatagramSocket 代 表 UDP 协 议 的 Socket,DatagramSocket本身只是码头,不维护状态,不能产生IO流,它唯一的作用是接收和发送数据报,Java用DatagramPacket代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的[7]。接收端步骤如下:
(1)创建接收端的 Socket对象(Datagram Socket),使用端口 12345:
socket=new DatagramSocket(null);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(12345));//端口号 12345
(2)创建1个数据包,用于接收数据:
byte[]data=new byte[1024];
packet=new DatagramPacket(data,data.length);
(3)调用DatagramSocket类中的receive方法接收数据:
socket.receive(packet);
(4)解析数据包,转换为字符串数据:
String info=new String(packet.getData(),0,packet.getLength());
(5)进一步解析包含小车信息的字符串,把数据存放在Map类型的对象clientMap中:
CarMsgPojo carMsgPojo=MsgUtil.msgDealWith(info);
Constant_Class.clientMap.put(String.valueOf(carMsgPojo.getCar()),carMsgPojo);
其中,MsgUtil.msgDealWith()方法对包含车辆信息的json字符串进行拆分,对原始车辆经纬度数据进行屏幕像素化处理,使经纬度转换为屏幕地图对应的像素位置,转换步骤如下:
(1)按显示屏幕分辨率像素比例截取地图上区域。
(2)记录地图上截图区域左上角经度值和纬度值,右下角经度值和纬度值。
(3)计算当前车辆经度值和左上角经度值的差,得出占多少个经度lon。
(4)计算当前车辆纬度值和左上角纬度值的差,得出占多少个纬度lat。
(5)计算地图在屏幕满屏状态下每个经度、纬度在当前分辨率下占多少像素,分别记为x,y。
x=屏幕横向x轴像素/(右上角经度-左上角经度)
y=屏幕纵向y轴像素/(左上角纬度-左下角纬度)
(6)计算车辆当前在屏幕上的像素位置:
(lon*x,lat*y)
可以加上纠偏参数,修正屏幕显示位置:
lon=lon*x+offset_x;
lat=lat*y+offset_y;
部分代码如下:
//计算GPS坐标到屏幕图像的像素位置-start
Properties properties=new Properties();
try{
//使用ClassLoader加载 properties配置文件生成对应的输入流
InputStream in=MsgUtil.class.getClass Loader () getResourceAsStream ("coordinate.properties");
properties.load(in);
//获取截取的地图边界经纬度
String longitude_left_top=properties.getProperty("longitude_left_top");
String latitude_left_top =properties.getProperty("latitude_left_top");
//当前经度和左上角经度差
lon=(lon-Double.parseDouble(longitude_left_top));//经度 左上角经度
//当前纬度和左上角纬度差
lat=(Double.parseDouble(latitude_left_top)-lat);//纬度 左上角纬度
//计算满屏下每个经度,纬度在当前分辨率下占多少像素
String longitude_right_top=properties.getProperty("longitude_right_top");
String latitude_left_down=properties.getProperty("latitude_left_down");
String screenresolution_x=properties.getProperty("screenresolution_x");
String screenresolution_y=properties.getProperty("screenresolution_y");
double
x=Double.parseDouble(screenresolution_x)/(Double.parseDouble(longitude_right_top)-Double.parseDouble(longitude_left_top));
double
y=Double.parseDouble(screenresolution_y)/(Double.parseDouble(latitude_left_top)-Double.parseDouble(latitude_left_down));
//加上纠偏,得出经纬度在屏幕地图上的像素位置
int offset_x=Integer.parseInt(properties.getProperty("offset_x"));
int offset_y=Integer.parseInt(properties.getProperty("offset_y"));
lon=lon*x+offset_x;
lat=lat*y+offset_y;
}catch(IOException e){
e.printStackTrace();
}
//----------计算GPS坐标到屏幕图像的像素位置--end------------
其中,配置文件coordinate.properties内容如下:
#截取的地图图片左上角GPS坐标
longitude_left_top=116.125763
latitude_left_top=39.967256
#截取的地图图片右上角GPS坐标
longitude_right_top=116.131148
latitude_right_top=39.967256
#截取的地图图片左下角GPS坐标
longitude_left_down=116.125763
latitude_left_down=39.964678
#当前屏幕分辨率
screenresolution_x=1680
screenresolution_y=1050
#纠偏,根据实际页面效果,调整在页面上对应的像素位置
offset_x=-3
offset_y=-5
2.2.2 W ebSocket服务端
UDP服务和WebSocket服务共享clientMap对象,UDP服务把车辆数据存入map对象中,一旦浏览器客户端和服务器建立WebSocket连接,服务端每隔500 ms把map对象中的数据推送到Web页面端。
服务端部分代码如下:
@ServerEndpoint(value="/clinetWebSocket")
@Component
public classWebSocketServer{
static{
new Thread(()->{
Map<String,CarMsgPojo>map=Constant_Class.clientMap;
while(true){
if(getOnlineCount()>0&&map.size()>0){
String jsonString =JSONObject.toJSONString(map);
try{
send InfoAll(jsonString);//500 ms推送1次
Thread.sleep(500);}catch(Exception ee){}
}
}
})start();
}
2.2.3 车辆数据W eb页面可视化显示
在该系统中设计index.jsp页面作为车辆位置显示页面,在页面中事先放入1张按显示器分辨率比例裁剪好的静态地图图片作为背景;在index.jsp页面中引入index.js脚本文件,index.js负责接收服务端推送的车辆信息,在index.js中创建map对象,连接WebSocket服务端,接收到服务端推送过来的json数据(json数据中包含了每个车辆的信息,其中,车辆的GPS数据是处理过后的屏幕像素位置)。
其中carGps()函数遍历map对象。若车辆信息不存在,则添加到map中,同时在页面中新增显示车辆标志的div层;若已经存在,则更新最新车辆数据;最后遍历map对象数据,通过设置每个车辆div样式的top和left属性,实现车辆在Web页面端的定位,如图4所示。代码如下:
图4 车辆实时定位显示效果
function carGps(msg){
var data=$.parseJSON(msg);
var carid=0;
var cardiv=null;
$.each(data,function(key,obj){
if(maps.get(key)==null){//如果 map里没有
maps.set(key+’’,obj);
addCar(maps.get(key));
}else{
maps.set(key+’’,obj);
}
});
maps.forEach(function(obj1){
var lat=obj1.lat;//经度
var lon=obj1.lon;//纬度
$("#"+obj1.car).css({top:parseInt(lat),left:parseInt(lon)})
});
}
其中,addCar()函数在页面中添加1个车辆图标,实现可视化效果,代码如下:
function addCar(data){
var cardiv=$('<div id="'+data.car+'"></div>');//创建 1个 cardiv
cardiv.addClass('carDiv'); //添加 css样式
cardiv.css({top:parseInt(data.lat),left:parseInt(data.lon)})
var car=$('<div class="car"></div>');//创建1个 car
car.appendTo(cardiv);
var carp=$('<p>'+data.car+'</p>');//创建1个显示车辆的cardiv
carp.appendTo(cardiv);
cardiv.appendTo('#index');
}
针对车辆的实时监控问题,笔者提出并设计实现了一个基于Web浏览器的车辆监控系统。搭建该系统基于Web的GPS监控系统环境,成本较低,通过在车辆上安装车载终端,可同时在多处Web浏览器上实时监控多个车辆,执行速度较快,安全性较高,且Web界面显示操作简单方便,易于被大众所接受。由于标识车辆需要通过车载终端的唯一编号确定,此系统更适用于固定场地的车辆监控,如景区、学校等场所的游览车、巡逻车的监控,具有很好的市场前景。