Считываем данные GPS на Raspberry Pi с помощью Python
Рассмотрим подключение модуля GPS NEO-6M к Raspberry Pi и чтение данных при помощи языка программирования Python.
Оборудование
Для работы потребуются следующие компоненты:
Raspberry Pi
NEO-6M GPS модуль
10 000 mAh аккумулятор
Соединительный провода/перемычки
Макетная плата
Хотя в каждом современном смартфоне есть встроенный 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-объекты для связи с клиентами, поэтому при работе с библиотекой 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-приёмника довольно несложно. Установка дополнительных пакетов при этом не требуется. Тем не менее, для взаимодействия с 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!"
На этом всё. Успехов в ваших проектах!