楊民峰 孫洪迪
(北京工業(yè)職業(yè)技術(shù)學(xué)院信息工程學(xué)院,北京 100042)
隨著物聯(lián)網(wǎng)技術(shù)的發(fā)展及日趨成熟,人們對車輛信息準(zhǔn)確把握的需求已經(jīng)不僅僅涉及自身車輛安全、管理的范疇,更與用戶體驗(yàn)聯(lián)系緊密[1]。目前車輛的定位監(jiān)控系統(tǒng)一般采用C/S模式,用戶需要下載客戶端才能使用。這種傳統(tǒng)的C/S系統(tǒng)通常通過TCP協(xié)議進(jìn)行通信,利用Socket編程技術(shù)實(shí)現(xiàn)客戶端與服務(wù)器端的雙向通信,這會(huì)導(dǎo)致長期占有連接資源[2]。而基于Web的GPS監(jiān)控系統(tǒng),利用HTTP協(xié)議可以實(shí)現(xiàn) C/S的雙向通信[3],用戶只需打開瀏覽器,結(jié)合在線地圖就能實(shí)時(shí)查看車輛位置,使用起來更加方便、簡潔。
筆者研究與實(shí)現(xiàn)的基于Web的GPS監(jiān)控系統(tǒng),通過在車輛上安裝定制的車載終端,并把車載終端內(nèi)置的唯一編號作為車輛標(biāo)識,可以實(shí)時(shí)獲取并在地圖上標(biāo)注車輛的地理位置,同時(shí)可以查詢車輛的行駛速度、方向、里程、油耗等信息。這些功能的實(shí)現(xiàn)是在GPS監(jiān)控設(shè)備完善的基礎(chǔ)上,結(jié)合定位技術(shù)和移動(dòng)通信技術(shù),將車輛的運(yùn)行狀況等信息及時(shí)反饋給系統(tǒng)平臺,系統(tǒng)平臺將獲取的信息進(jìn)行處理,從而實(shí)現(xiàn)車輛的實(shí)時(shí)信息顯示。該系統(tǒng)分為2個(gè)部分:(1)基于北斗/GPS定位的遠(yuǎn)程監(jiān)控車載終端機(jī);(2)車輛數(shù)據(jù)處理服務(wù)器系統(tǒng)。遠(yuǎn)程監(jiān)控車載系統(tǒng)結(jié)構(gòu)圖如圖1所示。
圖1 遠(yuǎn)程監(jiān)控車載系統(tǒng)結(jié)構(gòu)圖
該系統(tǒng)設(shè)計(jì)目的是基于WebSocket技術(shù),設(shè)計(jì)實(shí)現(xiàn)一個(gè)B/S架構(gòu)的、具有高實(shí)時(shí)性和穩(wěn)定性的車聯(lián)網(wǎng)監(jiān)控系統(tǒng),使用戶能夠通過隨身攜帶的計(jì)算機(jī)或智能移動(dòng)設(shè)備的Web瀏覽器,隨時(shí)掌握車輛的行駛狀況和位置信息。系統(tǒng)Web頁面要實(shí)現(xiàn)對車輛的實(shí)時(shí)監(jiān)控功能,瀏覽器客戶端就需要及時(shí)地獲取服務(wù)器端最新上傳的車輛數(shù)據(jù)信息,保持客戶端與服務(wù)器端的數(shù)據(jù)信息同步[4]。為了保持這種同步性,引入WebSocket服務(wù)器推送技術(shù)。
遠(yuǎn)程監(jiān)控車載終端利用衛(wèi)星定位實(shí)時(shí)采集車輛位置信息,然后通過4G網(wǎng)絡(luò),使用UDP通信協(xié)議將數(shù)據(jù)傳輸給服務(wù)器,服務(wù)器端對收到的車輛信息分析處理,得到車輛坐標(biāo)、行駛速率、行駛狀態(tài)等相關(guān)信息,然后利用WebSocket推送技術(shù),在Web頁面中的地圖界面顯示[5]。遠(yuǎn)程監(jiān)控車載系統(tǒng)數(shù)據(jù)處理過程圖如圖2所示。
圖2 遠(yuǎn)程監(jiān)控車載系統(tǒng)數(shù)據(jù)處理過程圖
遠(yuǎn)程監(jiān)控車載終端機(jī)負(fù)責(zé)收集并發(fā)送采集的車輛信息,每個(gè)車載終端機(jī)內(nèi)置1個(gè)唯一編號作為車輛的身份ID。該終端機(jī)系統(tǒng)主要負(fù)責(zé)對接收的數(shù)據(jù)進(jìn)行響應(yīng)、處理和控制,通過北斗/GPS模塊,得到車輛的經(jīng)緯度信息,并按通信協(xié)議,每5 ms向數(shù)據(jù)服務(wù)器發(fā)送1次數(shù)據(jù)。數(shù)據(jù)包括:車輛編號、車輛經(jīng)度、車輛緯度、車輛速度等信息。遠(yuǎn)程監(jiān)控車載終端機(jī)包括:電源處理及轉(zhuǎn)換模塊、衛(wèi)星定位模塊、通信與接口模塊以及數(shù)據(jù)存儲模塊。嵌入式系統(tǒng)硬件功能的實(shí)現(xiàn),除了需要基本形式的微控制器、存儲器設(shè)備、輸入輸出設(shè)備外,還需要能實(shí)現(xiàn)不同通信、處理等功能的不同的豐富外設(shè)[6]。其中,硬件平臺采用嵌入式系統(tǒng)結(jié)構(gòu),硬件電路分為北斗兼容型多頻功能模塊和功能底板電路系統(tǒng)(實(shí)時(shí)接收、分發(fā)、存儲部分及控制2個(gè)部分)。硬件框架如圖3所示。
圖3 硬件系統(tǒng)框架圖
2.2.1 UDP服務(wù)端
在數(shù)據(jù)處理服務(wù)器中,UDP服務(wù)端負(fù)責(zé)接收車輛終端傳回的原始數(shù)據(jù),并對數(shù)據(jù)進(jìn)行解析處理,臨時(shí)保存在 1 個(gè) Map類型的對象中。Java用DatagramSocket 代 表 UDP 協(xié) 議 的 Socket,DatagramSocket本身只是碼頭,不維護(hù)狀態(tài),不能產(chǎn)生IO流,它唯一的作用是接收和發(fā)送數(shù)據(jù)報(bào),Java用DatagramPacket代表數(shù)據(jù)報(bào),DatagramSocket接收和發(fā)送的數(shù)據(jù)都是通過DatagramPacket對象完成的[7]。接收端步驟如下:
(1)創(chuàng)建接收端的 Socket對象(Datagram Socket),使用端口 12345:
socket=new DatagramSocket(null);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(12345));//端口號 12345
(2)創(chuàng)建1個(gè)數(shù)據(jù)包,用于接收數(shù)據(jù):
byte[]data=new byte[1024];
packet=new DatagramPacket(data,data.length);
(3)調(diào)用DatagramSocket類中的receive方法接收數(shù)據(jù):
socket.receive(packet);
(4)解析數(shù)據(jù)包,轉(zhuǎn)換為字符串?dāng)?shù)據(jù):
String info=new String(packet.getData(),0,packet.getLength());
(5)進(jìn)一步解析包含小車信息的字符串,把數(shù)據(jù)存放在Map類型的對象clientMap中:
CarMsgPojo carMsgPojo=MsgUtil.msgDealWith(info);
Constant_Class.clientMap.put(String.valueOf(carMsgPojo.getCar()),carMsgPojo);
其中,MsgUtil.msgDealWith()方法對包含車輛信息的json字符串進(jìn)行拆分,對原始車輛經(jīng)緯度數(shù)據(jù)進(jìn)行屏幕像素化處理,使經(jīng)緯度轉(zhuǎn)換為屏幕地圖對應(yīng)的像素位置,轉(zhuǎn)換步驟如下:
(1)按顯示屏幕分辨率像素比例截取地圖上區(qū)域。
(2)記錄地圖上截圖區(qū)域左上角經(jīng)度值和緯度值,右下角經(jīng)度值和緯度值。
(3)計(jì)算當(dāng)前車輛經(jīng)度值和左上角經(jīng)度值的差,得出占多少個(gè)經(jīng)度lon。
(4)計(jì)算當(dāng)前車輛緯度值和左上角緯度值的差,得出占多少個(gè)緯度lat。
(5)計(jì)算地圖在屏幕滿屏狀態(tài)下每個(gè)經(jīng)度、緯度在當(dāng)前分辨率下占多少像素,分別記為x,y。
x=屏幕橫向x軸像素/(右上角經(jīng)度-左上角經(jīng)度)
y=屏幕縱向y軸像素/(左上角緯度-左下角緯度)
(6)計(jì)算車輛當(dāng)前在屏幕上的像素位置:
(lon*x,lat*y)
可以加上糾偏參數(shù),修正屏幕顯示位置:
lon=lon*x+offset_x;
lat=lat*y+offset_y;
部分代碼如下:
//計(jì)算GPS坐標(biāo)到屏幕圖像的像素位置-start
Properties properties=new Properties();
try{
//使用ClassLoader加載 properties配置文件生成對應(yīng)的輸入流
InputStream in=MsgUtil.class.getClass Loader () getResourceAsStream ("coordinate.properties");
properties.load(in);
//獲取截取的地圖邊界經(jīng)緯度
String longitude_left_top=properties.getProperty("longitude_left_top");
String latitude_left_top =properties.getProperty("latitude_left_top");
//當(dāng)前經(jīng)度和左上角經(jīng)度差
lon=(lon-Double.parseDouble(longitude_left_top));//經(jīng)度 左上角經(jīng)度
//當(dāng)前緯度和左上角緯度差
lat=(Double.parseDouble(latitude_left_top)-lat);//緯度 左上角緯度
//計(jì)算滿屏下每個(gè)經(jīng)度,緯度在當(dāng)前分辨率下占多少像素
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));
//加上糾偏,得出經(jīng)緯度在屏幕地圖上的像素位置
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();
}
//----------計(jì)算GPS坐標(biāo)到屏幕圖像的像素位置--end------------
其中,配置文件coordinate.properties內(nèi)容如下:
#截取的地圖圖片左上角GPS坐標(biāo)
longitude_left_top=116.125763
latitude_left_top=39.967256
#截取的地圖圖片右上角GPS坐標(biāo)
longitude_right_top=116.131148
latitude_right_top=39.967256
#截取的地圖圖片左下角GPS坐標(biāo)
longitude_left_down=116.125763
latitude_left_down=39.964678
#當(dāng)前屏幕分辨率
screenresolution_x=1680
screenresolution_y=1050
#糾偏,根據(jù)實(shí)際頁面效果,調(diào)整在頁面上對應(yīng)的像素位置
offset_x=-3
offset_y=-5
2.2.2 W ebSocket服務(wù)端
UDP服務(wù)和WebSocket服務(wù)共享clientMap對象,UDP服務(wù)把車輛數(shù)據(jù)存入map對象中,一旦瀏覽器客戶端和服務(wù)器建立WebSocket連接,服務(wù)端每隔500 ms把map對象中的數(shù)據(jù)推送到Web頁面端。
服務(wù)端部分代碼如下:
@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 車輛數(shù)據(jù)W eb頁面可視化顯示
在該系統(tǒng)中設(shè)計(jì)index.jsp頁面作為車輛位置顯示頁面,在頁面中事先放入1張按顯示器分辨率比例裁剪好的靜態(tài)地圖圖片作為背景;在index.jsp頁面中引入index.js腳本文件,index.js負(fù)責(zé)接收服務(wù)端推送的車輛信息,在index.js中創(chuàng)建map對象,連接WebSocket服務(wù)端,接收到服務(wù)端推送過來的json數(shù)據(jù)(json數(shù)據(jù)中包含了每個(gè)車輛的信息,其中,車輛的GPS數(shù)據(jù)是處理過后的屏幕像素位置)。
其中carGps()函數(shù)遍歷map對象。若車輛信息不存在,則添加到map中,同時(shí)在頁面中新增顯示車輛標(biāo)志的div層;若已經(jīng)存在,則更新最新車輛數(shù)據(jù);最后遍歷map對象數(shù)據(jù),通過設(shè)置每個(gè)車輛div樣式的top和left屬性,實(shí)現(xiàn)車輛在Web頁面端的定位,如圖4所示。代碼如下:
圖4 車輛實(shí)時(shí)定位顯示效果
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;//經(jīng)度
var lon=obj1.lon;//緯度
$("#"+obj1.car).css({top:parseInt(lat),left:parseInt(lon)})
});
}
其中,addCar()函數(shù)在頁面中添加1個(gè)車輛圖標(biāo),實(shí)現(xiàn)可視化效果,代碼如下:
function addCar(data){
var cardiv=$('<div id="'+data.car+'"></div>');//創(chuàng)建 1個(gè) cardiv
cardiv.addClass('carDiv'); //添加 css樣式
cardiv.css({top:parseInt(data.lat),left:parseInt(data.lon)})
var car=$('<div class="car"></div>');//創(chuàng)建1個(gè) car
car.appendTo(cardiv);
var carp=$('<p>'+data.car+'</p>');//創(chuàng)建1個(gè)顯示車輛的cardiv
carp.appendTo(cardiv);
cardiv.appendTo('#index');
}
針對車輛的實(shí)時(shí)監(jiān)控問題,筆者提出并設(shè)計(jì)實(shí)現(xiàn)了一個(gè)基于Web瀏覽器的車輛監(jiān)控系統(tǒng)。搭建該系統(tǒng)基于Web的GPS監(jiān)控系統(tǒng)環(huán)境,成本較低,通過在車輛上安裝車載終端,可同時(shí)在多處Web瀏覽器上實(shí)時(shí)監(jiān)控多個(gè)車輛,執(zhí)行速度較快,安全性較高,且Web界面顯示操作簡單方便,易于被大眾所接受。由于標(biāo)識車輛需要通過車載終端的唯一編號確定,此系統(tǒng)更適用于固定場地的車輛監(jiān)控,如景區(qū)、學(xué)校等場所的游覽車、巡邏車的監(jiān)控,具有很好的市場前景。