Считываем данные GPS на Raspberry Pi с помощью Python

Распиновка Raspberry Pi GPIO

Рассмотрим подключение модуля GPS NEO-6M к Raspberry Pi и чтение данных при помощи языка программирования Python.

Оборудование

Для работы потребуются следующие компоненты:

  1. Raspberry Pi

  2. NEO-6M GPS модуль

  3. 10 000 mAh аккумулятор

  4. Соединительный провода/перемычки

  5. Макетная плата

Хотя в каждом современном смартфоне есть встроенный GPS, Raspberry Pi можно превратить в GPS-приёмник с помощью недорогого GPS-модуля NEO-6M. Портативность готового устройства обеспечивается за счёт батарейного блока или USB-павербанка.

Распиновка Rasbpberry Pi

Схема соединения

Подключение платы к GPS-модулю выполняется достаточно просто:

GPS модуль

Raspberry Pi

VCC

3.3V (пин 1)

RX

TXD/GPIO 14 (пин 8)

TX

RXD/GPIO 15 (пин 10)

GND

Ground (пин 6)

Распиновка «малины» приведена выше.

Последовательный интерфейс

Если последовательный интерфейс ещё не настроен, необходимо сделать это предварительно. Для этого отредактируем следующий файл:

sudo nano /etc/inittab

В нём нужно закомментировать (или удалить) следующую строку (предположительно последнюю), добавив # в начало. Сохранение файла выполняется комбинацией STR+O, выход — нажатием CTRL+X.

T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

Затем откроем для редактирования файл /boot/cmdline.txt:

sudo nano /boot/cmdline.txt

Из него следует удалить записи:

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

Вторая запись может отсутствовать в некоторых версиях ОС. В итоге файл должен выглядеть так:

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Для применения изменений необходима перезагрузка. Её можно выполнить и после установки остального ПО.

ПО GPS-модуля

Для работы с GPS-модулем Raspberry Pi потребуется несколько программ. Но сначала обновим репозитории.

sudo apt-get update
sudo apt-get install minicom gpsd gpsd-clients

Если перезагрузка ранее не выполнялась, сделайте её сейчас.

sudo reboot now

После этого всё должно быть готово, и можно переходить к тестированию модуля. Также укажем модулю рабочую скорость 9600 бод:

stty -F /dev/ttyAMA0 9600

Сообщений об ошибках появиться не должно. Текущую конфигурацию можно посмотреть командой:

stty -F /dev/ttyAMA0

Приступим к первому тесту:

minicom -b 9600 -o -D /dev/ttyAMA0

Здесь указывается скорость передачи и устройство (завершение — CTRL+A, Q). Для более наглядного отображения данных запустим gpsd, также указав скорость передачи:

sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock -n

Затем выполните:

cgps -s
Вывод cgps -s

Убедитесь в наличии сигнала от спутников GPS — выйдите на улицу или подойдите к окну. Качество приёма в конкретном месте можно предварительно оценить с помощью телефона.

Однако подключение GPS-модуля — лишь первый шаг. Поскольку ручной ввод команд неудобен для большинства проектов, далее рассмотрим, как с помощью Python получать данные позиционирования от GPS-модуля и применять их в своих разработках.

Чтение необработанных данных GPS с последовательного порта

Большинство GPS-модулей обмениваются данными с Raspberry Pi по простому последовательному соединению. Они передают строки с GPS-данными и другими сообщениями о состоянии. Эти строки называются NMEA выражениями. Получить доступ к ним можно, напрямую прочитав последовательный порт, к которому подключён GPS-модуль:

sudo cat /dev/serial0

Результат будет примерно следующим:

Необработанные данные GPS

Имейте в виду, что эту операцию нужно выполнять до открытия сокета gpsd, иначе последовательные данные будут перенаправлены в сокет. Команда GPGGA содержит данные GPS-фиксации, включая координаты. Если GPS-модуль не может определить местоположение, соответствующие поля будут пустыми (как на рисунке) или содержать нулевые значения.

Для использования GPS-данных в Python можно напрямую считывать строки, которые модуль передаёт на последовательный порт Raspberry Pi. Однако в этом случае весь разбор данных и обработку ошибок придётся выполнять самостоятельно. Краткий пример программы, считывающей NMEA-выражения непосредственно из последовательного порта и выводящей координаты в консоль, приведён в конце статьи. Результат работы этой программы выглядит так:

Вывод Python скрипта GPS

Такой подход работоспособен и подходит для некритичных приложений. Кроме того, при его использовании не требуется установка дополнительного ПО. Тем не менее, если приложение должно быть более надёжным и нет желания самостоятельно обрабатывать все ошибки и прочие NMEA-выражения, рекомендуется воспользоваться проверенной готовой библиотекой, которая возьмёт эту работу на себя.

Установка модуля gpsd-клиентов

Если вы выполняли все предыдущие шаги, gpsd и клиентская среда уже должны быть настроены и работать без сбоев. Если нет, установите все необходимые библиотеки и программы следующей командой:

sudo apt-get install gpsd gpsd-clients

Это позволит импортировать нужные модули при открытии консоли Python:

Импорт GPS модуля в Python

Если при импорте GPS-модуля возникает ошибка, проверьте корректность установки всех необходимых пакетов.

Использование библиотеки Python для разбора данных GPS

Библиотека, установленная на предыдущем шаге, позволяет взаимодействовать с «демоном» GPS, который, в свою очередь, общается с GPS-приёмником. gpsd использует JSON-объекты для связи с клиентами, поэтому при работе с библиотекой gpsd-клиентов вы будете получать такие JSON-объекты, которые затем можно обработать в Python-скрипте.

В данном уроке нас не интересует отправка запросов к GPS-приёмнику. Мы лишь анализируем ответы. Ответы содержат классы, имена которых соответствуют типам NMEA-сообщений. Важно учитывать, что JSON-объекты могут быть неполными, если какое-либо значение не определено. Это происходит, когда GPS-приёмник не может установить ваше местоположение — в таком случае соответствующее поле просто отсутствует. Полный перечень команд и подробное описание доступны в официальной документации.

В любом случае, мы знаем, что ответы представляют собой обычные JSON-объекты, в которых часть полей может быть пропущена. Поэтому для извлечения данных о местоположении достаточно считать и разобрать JSON-поля:

def getPositionData(gps):
    nx = gpsd.next()
    if nx['class'] == 'TPV':
        latitude = getattr(nx, 'lat', "Unknown")
        longitude = getattr(nx, 'lon', "Unknown")
        print "Your position: lon = " + str(longitude) + ", lat = " + str(latitude)

Полный Python-скрипт приведён ниже. Если при запуске возникает ошибка, убедитесь, что сервер gpsd запущен и подключён к нужному последовательному порту. При корректной работе вы увидите примерно следующее:

Вывод библиотеки GPS Python

Как видите, считывать значения с GPS-приёмника довольно несложно. Установка дополнительных пакетов при этом не требуется. Тем не менее, для взаимодействия с GPS-приёмником рекомендуется использовать специализированную библиотеку наподобие gpsd-client, так как она упрощает обработку множества различных команд и непредвиденных ситуаций.

Serial GPS

import serial

SERIAL_PORT = "/dev/serial0"
running = True

# В сообщении NMEA позиция передается как:
# DDMM.MMMMM, где DD обозначает градусы, а MM.MMMMM обозначает минуты.
# Однако я хочу преобразовать этот формат в следующий:
# DD.MMMM. Этот метод преобразует переданную строку в желаемый формат

def formatDegreesMinutes(coordinates, digits):

    parts = coordinates.split(".")

    if (len(parts) != 2):
        return coordinates

    if (digits > 3 or digits < 2):
        return coordinates

    left = parts[0]
    right = parts[1]
    degrees = str(left[:digits])
    minutes = str(right[:3])

    return degrees + "." + minutes

# Этот метод читает данные из последовательного порта, к которому подключен ключ GPS,
# и затем анализирует сообщения NMEA, которые он передает.
# GPS это последовательный порт, который используется для связи с адаптером GPS
def getPositionData(gps):
    data = gps.readline()
    message = data[0:6]
    if (message == "$GPRMC"):
        # GPRMC = Рекомендуемые минимальные конкретные данные GPS / транзит
        # Чтение фиксированных данных GPS является альтернативным подходом,
        # который также работает
        parts = data.split(",")
        if parts[2] == 'V':
            # V = Предупреждение, скорее всего, спутников нет в поле зрения ...
            print "GPS receiver warning"
        else:
            # Получить данные о местоположении, которые были переданы с сообщением GPRMC.
            # В этом примере интересуют только долгота и широта. Для других значений можно
            # обратиться к http://aprs.gids.nl/nmea/#rmc
            longitude = formatDegreesMinutes(parts[5], 3)
            latitude = formatDegreesMinutes(parts[3], 2)
            print "Your position: lon = " + str(longitude) + ", lat = " + str(latitude)
    else:
        # Обрабатывать другие сообщения NMEA и неподдерживаемые строки
        pass

print "Application started!"
gps = serial.Serial(SERIAL_PORT, baudrate = 9600, timeout = 0.5)

while running:
    try:
        getPositionData(gps)
    except KeyboardInterrupt:
        running = False
        gps.close()
        print "Application closed!"
    except:
        # Вы должны сделать некоторую обработку ошибок здесь ...
        print "Application error!"

Library GPS

from gps import *
import time

running = True

def getPositionData(gps):
    nx = gpsd.next()
    # Список всех поддерживаемых классов и полей см. на:
    # https://gpsd.gitlab.io/gpsd/gpsd_json.html
    if nx['class'] == 'TPV':
        latitude = getattr(nx,'lat', "Unknown")
        longitude = getattr(nx,'lon', "Unknown")
        print "Your position: lon = " + str(longitude) + ", lat = " + str(latitude)

gpsd = gps(mode=WATCH_ENABLE|WATCH_NEWSTYLE)

try:
    print "Application started!"
    while running:
        getPositionData(gpsd)
        time.sleep(1.0)

except (KeyboardInterrupt):
    running = False
    print "Applications closed!"

На этом всё. Успехов в ваших проектах!