简体   繁体   English

从多个串行端口实时读取延迟(GPS,通过 RS232 - USB 适配器)

[英]Delay in Real-Time Reading from Multiple Serial Ports (GPS, through RS232 - USB adapter)

At the moment i am working on a ROS system with multiple sensors through various interfaces.目前,我正在通过各种接口开发具有多个传感器的 ROS 系统。 Two of the most important sensors are GPS and Gyro that are connected through RS232-USB adapter.两个最重要的传感器是 GPS 和陀螺仪,它们通过 RS232-USB 适配器连接。

I would like to emphasize that i am not in anyway familiar or expert in serial connections.我想强调的是,我对串行连接并不熟悉或不熟悉。

I use python to read the serial data and publish them to ROS-topic and subscribe them via Matlab Simulink.我使用 python 读取串行数据并将它们发布到 ROS-topic 并通过 Matlab Simulink 订阅它们。

I had been using one GPS and Gyro simultaneously and there were no problem at all (adapters from different manufacturer were used for each, let's say, i have 2 RS232-USB adapter manufacturers; TA and TB).我一直在同时使用一个 GPS 和陀螺仪,完全没有问题(每个制造商都使用了不同制造商的适配器,比方说,我有 2 个 RS232-USB 适配器制造商;TA 和 TB)。

Due to some purposes, i decided to install an additional GPS (hence 2 GPS(s) and a Gyro simultaneously were used in real time).由于某些目的,我决定安装一个额外的 GPS(因此同时使用了 2 个 GPS 和一个陀螺仪)。 Let's say i put those 2 GPS quite close;假设我把那 2 个 GPS 放得很近; less than 1m.小于1m。

Those two GPS have identical serial port setting (same baud rate, same parity, same stopbits, same update frequency, etc.) and i used the same RS232-USB adapter TA type for both GPS.这两个 GPS 具有相同的串行端口设置(相同的波特率、相同的奇偶校验、相同的停止位、相同的更新频率等),并且我为两个 GPS 使用了相同的 RS232-USB 适配器 TA 类型。

However, there was a significant delay when subscribing in Matlab Simulink (i used simulation data inspection/logger).但是,订阅 Matlab Simulink 时出现了明显的延迟(我使用了模拟数据检查/记录器)。 One of the GPS was late!其中一台 GPS 迟到了! What i mean by late is that one GPS did not update in real time.我的意思是说一个 GPS 没有实时更新。

For example, at t(time)=3, both GPS should be at p(position)=10.例如,在 t(time)=3 时,两个 GPS 都应该在 p(position)=10。 But one GPS reaches p=10 after 40~60 seconds after the other GPS;但是一个GPS在另一个GPS之后40~60秒达到p=10; GPS which is correct (time vs position is real time). GPS 是正确的(时间与位置是实时的)。 Below i attach a graph.下面我附上一张图。

Those two curves should not be that far那两条曲线应该不会那么远

I dont know what might have caused this problem, Gyro has no delay problem at all, it seems only those 2 GPS are racing each other to send the data.我不知道是什么原因导致了这个问题,陀螺仪根本没有延迟问题,似乎只有那两个 GPS 在互相竞争发送数据。

Edit (12/11/2019 12:34 GMT+9)编辑 (12/11/2019 12:34 GMT+9)

Below is my code for one GPS, and for other GPS i simply create a new .py, and use exactly the same code but with different node name and rostopic.下面是我的一个 GPS 代码,而对于另一个 GPS,我只是创建一个新的 .py,并使用完全相同的代码,但具有不同的节点名称和 rostopic。

#!/usr/bin/env python

import serial
import types
import re
import rospy
import time
import threading

from datetime import datetime
from shipcon.msg import gps_position
from shipcon.msg import gps_local_position
from shipcon.msg import gps_navinfo
from shipcon.msg import gps_time


#Constants-----------------------------------------------------------------------------------------
PUBLISH_RATE = 10 #Hz
SERIAL_PORT = "/dev/ttyUSB1"
BAUD_RATE = 115200

#Class-----------------------------------------------------------------------------------------
class GpsSerial:
    def __init__(self, serial_port, baud_rate):
        self.gpsArr = []
        self.gpsStr = ""
        self.pos = gps_position()
        self.localpos = gps_local_position()
        self.navinfo = gps_navinfo()
        self.time = gps_time()
        self.ser = serial.Serial(
            port = serial_port,
            baudrate = baud_rate,
            parity = serial.PARITY_NONE,
            bytesize = serial.EIGHTBITS,
            stopbits = serial.STOPBITS_ONE,
            timeout = None,
            xonxoff = 0, #Enable software flow control
            rtscts = 0, #Enable Hardware(RTS,CTS) flow control
            )

    def get_gps(self):
        self.gpsStr = self.ser.readline()
        self.gpsArr = self.gpsStr.split(',')

        if re.match(r"^\$.*", self.gpsArr[0]):

            if self.gpsArr[0] == '$PTNL':
                if self.gpsArr[1] == 'GGK':
                    self.pos.latitude_deg = int(float(self.gpsArr[4]) // 100)
                    self.pos.latitude_min = float(self.gpsArr[4])  - (self.pos.latitude_deg * 100)
                    self.pos.latitude_dir = self.gpsArr[5]
                    self.pos.longitude_deg = int(float(self.gpsArr[6]) // 100)
                    self.pos.longitude_min = float(self.gpsArr[6])  - (self.pos.longitude_deg * 100)
                    self.pos.longitude_dir = self.gpsArr[7]
                    self.pos.gps_quality = int(self.gpsArr[8])
                    self.pos.satellite_number = int(self.gpsArr[9])
                elif self.gpsArr[1] == 'VGK':
                    if self.gpsArr[4] =='-' or self.gpsArr[4] == '':
                        pass
                    else:
                        self.localpos.localpos_e = float(self.gpsArr[4])#float( re.sub(r'\+', "", self.gpsArr[4]) )
                    if self.gpsArr[5] =='-' or self.gpsArr[5] == '':
                        pass
                    else:
                        self.localpos.localpos_n = float(self.gpsArr[5])#float( re.sub(r'\+', "", self.gpsArr[5]) )
                    if self.gpsArr[6] =='-' or self.gpsArr[6] == '':
                        pass
                    else:
                        self.localpos.localpos_z = float(self.gpsArr[6])#float( re.sub(r'\+', "", self.gpsArr[6]) )
                elif self.gpsArr[1] == 'VHD':
                    self.localpos.local_azimath = float(self.gpsArr[4])
                    self.localpos.local_vertical = float(self.gpsArr[6])
                    self.localpos.local_range = float(self.gpsArr[8])

            elif self.gpsArr[0] == '$GPGST':
                self.pos.latitude_err = float(self.gpsArr[6])
                self.pos.longitude_err = float(self.gpsArr[7])
                self.pos.ellipse_err_maj = float(self.gpsArr[3])
                self.pos.ellipse_err_min = float(self.gpsArr[4])
                self.navinfo.truenorth_heading_err = float(self.gpsArr[5])

            elif self.gpsArr[0] == '$GPROT':
                if self.gpsArr[1] =='-' or self.gpsArr[1] == '':
                    pass
                else:
                    self.navinfo.rotation_rate = float(self.gpsArr[1])

            elif self.gpsArr[0] == '$GPVTG':
                if self.gpsArr[1] =='' or self.gpsArr[5]=='' or self.gpsArr[7]=='':
                    pass
                else:
                    self.navinfo.truenorth_heading = float(self.gpsArr[1])
                    self.navinfo.speed_knot = float(self.gpsArr[5])
                    self.navinfo.speed_km = float(self.gpsArr[7])

            elif self.gpsArr[0] == '$GPZDA':
                tmptime = datetime.strptime(self.gpsArr[1]+','+self.gpsArr[4]+self.gpsArr[3]+self.gpsArr[2], '%H%M%S.%f,%Y%m%d')
                self.time.day = tmptime.day
                self.time.month = tmptime.month
                self.time.year = tmptime.year
                self.time.hour = tmptime.hour
                self.time.min = tmptime.minute
                self.time.sec = tmptime.second

        elif self.gpsArr[0] == '$GPGGA':
                self.pos.latitude_deg = int(float(self.gpsArr[2]) // 100)
                self.pos.latitude_min = float(self.gpsArr[2])  - (self.pos.latitude_deg * 100)
                self.pos.latitude_dir = self.gpsArr[3]
                self.pos.longitude_deg = int(float(self.gpsArr[4]) // 100)
                self.pos.longitude_min = float(self.gpsArr[4])  - (self.pos.longitude_deg * 100)
                self.pos.longitude_dir = self.gpsArr[5]
                #self.pos.gps_quality = int(self.gpsArr[8])
                #self.pos.satellite_number = int(self.gpsArr[7]) 

class RosPublish:
    def __init__(self):
        self.pub_pos = rospy.Publisher('gps_position', gps_position, queue_size = 1)
        self.pub_localpos = rospy.Publisher('gps_localposition', gps_local_position, queue_size = 1)
        self.pub_navinfo = rospy.Publisher('gps_navinfo', gps_navinfo, queue_size = 1)
        self.pub_time = rospy.Publisher('gps_time', gps_time, queue_size = 1)

    def ros_log(self, pos, localpos, navinfo, time):
        rospy.loginfo(pos)
        rospy.loginfo(localpos)
        rospy.loginfo(navinfo)
        rospy.loginfo(time)

    def ros_pub(self, pos, localpos, navinfo, time):
        self.pub_pos.publish(pos)
        self.pub_localpos.publish(localpos)
        self.pub_navinfo.publish(navinfo)
        self.pub_time.publish(time)        



#Function-----------------------------------------------------------------------------------------
def publish_gps(gps):
    rospy.init_node('NODE_Gps', anonymous = True)
    ros = rospy.Rate(PUBLISH_RATE)
    pub = RosPublish()

    pub.ros_log(gps.pos, gps.localpos, gps.navinfo, gps.time)
    pub.ros_pub(gps.pos, gps.localpos, gps.navinfo, gps.time)
    ros.sleep()


def refresh_gps(gps):
    while not rospy.is_shutdown():
        gps.get_gps()



#Main-----------------------------------------------------------------------------------------
if __name__ == '__main__':
    try:
        gps = GpsSerial(SERIAL_PORT, BAUD_RATE)
        thread_1 = threading.Thread(target = refresh_gps, args = (gps,))
        thread_1.start()

        while not rospy.is_shutdown():
            publish_gps(gps)

    except rospy.ROSInterruptException:
        pass

I used Matlab to subscribe and log all the data from the sensors.我使用 Matlab 订阅和记录来自传感器的所有数据。 At the moment the only calculation that i am doing in Matlab is just a conversion of GPS long/lat data to XY coordinates to get the real time trajectory of my vessel.目前,我在 Matlab 中所做的唯一计算只是将 GPS 长/纬度数据转换为 XY 坐标,以获得我船只的实时轨迹。 This conversion is a very simple calculation which i dont think will cause a serious delay.这种转换是一个非常简单的计算,我认为不会造成严重的延迟。

If you could give me solution, or even an insight, it would be very appreciated.如果您能给我解决方案,甚至是见解,将不胜感激。 Thank you very much.非常感谢。

Warmest regards温馨问候

I do not have enough rep to post a comment, so this is not really an answer.我没有足够的代表发表评论,所以这不是一个真正的答案。 There are some issues with the way you have set up your python node.您设置 Python 节点的方式存在一些问题。 It might appear to be working, but it is definitely doing it quite inefficiently.它可能看起来有效,但它确实效率很低。 There might be more issues, but the following issues should be solved:可能还有更多问题,但应解决以下问题:

  1. move the rospy.init_node('NODE_Gps', anonymous = True) to your '__main__'.rospy.init_node('NODE_Gps', anonymous = True)到你的 '__main__'。 Currently you are re-initializing the node every time you publish the message.目前,您每次发布消息时都会重新初始化节点。 This has considerable overhead and is only meant to be done once when the node is started.这具有相当大的开销,并且仅在节点启动时执行一次。
  2. Very similar to point 1, move the all of the self.pub_... = rospy.Publisher(...) lines to your '__main__'.与第 1 点非常相似,将所有self.pub_... = rospy.Publisher(...)行移动到您的“__main__”。 Creating the publishers is meant to be done only once, in fact I would get rid of the entire "RosPublish class".创建发布者只需要完成一次,实际上我会摆脱整个“RosPublish 类”。 If you want some separation of ROS related things and your GPS handling code then make them a function and call it from the get_gps() function.如果你想将 ROS 相关的东西和你的 GPS 处理代码分开,那么把它们变成一个函数并从 get_gps() 函数中调用它。 If you want to keep your RosPublish class then move the three first lines of publish_gps(gps) to the RosPublish class init part and create the RosPublish object in your main.如果你想保留你的 RosPublish 类,那么将 publish_gps(gps) 的前三行移动到 RosPublish 类的初始化部分,并在你的主目录中创建 RosPublish 对象。

However you decide to refactor your node, keep in mind that rospy.init_node and the rospy.Publisher lines should only be called once.不管你决定重构你的节点,记住 rospy.init_node 和 rospy.Publisher 行应该只被调用一次。

Some more notes on the code:关于代码的更多说明:

  1. You are creating a thread to constantly read a line from the serial port.您正在创建一个线程以不断从串行端口读取一行。 From this issue at pyserial git , it seems that might cause some issues with the CPU usage.pyserial git 的这个问题来看,似乎可能会导致 CPU 使用率出现一些问题。 Combine that with the unnecessary overhead of initializing the ROS node and publishers it could well be that you are using up a lot of your resources, especially if you run two of these.再加上初始化 ROS 节点和发布者的不必要开销,很可能会消耗大量资源,尤其是当您运行其中两个时。 What is your CPU usage, when you run the node?当您运行节点时,您的 CPU 使用率是多少? You could tie the reading of the line to the publishing rate, only ask for the new line when you want to publish a message.您可以将行的阅读与发布率联系起来,仅在您要发布消息时才要求新行。 And/or implement the serial reading in a way that is not resource heavy.和/或以不占用大量资源的方式实现串行读取。
  2. The naming of the variable in the publish_gps(gps) function ros = rospy.Rate(PUBLISH_RATE) , it really bothers me for some reason. publish_gps(gps) 函数中变量的命名ros = rospy.Rate(PUBLISH_RATE) ,出于某种原因确实让我感到困扰。 The name of the variable has absolutely nothing to do with what it represents and is very misleading.变量的名称与其所代表的内容完全没有关系,而且非常具有误导性。
  3. The way you have structured your code seems very arbitrary in general.您构建代码的方式通常看起来非常随意。 Why are the "classes" classes and "functions" functions.为什么是“类”类和“函数”函数。 I think, and it's just my personal opinion, you could do this whole thing without any classes, just functions, that do one specific thing.我认为,这只是我个人的意见,你可以在没有任何类的情况下完成整个事情,而只是做一件特定事情的函数。 I think you could make it look a lot better and more understandable.我认为你可以让它看起来更好,更容易理解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM