专栏首页FreeBuf智能网联汽车开发篇:行驶轨迹跟踪

智能网联汽车开发篇:行驶轨迹跟踪

0×00 前言

有时,在老婆下班开车回家的路上,我总是需要打电话问你到哪里了。

真希望能有个程序能实时了解车的实时位置。

于是有了本篇文章。

0×01 所需材料

1.树莓派。

树莓派的系统下载地址为:

https://www.raspberrypi.org/downloads/raspbian/

2.GPS模块。

3.GPS天线。

4.VPS服务器(虚拟专用服务器)

我使用的是vultr的VPS服务器(最便宜的3.5美元/月就可以,不用买贵的。并且不使用时可以删除掉,不计费的),注册地址(打个广告,可以忽略):

https://www.vultr.com/?ref=7521512

安装的系统为:Ubuntu 18.04.4 LTS。

5.汽车一辆。

没有汽车的话,也可以用自行车等交通工具代替,可以将设备放在背包中测试。

0×03 设计方案

设计方案如下:

1.首先将树莓派安置在目标小车上。 2.树莓派通过GPS模块实时采集GPS情报,并将GPS情报实时上传到云端服务器。 3.云端服务器将GPS信息存储在数据库中。 4.通过访问云端服务器的HTML网页,使用百度地图,将目标小车的轨迹描画出来。

0×04 部署过程

整个部署过程可以分为两部分:云端服务器部署和树莓派部署

Part1:云端服务器部署

步骤1:搭建PHP环境。

(1)安装MySQL。

sudo apt-get install mysql-server

(2)安装Apache。

sudo apt-get install apache2

(3)安装PHP7.0。

sudo apt-get install php7.0

检测是否安装成功:

php7.0 -v

(4)其他模块安装。

sudo apt-get install php-mbstring php7.0-mbstring php-gettext libapache2-mod-php7.0

(5)安装phpMyAdmin。

sudo apt-get install phpmyadmin

安装过程会提示输入mysql的root账号的密码,密码一定记住。

此时的phpmyadmin文件夹被安装在/usr/share/phpmyadmin下,为了能在浏览器中访问到phpmyadmin,需要在/var/www/html下做一个软连接到该文件夹:

进入/var/www/html文件夹,在该目录下执行如下操作:

sudo ln -s /usr/share/phpmyadmin

此时在浏览器中键入http://localhost/phpmyadmin ,进入管理界面。

(6)重启MySQL和Apache

sudo service mysql restart sudo service apache2 restart

步骤2:创建数据库。

通过http://localhost/phpmyadmin访问数据库,并建立如下数据库。

步骤3:创建更新经纬度的PHP接口。

进入/var/www/html文件夹,创建interface文件夹。

进入/var/www/html/interface文件夹,创建updateGPS.php文件。

功能:更新GPS信息到数据库。

<?php
    function isInvalidKey() {
        $session = @$_GET['session'] ? $_GET['session'] : '';

        if (empty($session)) {
            return true;
        }
        // 防止SQL注入
        if (false==ctype_alnum($session)) {
            return true;
        }

        if (isDeadKey($session)) {
            return true;
        }
        else {
            return false;
        }
    }

    function isDeadKey(&$session) {
        $mysql_server_name='localhost'; //mysql数据库服务器
        $mysql_username='root';   // user
        $mysql_password='password'; // 【注意,请设置为正确的密码。】
        $mysql_database='infos';  // 数据库名

        $con=mysqli_connect($mysql_server_name,$mysql_username,$mysql_password,$mysql_database);
        if(!$con){
            die("连接失败: " . mysql_error());
        }

        $sqldata="SELECT * FROM session_info WHERE session = '$session'";
        echo $sqldata;
        echo "<br>";
        $result=mysqli_query($con,$sqldata);
        mysqli_close($con);
        //echo mysqli_num_rows($result);

        if (mysqli_num_rows($result) == 0) {
            return true;
        }
        else {
            return false;
        }
    }

    function updateGPS() {
        $session = @$_GET['session'] ? $_GET['session'] : '';

        $mysql_server_name='localhost'; //mysql数据库服务器
        $mysql_username='root';   // user
        $mysql_password='password'; // 【注意,请设置为正确的密码。】
        $mysql_database='infos';  // 数据库名

        $connent=new mysqli($mysql_server_name,$mysql_username,$mysql_password,$mysql_database);
        if($connent->connect_error){
            die("连接失败: " . $connent->connect_error);
        }

        // 插入数据
        date_default_timezone_set('PRC');
        $time = date("Y/m/d H:i:s");
        $lat = $_GET['lat'];
        $lon = $_GET['lon'];

        $insertdata="insert into map_route(session,time,lat,lon) values('$session','$time','$lat','$lon')";
        echo $insertdata;
        if($connent->query($insertdata)==true){
            echo "插入数据成功";
        }else{
            echo "插入数据失败: " . $connent->error;
        }

        echo "<br>";

        //关闭数据库
        mysqli_close($connent);
    }

    if(isInvalidKey()) {
        exit("session is invalid");
    }

    //更新GPS
    updateGPS();
?>

步骤4:创建地图显示模块

进入/var/www/html文件夹,创建location文件夹。

location文件夹内的文件,见baidu网盘,如下:

链接: https://pan.baidu.com/s/1zamZax-S36paXvl04_tc9g 提取码: 3biu

主要功能:

读取数据库中的GPS信息,并用百度地图显示出来。

Part2:树莓派部署

创建updateGPS.py文件,代码如下,并使之在系统启动后自动运行。

代码功能:通过GPS模块,自动采集GPS信息,并将GPS信息转换为百度坐标系信息上传到云端服务器。

# -*- coding: utf-8 -*-
import serial
import pynmea2
import time
import requests
import urllib
import json
import math

x_pi = 3.14159265358979324 * 3000.0 / 180.0
pi = 3.1415926535897932384626  # π
a = 6378245.0  # 长半轴
ee = 0.00669342162296594323  # 偏心率平方

def gcj02_to_bd09(lng, lat):
    """
    火星坐标系(GCJ-02)转百度坐标系(BD-09)
    """
    z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_pi)
    theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_pi)
    bd_lng = z * math.cos(theta) + 0.0065
    bd_lat = z * math.sin(theta) + 0.006
    return [bd_lng, bd_lat]

def bd09_to_gcj02(bd_lon, bd_lat):
    """
    百度坐标系(BD-09)转火星坐标系(GCJ-02)
    """
    x = bd_lon - 0.0065
    y = bd_lat - 0.006
    z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
    theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
    gg_lng = z * math.cos(theta)
    gg_lat = z * math.sin(theta)
    return [gg_lng, gg_lat]

def wgs84_to_gcj02(lng, lat):
    """
    WGS84转GCJ02(火星坐标系)
    """
    if out_of_china(lng, lat):  # 判断是否在国内
        return [lng, lat]
    dlat = _transformlat(lng - 105.0, lat - 35.0)
    dlng = _transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * pi
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
    mglat = lat + dlat
    mglng = lng + dlng
    return [mglng, mglat]

def gcj02_to_wgs84(lng, lat):
    """
    GCJ02(火星坐标系)转GPS84
    """
    if out_of_china(lng, lat):
        return [lng, lat]
    dlat = _transformlat(lng - 105.0, lat - 35.0)
    dlng = _transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * pi
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
    mglat = lat + dlat
    mglng = lng + dlng
    return [lng * 2 - mglng, lat * 2 - mglat]

def bd09_to_wgs84(bd_lon, bd_lat):
    lon, lat = bd09_to_gcj02(bd_lon, bd_lat)
    return gcj02_to_wgs84(lon, lat)

def wgs84_to_bd09(lon, lat):
    lon, lat = wgs84_to_gcj02(lon, lat)
    return gcj02_to_bd09(lon, lat)

def _transformlat(lng, lat):
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + \
          0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
    ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
            math.sin(2.0 * lng * pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * pi) + 40.0 *
            math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *
            math.sin(lat * pi / 30.0)) * 2.0 / 3.0
    return ret

def _transformlng(lng, lat):
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
          0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
    ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
            math.sin(2.0 * lng * pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lng * pi) + 40.0 *
            math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *
            math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
    return ret

def out_of_china(lng, lat):
    return not (lng > 73.66 and lng < 135.05 and lat > 3.86 and lat < 53.55)

def report_GPS_to_server():
    ser = serial.Serial("/dev/ttyAMA0",9600)
    while True:
        line = ser.readline()
        if line.startswith('$GNRMC'):
            # The sentence has lat/long
            print line
            rmc = pynmea2.parse(line)

            #if len(rmc.lon)>0 and len(rmc.lat)>0:
        if rmc.status =='A':
                lon = int(float(rmc.lon)/100)+(float(rmc.lon)*10000%1000000)/10000/60
                lon = round(lon,6)
                lat = int(float(rmc.lat)/100)+(float(rmc.lat)*10000%1000000)/10000/60
                lat = round(lat,6)

                lon,lat =  wgs84_to_bd09(lon,lat)
                print lon,lat

                params = {'session' : 'Y8bhFnBJ7sePopR1','lat' : lat,'lon' : lon}
        try:
                    r = requests.post("http://VPS'sIP/interface/updateGPS.php", params=params)
        except Exception , e:
            print e
                #print (r.text)
                print "---------------------------------"

if __name__ == '__main__':
    report_GPS_to_server()

注意:

代码中的【http://VPS‘sIP/interface/updateGPS.php】需要正确设置为云端服务器的IP。

0×05 最终效果

开着汽车出去转了一大圈后,GPS信息会被实时上传到云端服务器。

在地球上任何有网络的地方,在浏览器中输入以下地址,就可以实时显示汽车的运行轨迹。

http://VPS的IP地址/location/index.html

手机端的效果图,如下所示:

PC端的效果图,如下所示:

0×06 结束

功能有许多可以优化的地方,比如可以追加轨迹播放、根据时间检索轨迹等功能。

最后,祝福祖国繁荣昌盛,疫情早日结束。

严正声明:本文仅用于技术探讨,严禁用于其他非法途径

*本文作者:xutiejun,转载请注明来自FreeBuf.COM

本文分享自微信公众号 - FreeBuf(freebuf),作者:xutiejun

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 联通疑似屏蔽工信部投诉网站,域名被指向127.0.0.1

    4月25日开始,陆续有用户在微博上反映,部分地区的联通用户无法正常访问工信部投诉网站,经过查证之后发现该地区联通用户在访问工信部投诉网站时IP地址竟然被解析到了...

    FB客服
  • 走进科学:如何正确的隐藏自己的行踪

    大家好!我发现很多新人在讨论 “匿名”,所以我想我该写一篇清晰讲解它的教程,无论你是否能够隐藏自己。 我们也会讨论“隐藏自己”的方法及工具,但先集中注意力去理...

    FB客服
  • WPScan使用完整攻略:如何对WordPress站点进行安全测试

    WPScan是Kali Linux默认自带的一款漏洞扫描工具,它采用PHP编写,能够扫描WordPress网站中的多种安全漏洞,其中包括主题漏洞、插件漏洞和Wo...

    FB客服
  • (数据科学学习手札59)从抓取数据到生成shp文件并展示

      shp格式的文件是地理信息领域最常见的文件格式之一,很好的结合了矢量数据与对应的标量数据,而在Python中我们可以使用pyshp来完成创建shp文件的过程...

    Feffery
  • python实现地理位置类数据爬取与geohash应用初探

    最近想做一个简单的地理位置分析,比如获取一些城市公交站点对应的geohash,geohash其实是将平时常见的经纬度进行了降维,这样可以进行类似附近的餐馆等内容...

    用户1539362
  • java经纬度操作

    通过第三方sdk定位目标经纬度,然后计算两个位置的距离等,合并经纬度为一个单一数字等(方便存数据库)和一些转换操作,代码如下:

    低调小熊猫
  • 电子竞技的广播–CDN

    本文来自The Broadcast Knowledge,演讲者是来自Vecima的广播电视市场营销副总裁Paul Martin,演讲主题是内容分发网络CDN。

    用户1324186
  • 加载resouce下的spring配置文件 原

    wuweixiang
  • 编程的几种境界与招式

    七月半夏
  • 编程的几种境界与招式

    常常听到管理层谈团队建设与团队成长的话题,一个团队要永葆生机,保持强大战斗力,就必须不断成长。不进则退,这已是亘古不变的道理,不仅仅适用于个人,对于团队来说也是...

    三哥

扫码关注云+社区

领取腾讯云代金券