Иван Иванов   28 февраля в 16:48

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

Подключаем модуль 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

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

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

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

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

sudo cat /dev/serial0

Эта команда вернет вам что-то вроде этого:

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

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

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

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

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

sudo apt-get install gpsd gpsd-clients

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

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

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

Библиотека, которую мы установили на последнем шаге, позволяет вам общаться с "демоном" GPS, который затем связывается с GPS-приемником. gpsd использует объекты JSON для связи со своими клиентами, поэтому вы получите такие объекты JSON при использовании библиотеки gpsd-клиентов, которую вы можете проанализировать в своем скрипте 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. Нет необходимости устанавливать какие-либо дополнительные пакеты. Однако я рекомендую вам использовать библиотеку, такую как пакет gpsd-client, для связи с приемником GPS, поскольку это облегчает обработку множества различных команд и неожиданных ситуаций.

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!"

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