37429浏览
查看: 37429|回复: 2

[动态] 使用Python在行空板上实现卫星导航功能

[复制链接]
本帖最后由 虚怀若谷 于 2024-4-8 14:43 编辑

使用Python在行空板上实现卫星导航功能

本指南旨在向您展示在行空板上使用 Python 开发卫星导航应用程序是多么容易。在本教程结束时,您将能够进一步扩展代码并开发满足您需求的出色应用程序。

硬件清单

使用Python在行空板上实现卫星导航功能图1

将天线与 GNSS 模块连接(天线接口:IPEX1)。现在将 GNSS 模块与 行空板连接并确保选择 I2C!最后一步很简单,只需将行空板与您的 PC 或笔记本电脑(通过 USB)连接即可。

使用Python在行空板上实现卫星导航功能图2

步骤1 创建项目文件夹结构,包括文件

项目结构非常简单!例如,创建一个名为“GNSS”的项目目录,并在其中另外创建两个名为“img”和“lib”的目录。另外,直接在“GNSS”内创建一个Python文件“main.py”。该文件稍后将用于为应用程序创建 GUI。在目录“img”中存储名为“satellite.png”的图片,在目录“lib”中创建名为“DFRobot_GNSS_I2C.py”的Python库文件。该库将用于与 GNSS 模块进行通信。

这里是项目结构的概述:

使用Python在行空板上实现卫星导航功能图3

步骤2 开发代码

让我们从名为“DFRobot_GNSS_I2C.py”的库文件开始。在那里您导入 2 个模块/包,定义一些常量并使用一些简单的方法创建一个类。 Python 文档字符串将详细解释每个方法的作用。

代码

from pinpong.board import I2C
from time import sleep

GNSS_DEVICE_ADDR = 0x20

MODE_GPS = 0x01
MODE_BeiDou = 0x02
MODE_GPS_BeiDou = 0x03
MODE_GLONASS = 0x04
MODE_GPS_GLONASS = 0x05
MODE_BEIDOU_GLONASS = 0x06
MODE_GPS_BEIDOU_GLONASS = 0x07

class DFRobot_GNSS_I2C:

    ENABLE_POWER = 0x00
    DISABLE_POWER = 0x01

    RGB_ON = 0x05
    RGB_OFF = 0x02

    I2C_YEAR_H = 0x00
    I2C_HOUR = 0x04
    I2C_LAT_1 = 0x07
    I2C_LON_1 = 0x0D
    I2C_USE_STAR = 0x13
    I2C_ALT_H = 0x14
    I2C_SOG_H = 0x17
    I2C_COG_H = 0x1A
    I2C_GNSS_MODE = 0x22
    I2C_SLEEP_MODE = 0x23
    I2C_RGB_MODE = 0x24

    def __init__(self, i2c_addr=GNSS_DEVICE_ADDR, bus=0):
        """
        Initialize the DFRobot_GNSS communication
        :param i2c_addr: I2C address
        :param bus: I2C bus number
        """
        self._addr = i2c_addr

        try:
            self._i2c = I2C(bus)
        except Exception as err:
            print(f'Could not initialize i2c! bus: {bus}, error: {err}')

    def _write_reg(self, reg, data) -> None:
        """
        Write data to the I2C register
        :param reg: register address
        :param data: data to write
        :return: None
        """
        if isinstance(data, int):
            data = [data]

        try:
            self._i2c.writeto_mem(self._addr, reg, bytearray(data))
        except Exception as err:
            print(f'Write issue: {err}')

    def _read_reg(self, reg, length) -> bytes:
        """
        Reads data from the I2C register
        :param reg: I2C register address
        :param length: number of bytes to read
        :return: bytes
        """
        try:
            result = self._i2c.readfrom_mem(self._addr, reg, length)
        except Exception as err:
            print(f'Read issue: {err}')
            result = [0, 0]

        return result

    @staticmethod
    def _calculate_latitude_longitude(value: bytes) -> float:
        """
        Calculates the latitude and longitude from bytes to float
        :param value: gnss bytes
        :return: list
        """
        val_dd = value[0]
        val_mm = value[1]
        val_mm_mm = value[2] * 65536 + value[3] * 256 + value[4]
        degree = val_dd + val_mm / 60.0 + val_mm_mm / 100000.0 / 60.0

        return degree

    @staticmethod
    def _optional_calculate_bytes_to_float(value: bytes) -> float:
        """
        Calculates the bytes to float (for altitude, cog and sog)
        :param value: gnss bytes
        :return: float
        """
        return value[0] * 256 + value[1] + value[2] / 100.0

    def set_enable_power(self) -> None:
        """
        Enable gnss power
        :return: None
        """
        self._write_reg(self.I2C_SLEEP_MODE, self.ENABLE_POWER)
        sleep(.1)

    def set_disable_power(self) -> None:
        """
        Disable gnss power
        :return: None
        """
        self._write_reg(self.I2C_SLEEP_MODE, self.DISABLE_POWER)
        sleep(.1)

    def set_rgb_on(self) -> None:
        """
        Turn LED on
        :return: None
        """
        self._write_reg(self.I2C_RGB_MODE, self.RGB_ON)
        sleep(.1)

    def set_rgb_off(self) -> None:
        """
        Turn LED off
        :return: None
        """
        self._write_reg(self.I2C_RGB_MODE, self.RGB_OFF)
        sleep(.1)

    def set_gnss_mode(self, mode: int) -> None:
        """
        Set gnss mode
        - 1 for GPS
        - 2 for BeiDou
        - 3 for GPS + BeiDou
        - 4 for GLONASS
        - 5 for GPS + GLONASS
        - 6 for BeiDou + GLONASS
        - 7 for GPS + BeiDou + GLONASS
        :param mode: number for mode
        :return: None
        """
        if 1 <= mode <= 7:
            self._write_reg(self.I2C_GNSS_MODE, int(mode))
            sleep(.1)

    def get_gnss_mode(self) -> int:
        """
        Get gnss mode (1 till 7)
        :return: number for GNSS mode
        """
        result = self._read_reg(self.I2C_GNSS_MODE, 1)
        return int(result[0])

    def get_num_sta_used(self) -> int:
        """
        Get number of current satellite used
        :return: number of current satellite used
        """
        result = self._read_reg(self.I2C_USE_STAR, 1)
        return int(result[0])

    def get_date(self) -> str:
        """
        Get date and return in format "YYYY-MM-DD"
        :return: str
        """
        year = 2000
        month = 1
        day = 1

        result = self._read_reg(self.I2C_YEAR_H, 4)

        if result != -1:
            year = result[0] * 256 + result[1]
            month = result[2]
            day = result[3]

        return f'{year}-{month:02d}-{day:02d}'

    def get_time(self) -> str:
        """
        Get utc time and return in format "HH:MM:SS"
        :return: str
        """
        hour = 0
        minute = 0
        second = 0

        result = self._read_reg(self.I2C_HOUR, 3)

        if result != -1:
            hour = result[0]
            minute = result[1]
            second = result[2]

        return f'{hour:02d}:{minute:02d}:{second:02d}'

    def get_lat(self) -> list:
        """
        Get latitude and return in format [degree, direction]
        :return: list
        """
        degree = 0.00
        direction = 'S'

        result = self._read_reg(self.I2C_LAT_1, 6)

        if result != -1:
            degree = DFRobot_GNSS_I2C._calculate_latitude_longitude(result)
            direction = chr(result[5])

        return [degree, direction]

    def get_lon(self) -> list:
        """
        Get longitude and return in format [degree, direction]
        :return: list
        """
        degree = 0.00
        direction = 'W'

        result = self._read_reg(self.I2C_LON_1, 6)

        if result != -1:
            degree = DFRobot_GNSS_I2C._calculate_latitude_longitude(result)
            direction = chr(result[5])

        return [degree, direction]

    def get_alt(self) -> float:
        """
        Get altitude over ground in meters
        :return: float
        """
        result = self._read_reg(self.I2C_ALT_H, 3)

        if result != -1:
            high = DFRobot_GNSS_I2C._optional_calculate_bytes_to_float(result)
        else:
            high = 0.0

        return high

    def get_cog(self) -> float:
        """
        Get course over ground in degrees
        :return: float
        """
        result = self._read_reg(self.I2C_COG_H, 3)

        if result != -1:
            cog = DFRobot_GNSS_I2C._optional_calculate_bytes_to_float(result)
        else:
            cog = 0.0

        return cog

    def get_sog(self) -> float:
        """
        Get speed over ground on knot
        :return: float
        """
        result = self._read_reg(self.I2C_SOG_H, 3)

        if result != -1:
            sog = DFRobot_GNSS_I2C._optional_calculate_bytes_to_float(result)
        else:
            sog = 0.0

        return sog

现在您在“main.py”中开发代码。另外,这里没有什么大魔法!很少有模块/包被导入(包括库)。定义了一些常量并创建了2个函数。通过 Python 标准库“Tkinter”创建 GUI。

代码

from pinpong.board import Board
from tkinter import Tk, Label
from PIL import ImageTk, Image
from tkintermapview import TkinterMapView
from lib.DFRobot_GNSS_I2C import DFRobot_GNSS_I2C, MODE_GPS_BEIDOU_GLONASS

SCREEN_WIDTH: int = 240
SCREEN_HEIGHT: int = 320

MINIMUM_SATELLITES: int = 3
DELAY_MILLISECONDS: int = 2000

TILE_SERVER: str = 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'
TILE_ZOOM: int = 15

def create_map(display, latitude, longitude) -> None:
    """
    Create a map with marker
    :param display: tkinter display object
    :param latitude: float value of latitude
    :param longitude: float value of longitude
    :return: None
    """
    gmap = TkinterMapView(display, width=SCREEN_WIDTH, height=SCREEN_HEIGHT)
    gmap.pack(fill='both', expand=True)
    gmap.set_tile_server(TILE_SERVER)
    gmap.set_position(latitude[0], longitude[0], marker=True)
    gmap.set_zoom(TILE_ZOOM)

def check_satellite() -> None:
    """
    Check if minimum of satellites is reached
    :return: None
    """
    global satellite_found
    global screen
    global sensor
    global image_label

    num_satellites = sensor.get_num_sta_used()
    current_time = sensor.get_time()
    current_date = sensor.get_date()

    print(f"Found {num_satellites} satellites at {current_date} {current_time}")

    if num_satellites > MINIMUM_SATELLITES and not satellite_found:
        satellite_found = True

        lat = sensor.get_lat()
        long = sensor.get_lon()

        sensor.set_disable_power()

        image_label.pack_forget()

        create_map(display=screen, latitude=lat, longitude=long)

    if not satellite_found:
        screen.after(DELAY_MILLISECONDS, check_satellite)

if __name__ == '__main__':
    Board().begin()

    sensor = DFRobot_GNSS_I2C()
    sensor.set_gnss_mode(MODE_GPS_BEIDOU_GLONASS)
    sensor.set_enable_power()
    sensor.set_rgb_on()

    satellite_found = False

    screen = Tk()
    screen.geometry(f'{SCREEN_WIDTH}x{SCREEN_HEIGHT}+0+0')
    screen.resizable(False, False)

    image_label = Label(screen)
    image_label.pack(fill='both')
    image_src = Image.open("img/satellite.png")
    image = ImageTk.PhotoImage(image_src)
    image_label.config(image=image)

    check_satellite()

    screen.mainloop()

如果从未使用过 TkinterMapView 看看这里!您可以用它做更多的事情,还可以根据您的需要更改地图提供商。

步骤3 在行空板上安装 Python 包

即使行空板已经安装了很多 Python 包,你也必须自己快速安装 TkinterMapView。因此,通过 SSH(可能在 Windows 上使用 Putty)连接到 行空板并在行空板终端内运行以下命令:

$ pip3 安装 tkintermapview

查看几秒钟后,您应该准备好了。

步骤4 上传项目并运行

使用 SCP,您可以将项目从 PC/笔记本电脑上传到 UNIHIKER(可能在 Windows 上使用 WinSCP 或 SMB)。

$ scp -r GNSS root@10.1.2.3:/root/

用户root的密码是dfrobot这只是一个可能的示例,但我无法告诉您哪一种最适合您。如果你不明白我的意思,请阅读维基百科

现在启动应用程序并等待卫星......

使用Python在行空板上实现卫星导航功能图4

如果您无法立即找到卫星,请改变您的位置(最好在室外)。几秒钟后您应该会看到最终结果。

使用Python在行空板上实现卫星导航功能图5

如果您想要更多类似的东西,请留下一个点赞。

作者:Lupin

发布时间:2024年2月16日

原文链接:https://community.dfrobot.com/makelog-314081.html

b46d2c2e4286005bc8c65200063c5234.zip

14.7 KB, 下载次数: 61

文件

曾剑波  初级技匠

发表于 2024-5-7 03:03:07

值得参考学习一下
回复

使用道具 举报

easy猿  初级技师

发表于 2024-5-18 22:32:40

国内的地图打不开
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail