Ping360 effective range

Hello!
We have Ping360 on our ROV. It works fine, but maximum distance to strong object, such as ship or wall, is about 10m (30ft). We can’t see anything on bigger distances. I don’t have pictures yet, but i will take it. Water is fresh and very clear, so no problems of acoustic absorbtion.
In protocol description i read that acoustic data have only 8 bits. Is any signal compression, such as logarithm, or TVG userd in sonar? Is sonar tested on long ranges?

I did a quick range test of my own and got about 120 feet for my max range. The frequency and ping duration will affect the max range and the quality of the image.

Hi @Kost, welcome to the forum :slight_smile:

Our Ping360 technical details specify a maximum range of 50m (165ft), and it’s expected that large objects should be detectable at that range, assuming enough of the transmitted beam is reflected back towards the sonar (dependent on target visibility and reflectivity, and angle of incidence between the transmitted beam and the target, as discussed in our scanning sonar guide).

As an example consider the visibility for a ship in the water:

Consider also that at large distances the sensor size as a proportion of the return signal becomes very small, especially for areas that aren’t perpendicular to the beam, so the measured return signal is much weaker than for a closer target.

For improving the long-distance performance more generally, I’d suggest you look at the Ping360 Device Settings section of our PingViewer documentation. That should provide you with an idea of what each parameter does and how to better tune them to specific operating conditions (in particular for long-distance consider the receiver gain and transmit duration).

1 Like

Brian, thanks or video, our pictures looks not so good.

Eliot, good explanation, but i know, how hydroacoustics works. And can’t understand, why we doesn’t see very strong ojects like vertical wall at 15m (45ft), but Brian’s sonar can see angled bottom at 30m (100ft) distance. We try to adjust a frequency and gain, no luck. For pulse duration we use that code:

def adjustTransmitDuration(distance, samplePeriod, speedOfSound, _firmwareMinTransmitDuration=5):
    # type: (float, float, int, int) -> float
    """
     @brief Adjust the transmit duration for a specific range
     Per firmware engineer:
     1. Starting point is TxPulse in usec = ((one-way range in metres) * 8000) / (Velocity of sound in metres
     per second)
     2. Then check that TxPulse is wide enough for currently selected sample interval in usec, i.e.,
          if TxPulse < (2.5 * sample interval) then TxPulse = (2.5 * sample interval)
        (transmit duration is microseconds, samplePeriod() is nanoseconds)
     3. Perform limit checking
     Returns:
        float: Transmit duration
     """
    duration = 8000 * distance / speedOfSound
    transmit_duration = max(
        2.5 * getSamplePeriod(samplePeriod) / 1000, duration)
    return max(_firmwareMinTransmitDuration, min(transmitDurationMax(samplePeriod), transmit_duration))


def transmitDurationMax(samplePeriod, _firmwareMaxTransmitDuration=500):
    # type: (float, int) -> float
    """
    @brief The maximum transmit duration that will be applied is limited internally by the
    firmware to prevent damage to the hardware
    The maximum transmit duration is equal to 64 * the sample period in microseconds

    Returns:
        float: The maximum transmit duration possible
    """
    return min(_firmwareMaxTransmitDuration, getSamplePeriod(samplePeriod) * 64e6)

I believe that code is anywrere from Bluerov github.

Are you using PingViewer or your own code? If you’re using your own code you’ll need to make sure you’re applying any relevant time/distance scaling correctly or your distance estimates could be incorrect.

Also make sure you’re orienting the Ping360 correctly. In PingViewer if you turn off Heading Integration the top of the polar plot corresponds to the direction the arrow on the bottom of the Ping360 points (cable at the back). From memory in code the arrow points to angle 200, with the 0/400 gradians direction being where the cable penetrator is.

We using code based on this: src/ping360_sonar/sensor.py · master · ssh / ping360_sonar_python · GitLab
Time/distance almost correct, excluding sound velocity (we have 1425m/s but using 1500 for now), and checked on known distances. Device orienation is correct too.
I make some videos for you:
Лаборатория гидрологии и гидрофизики
We can’t make sonar video works on our server for now, but it’s downloadable and that same that embedded in main video stream. Picture looks very noisy, i switch the gain during record, but at most time it’s on “high”. Comparing to Brian’s video it’s very bad looking. On “ultra” scan setting picture is smooth, but at other settings we have strong periodical noise.
Do you have any recomendations about sonar shielding from electic noises? Our rov is tether-powered and have 400v->24v switching convertor inside and 24->12v stabilized psu for sonar. Connection is rs485, no galvanic insulation.

That code seems to be a slightly modified two year old copy of our bluerobotics-ping library file ping360.py.

I watched a few of these and it looks like you’re suffering some issues with signal noise, but it’s hard to tell how severe those issues are because I’m not sure what colour scheme you’re using. Is it possible for you to try using our Ping Viewer application so we can more easily interpret your data, or can you at least provide the mapping between ping intensity value and colour used in your plot?

The parsing code doesn’t add noise, so that has to be either signal noise (e.g. other sonars operating in the same frequency around your ROV) or electrical noise. Given how it displays, and that it’s present in both water and air, it’s most likely electrical noise.

I’m not sure what you’re referring to here - what is the “ultra” setting?

Electrical noise on the communication wires would corrupt packets, so your noise issues are likely instead related to a noisy power supply/ground that’s going to the ping360. A general suggestion is to keep the ping360 power wires as isolated as possible from the tether power wires and any thruster cables. That can be achieved with physical distance separation (run the wires on opposite sides of an enclosure) as well as electrical insulation (e.g. wrap the ping360 power wires in foil to keep external noise out, and/or wrap your thruster wires and tether power wires in foil to keep noise in).

If your noise issues are coming from an unclean supply rather than noise picked up on the wires then you’ll need to smooth out the supply, possibly by using some decoupling capacitors between your 12V and ground wires to reduce unwanted high frequencies. If possible I’d suggest you probe your power wires at the ping360 end with an oscilloscope, to determine how much noise and which frequencies are present. That can then serve as a reference so you can see how much the noise is reduced by different approaches (e.g. moving the power wires, shielding, and adding decoupling capacitors).

        elif setting == 'resolution':
            if self.debug_print: print(f'Trying set new range resolution : {value}')
            if value == 'LOW':
                self.control_data['step'] = 10
            elif value == 'MEDIUM':
                self.control_data['step'] = 6
            elif value == 'HIGH':
                self.control_data['step'] = 3
            elif value == 'ULTIMATE':
                self.control_data['step'] = 1

This is scan step in gradians.

I found these in code:

color_yellow    = (0,255,255)
color_blue      = (255,0,0)  
color_green     = (0,255,0)
color_red       = (0,0,255)
color_white     = (255,255,255)
color_black     = (0,0,0)
color_navy      = (128,0,0)
color_test      = (12, 13, 14)
        self.colour_range = { #according to Decoding Sonar Scanline Data.pdf from Tritech
            0: color_black,
            1: color_black,
            2: color_black,
            3: color_blue,
            4: color_navy,
            5: color_navy,
            6: (128, 0, 128),
            7: (128, 0, 128),
            8: (128, 128, 0),
            9: (0, 128, 0),
            10: (0, 128, 128),
            11: (0, 128, 128),
            12: (0, 255, 0),
            13: (128, 192, 255),
            14: (128, 255, 255),
            15: color_white
        }

self.colour_range[int(self.sonar_data['bins'][i] >> 4)]

I’m not a programmer, but i can try to adjust color scheme. I think that scheme is legacy from old tritech micron sofware because these model of rov can use it. Using actual ping360 software causes some problems and i can’t do it now, but i can make it.

Right, that probably just means the noise isn’t very consistent across pings (which would make sense), so when you’re using a smaller step size then the sonar display shows narrower bars, and the noise is less obvious.

Thanks for the colours. Here’s a comparison of those with a couple of the Ping Viewer schemes (and a custom one that I like to use):

ping360-display-comparison-colours

Note that I’ve set the distance to minimum, and the maximum transmit duration for purposes of easier colour comparisons.

Looking back at your videos it seems like you have some light green values in the noise, so clearly it’s quite strong (almost the strongest). That’s definitely not a normal display in the sensor, so reducing the electrical noise should help quite a bit with your visibility :slight_smile:

Our colour schemes that come default with Ping Viewer only specify a small number of colours, which get interpolated between for the 255 intensity values. The code you shared seems to instead only use the 16 specified values, and truncate/bucket the data to fit across those colours. Your code also seems to use a BGR colour-ordering, whereas Ping Viewer uses RGB.

To test with the equivalent of our ‘thermal blue’ theme you can use the following:

self.colour_range = {
    0: (95, 34, 5),
    1: (90, 69, 31),
    2: (86, 105, 58),
    3: (82, 141, 85),
    4: (73, 173, 115),
    5: (52, 197, 155),
    6: (31, 220, 195),
    7: (10, 243, 235),
    8: (0, 233, 237),
    9: (0, 191, 203),
    10: (0, 149, 169),
    11: (0, 106, 135),
    12: (1, 79, 120),
    13: (3, 58, 110),
    14: (5, 36, 101),
    15: (8, 15, 92)
}

To create that I wrote the following Python code, which interpolates a larger number of colours from a small specified set, using numpy, and scipy’s interpolation:

from scipy.interpolate import interp1d
import numpy as np
from pprint import pprint

def rgb_bgr_swap(colours):
    """ Returns an Nx3 numpy array of colours with columns 0 and 2 swapped. """
    swapped = colours.copy()
    swapped[:, 0], swapped[:,2] = colours[:,2], colours[:,0]
    return swapped
    
class Interpolator:
    def __init__(self, colours, input_max=255, input_min=0):
        self.colours = np.asarray(colours)
        self.input_max = input_max
        self.input_min = input_min
        self._interp = interp1d(np.linspace(input_min, input_max, len(colours)), colours.T)
    
    def __call__(self, query_points, output_dtype=np.uint8):
        return self._interp(query_points).T.astype(output_dtype)


thermal_blue = np.array([
    [5, 34, 95],
    [106, 168, 79],
    [255, 255, 0],
    [127, 96, 0],
    [92, 15, 8]
])

thermal_blue_bgr = rgb_bgr_swap(thermal_blue)
interp = Interpolator(thermal_blue_bgr, 15)
result = interp(np.arange(16))
pprint({i: colour for i, colour in enumerate(result)}, indent=4)

That Interpolator class could also be used to do the interpolation instead of the existing dictionary approach from the code you shared, but that would require some modification of how your existing code works, instead of just swapping out the colours that get used.

Thanks Eliot for so much help, i will try all approaches and show a results.


I make that shield and apply Ping360 color scheme. That improve visiblity at least twice. Can’t test it in same place as previous video yet, but i do some tests in “open sea”. In that case i have only one target, but i can feel the difference.

I can’t use that code, something broken here. But i butch it apart, make something working and calculate the color coefficient table.
Sample video here:

Glad to hear you managed to get some improved results :slight_smile:

Interesting shield - did you test the shield and colour scheme independently? I’m curious as to how much benefit each one provides.

Wow sorry, it seems like I left off the end of that line. I’ve fixed that now in the original code block :slight_smile: