Getting Data from a GPS Moduleand calculating distance and bearing to a waypoint

Getting Data from a GPS Module

and calculating distance and bearing to a waypoint

Copyright:Copyright 2007 Dean Hall. All rights reserved.
Author:Dean Hall
Revision:2
Date:2007/11/25

Purpose

This page describes an early stage of experimentation with GPS navigation. For more, see the parent GPS page.

Test readings from the GPS modules

When using a GPS module on a Unix/Posix/Linux/Mac OS X machine, first find the path to the GPS device:

$ ls /dev/cu*
/dev/cu.Bender-DialupNetworking-1       /dev/cu.modem
/dev/cu.Bluetooth-Modem                 /dev/cu.usbserial
/dev/cu.Bluetooth-PDA-Sync

In my case, the path is /dev/cu.usbserial. Your path will be different based on what type of USB module you have. Use process of elmination to figure it out. Also, most OSs require the device to be plugged in for it to show up in /dev.

Most USB devices initalize their serial output to 4800 baud, 8N1. I use Python's serial module to open the device for reading and print the NMEA data stream:

$ python
>>> import serial
>>> s = serial.Serial("/dev/cu.usbserial", 4800)
>>> while True: s.readline() #ignore any junk on the first line of output
...
'x\x0f\xcf\xf8\xe6\xe0\x86\x00VTG,,T,,M,,N,,K,N*2C\r\n'
'$GPGGA,175914.000,2940.1044,N,09531.4268,W,1,04,1.8,5.4,M,-23.6,M,,0000*6F\r\n'
'$GPRMC,175914.000,A,2940.1044,N,09531.4268,W,0.00,,161107,,,A*65\r\n'
'$GPVTG,,T,,M,0.00,N,0.0,K,A*13\r\n'
'$GPGGA,175915.000,2940.1044,N,09531.4268,W,1,04,1.8,5.4,M,-23.6,M,,0000*6E\r\n'
'$GPGSA,A,3,28,11,26,04,,,,,,,,,4.4,1.8,4.0*35\r\n'
'$GPGSV,3,1,10,08,67,162,23,17,63,277,,28,58,003,37,27,41,159,*7B\r\n'
'$GPGSV,3,2,10,11,39,058,36,25,21,154,,29,19,278,,26,18,288,32*7F\r\n'
'$GPGSV,3,3,10,04,16,192,27,20,03,113,*76\r\n'
'$GPVTG,,T,,M,0.00,N,0.0,K,A*13\r\n'
^C

NMEA Data

The snippet of NMEA sentences from above is slightly contrived. The GGA, RMC and VTG are the most common. The GSA and GSV sentences occur less often. I looked up the meanings of the fields in the NMEA sentences in the SiRF NMEA Manual. Note that the RMC sentence is NMEA version 2.3 (or later), so it has one more field than prior versions. Also note when speed is zero, course is blank.

The following fields contain data that I plan to use in the robot software:

SentenceField NumField NameFormatUse
GPGGA1UTChhmmss.sssDate/Time
GPGGA2Latitudeddmm.mmmmPosition
GPGGA3N/S indicator[N|S]Position
GPGGA4Longitudedddmm.mmmmPosition
GPGGA5E/W indicator[E|W]Position
GPGGA6Position Fix[0..3]QOS
GPGGA7Satellites[0..12]QOS
GPGGA8HDOP#*.#*QOS
GPRMC2Status[A|V]QOS
GPRMC7Speed [knots]#*.##Velocity (magnitude)
GPRMC8Course [deg][<blank>|#*.##]Velocity (direction)
GPRMC9DateddmmyyDate/Time
GPRMC11Mode[A|D|E]QOS
Position Fix := (0 == invalid, 1 == GPS SPS mode, 2 == differential, 3 == GPS PPS mode)
Status := (A == data valid, V == data not valid)
Mode := (A == autonomous, D == DGPS, E == DR)

QOS (Quality of Signal) is not a real measurement of the quality of signals from the satellites. Instead, I use QOS as a generalization of the level of trust I should have for the position and heading data.

HDOP is the Horizontal Dilution of Precision.

Units of Measure

When utilizing collected data, it is important that all units be compatible. I also prefer using a sane unit system. So, I decided to use the SI units.

While centimeters per second would have made a nice unit to measure velocity because the range of an 8-bit number [0..255] would have covered the estimated max velocity of the vehicle. Using cm/s as a unit is not typically done. Instead, I will be using meters per second [m/s].

This means I will have to convert GPVTG field 5 (Speed [kph]) to [m/s]. This is done using the following units conversion:

Speed Km    1000   m    1      h
--------  * --------  * -------- ;  Speed [kph] * 0.278 = Speed [m/s]
       h    1     Km    3600   s

Using the SI system also has one handy feature when dealing with GPS coordinates. Due to where I live on the planet, the least significant digit (out of eight) of a coordinate pair is roughly equal to one-tenth of a meter. This gives me a nice rule-of-thumb when watching coordinates scroll by. This also means that the GPS coordinate numbers are roughly ten times more accurate than the 1 meter accuracy and precision I am counting on when receiving a strong and clean GPS reading.

Prototype Calculations

Using the NMEA data above, I now need a way to calculate the direction and distance the robot needs to go in order to reach a desired waypoint. The waypoint is also given as a GPS coordinate pair. So, my inputs are two pairs of latitude and longitude and I want outputs of bearing [degrees] and distance [meters]. I'm going to prototype these calculations in Python so I can fiddle with things until I have a good method of getting my desired output.

Bearing

This site gives a great discussion of the different calculations that are available for yielding useful output from lattitude and longitude values. The site recommends the following function that I translated to Python:

def calcBearing(lat1, lon1, lat2, lon2):
    dLon = lon2 - lon1
    y = math.sin(dLon) * math.cos(lat2)
    x = math.cos(lat1) * math.sin(lat2) \
        - math.sin(lat1) * math.cos(lat2) * math.cos(dLon)
    return math.atan2(y, x)

Distance

For distance, the Haversine method and the Spherical Law of Cosines (SLC) method are two (of many) ways to calculate the distance between a pair of coordinates. The SLC method uses less computation, but may have more error when performed with 32-bit floats or, worse, fixed point values. For completeness, I'm going to show the Haversine, the SLC and my easy planar trig methods. First, Python code for the Haversine:

def havDistance(lat1, lon1, lat2, lon2):
    dLat = lat2 - lat1
    dLon = lon2 - lon1
    a = math.sin(dLat / 2) * math.sin(dLat / 2) \
        + math.cos(lat1) * math.cos(lat2) \
        * math.sin(dLon / 2) * math.sin(dLon / 2);
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    return R * c

And the SLC method:

def slcDistance(lat1, lon1, lat2, lon2):
    return math.acos(math.sin(lat1) * math.sin(lat2)
                     + math.cos(lat1) * math.cos(lat2)
                     * math.cos(lon2 - lon1)) * R

Turning

The vehicle's current bearing is measured. The desired bearing is calculated. From these two data points, the amount and direction to turn must be calculated. I used the following algorithm for this:

def calcTurn(Btarget, Bcurrent):
    """Returns tuple (turn angle [rads], turn dir [+1 == right, -1 == left])."""

    diff = Btarget - Bcurrent
    neg = diff < 0
    big = abs(diff) > PI

    if not neg and not big: theta = diff; lr = +1
    if not neg and big: theta = 2*PI - diff; lr = -1
    if neg and not big: theta = abs(diff); lr = -1
    if neg and big: theta = 2*PI - abs(diff); lr = +1

    return (theta, lr)

Outcome

I tried the above bearing and distance functions for three pairs of coordinates. The first pair of coordinates is a "short" distance, the length of my driveway. The second pair is a "medium" distance, the length of two opposing cul-du-sacs in my neighborhood. The third pair is from my house to a Home Depot a few kilometers away. Here are the results of running each function on all 3 pairs of coordinates:

CoordinatesMethodResultError
DrivewayBearing-0.1808822711030
DrivewayhavDistance26.32299443810
DrivewayslcDistance26.3230932984-3.75566171046e-06
StreetBearing89.32352717260
StreethavDistance385.4151220770
StreetslcDistance385.4151208043.30248293708e-09
HomedepotBearing106.4722626230
HomedepothavDistance4636.542864740
HomedepotslcDistance4636.54286494-4.36663269333e-11

I used the Haversine method of calculating distance as the benchmark for calculating the SLC method's error. Results show that the SLC's error is very small, approaching insignificance. Since the SLC method has fewer trig functions, I will use it in Argonaut's code.

It remains to be seen if Argonaut's AVR processor can calculate these trig-heavy functions using floating point values fast enough to run the control system in real time. Conversely, using fixed point numbers may not yeild enough precision to give meaningful results. I will let actual experiments determine the type of numbers used.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值