Home        Store        Learn        Blog

Ping1D lag in receiving measurements

We have a ping installed on our new AUV we are building. We are getting considerable lag - about 2 seconds, which makes altitude control using the ping impossible.

This lag can be seen in these two plots. It was taken in a flat bottom pool. The depth sensor reaches its maximum 2s (at t=146s) before the ping reaches its minimum (at t=148s).

On our previous version of this vehicle, the altitude controller worked fine in the tank. When we upgraded the vehicle, we replaced the CPU and downloaded the latest ping python software. From then onwards we noticed the lag issues. We have tried downgrading the ping python software to 0.0.10 (what we used in the initial trials), but the lag issue still exists.

The ping firmware is currently 3.26. Sample rate = 10Hz.

Do you know how to fix these lag issues?

If a new version of the firmware fixes these issues, is there a way to update the ping firmware remotely (without using ping-viewer)?

Are you running Ping connected directly in the pixhawk or in companion ?
Ping does a series of fitting and filters to get the best result from the samples and some delay is expected but I can’t confirm phase delay right now.

We currently have a navio2 in our system (it isn’t a bluerov), but we are connected directly to its UART port.

A small delay would be fine, but 2 seconds is a lot.

How are you communicating with it ?
Can you share the code ? It may be a buffer problem.

Here’s the ROS node I’m using to communicate with the ping. It wouldn’t let me upload the file, so here it is pasted in:

#!/usr/bin/env python3

from brping import Ping1D
import rospy
from sensor_msgs.msg import Range
import numpy as np
import floatpi.srv
import std_srvs.srv

class Altimeter:
    def __init__(self):
        device = rospy.get_param("~device", "/dev/ttyAMA0")
        baudrate = rospy.get_param("~baudrate", 115200)
        sample_rate = rospy.get_param("~sample_rate", 10.0)
        speed_of_sound = rospy.get_param("~speed_of_sound", 1500.0)  # 1500m/s is the speed of sound in salt water, 1435 for fresh, 343 for air

        self.auto_mode = rospy.get_param("~auto_mode", True)
        self.range_min = rospy.get_param("~range_min", 0.1)
        self.range_max = rospy.get_param("~range_max", 30.0)

        self.range_pub = rospy.Publisher('ping', Range, queue_size=1)

        self.field_of_view = 30 * np.pi / 180.0

        # Old initialisation...
        # self.pinger = Ping1D(device, baudrate)

        # New initialisation
        self.pinger = Ping1D()
        self.pinger.connect_serial(device, baudrate)

        if self.pinger.initialize() is False:
            rospy.logerr("Failed to initialize Ping")
            exit(1)

        self.pinger.set_speed_of_sound(int(speed_of_sound))

        if not self.auto_mode:
            self.pinger.set_mode_auto(0)
            scan_length = (self.range_max - self.range_min) *1000.0
            self.pinger.set_range(self.range_min*1000., scan_length)
            rospy.loginfo("Setting scan range to [%f, %f] (m)" %self.range_min, self.range_max)

        self.count = 0

        self.set_range_service = rospy.Service('set_pinger_range', floatpi.srv.SetPingerRange, self.setRangeCallback)
        self.set_mode_service = rospy.Service('set_pinger_mode', std_srvs.srv.SetBool, self.setModeCallback)
        print("Started services")
        rospy.Timer(rospy.Duration(1 / sample_rate), self.timerCallback)

    def setRangeCallback(self, req):
        self.range_min = req.range_min
        self.range_max = req.range_max
        self.pinger.set_mode_auto(0)
        scan_length = (self.range_max - self.range_min) * 1000.0
        self.pinger.set_range(int(self.range_min * 1000.), int(scan_length))
        rospy.loginfo("Setting pinger to auto mode - necessary for custom scan range")
        rospy.loginfo("Setting scan range to [%f, %f] (m)" % (self.range_min, self.range_max))
        res = floatpi.srv.SetPingerRangeResponse()
        res.success = True
        return res

    def setModeCallback(self, req):
        self.pinger.set_mode_auto(req.data)
        if req.data is True:
            mode = "Auto"
        else:
            mode = "Manual"
        res = std_srvs.srv.SetBoolResponse()
        res.success = True
        res.message = "Set mode to %s" % mode
        rospy.loginfo("Set pinger mode to %s" % mode)
        return res



    def timerCallback(self, event):
        data = self.pinger.get_distance()

        if data:
            range_msg = Range()
            range_msg.header.stamp = rospy.Time.now()
            range_msg.header.frame_id = 'pinger'
            range_msg.header.seq = self.count
            range_msg.field_of_view = self.field_of_view
            range_msg.min_range = 0.5
            range_msg.max_range = 30.0
            range_msg.range = float(data["distance"])/1000.0
            self.range_pub.publish(range_msg)
        else:
            rospy.loginfo("Failed to get distance data")
        self.count += 1


if __name__ == "__main__":
    rospy.init_node('pinger')
    alt = Altimeter()
    rospy.spin()

This lag issue is really holding up our development, is there any updates/possible solutions?

Hi Jackson,

Sorry for the delay! Have you tested our minimal example ?
I just tested it here and everything appears to be normal, I was unable to detect the 2s delay that you found.
If everything goes fine with the example, I would recommend to run some kind of python profile in your code to detect where is slowing things, or check the cpu usage to see if something is holding the processing down.

Hi Patrick,
Sorry for the delay, it takes quite a long time to set up the tests for this.
Yes I have tested the minimal example, I built the code attached around that example. The CPU usage is fine, barely anything is running.

To show you the problem, I have adapted your simple example for the ping, and added the depth sensor.

#!/usr/bin/env python

#simplePingExample.py
from brping import Ping1D
import time
import argparse
import ms5837
from builtins import input
import csv
import os
##Parse Command line options
############################

parser = argparse.ArgumentParser(description="Ping python library example.")
parser.add_argument('--device', action="store", required=False, type=str, help="Ping device port. E.g: /dev/ttyUSB0")
parser.add_argument('--baudrate', action="store", type=int, default=115200, help="Ping device baudrate. E.g: 115200")
parser.add_argument('--udp', action="store", required=False, type=str, help="Ping UDP server. E.g: 192.168.2.2:9090")
args = parser.parse_args()
if args.device is None and args.udp is None:
    parser.print_help()
    exit(1)

# Make a new Ping
#myPing = Ping1D()
#if args.device is not None:
#    myPing.connect_serial(args.device, args.baudrate)
#elif args.udp is not None:
#    (host, port) = args.udp.split(':')
#    myPing.connect_udp(host, int(port))

myPing = Ping1D(args.device, args.baudrate)

if myPing.initialize() is False:
    print("Failed to initialize Ping!")
    exit(1)

sensor = ms5837.MS5837_30BA()
# We must initialize the sensor before reading it
if not sensor.init():
   print("Sensor could not be initialized")
   exit(1)
# We have to read values from sensor to update pressure and temperature
if not sensor.read():
    print("Sensor read failed!")
    exit(1)

print("------------------------------------")
print("Starting Ping..")
print("Press CTRL+C to exit")
print("------------------------------------")

input("Press Enter to continue...")

with open('logs.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerow(["Time", "Depth", "Range"])

# Read and print distance measurements with confidence
while True:
    data = myPing.get_distance()
    if sensor.read():
        depth = sensor.depth()
        print("Depth: %f"%depth)
    else:
        print("Failed to get depth")
    alt = float(data["distance"])/1000.0
    if data:
        print("Distance: %s\tConfidence: %s%%" % (alt, data["confidence"]))
    else:
        print("Failed to get distance data")

    with open('logs.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow([time.time(), depth, alt])

    time.sleep(0.1)

I dunked the AUV in the tank.

The results show a lag between the peaks of the depth, and the troughs of the ping.

I have updated the ping to the latest firmware, but I am still getting these issues.

I am only allowed one image per post. I repeated the dunking of the AUV, and this time recorded the values using ROS. It shows similar results, a clear lag between the peaks of the depth and troughs of the ping.