Home        Store        Docs        Blog

Communication between Ardusub/ QGroundControl and underwater GPS System

(Sven) #1


we just installed our underwater GPS System on our BlueROV2 and everything is working (so far, we can tell). But there is a question that came up in our team and we can’t find a suitable answer on the webpage or in the forum. The Underwater GPS System from Waterlinked is only equipped with an A1 locator, without the depth sensor. How is the depth information transferred from the ROV to the Waterlinked system? In the documentation of the UGPS System they say, that the depth information needs to be provided to the system if an A1 locator is used. Did the Ardusub Software on the Pixhawk or the QGroundControl Software take care of this information transfer?

Another thing we noticed while trying out the system is that the displayed position information in QGroundcontrol has a small delay (maybe 1-2 sec) to the actual position in the test tank. In another forum post I read, that this is the effect of a fusion filter. Which sensor information are fused and how do we get the raw position data?

If anyone has information to the above questions or know a webpage where we can find the information, please let us know.

Thanks in advance


(Willian Galvani) #2

The Companion sends this information to the Water Linked system in this script.

The delay is probably caused by the Kalman filter and update rate of Water Linked.


Hi, I am trying to run the script, but get this error:

ErrorDuringImport: problem in underwater-gps - SystemExit: 2

Any idea what I am doing wrong?

I am 100% new at using python, so please be patient…

(Jacob) #4

@Mikxie, can you let us know what your goal is? This script is not designed to be run by a user.


I am trying to get the depth from the ROV forwarded to Waterlinked and position from Waterlinked into QGC.

BTW i use the S1 locator.

Update: For some reason the position is now working in QGC, so now I just need depth to reach Waterlinked…

(Jacob) #6

@Mikxie, If you are using the BlueROV2, you do not have to run any program yourself. Everything is configured automatically to send the depth.


Is it possible to check if it works by opening a udp port?

I just found it: “Stat for nerds” under “Diagnostic” in Waterlinked GUI

(Jacob) #8

If it is not working you will see an error message in the Water Linked GUI (if configured for the A1 or S1).

1 Like

Thank You. Next question is how to get the ROV position on a UDP port for use in other programs?

(Bo Koppel) #10

You could start with Waterlinkeds Python examples on their webpage.
I have also put one of my modules to Github:

1 Like
(Sven) #11

Hi everybody,

back to the main topic… the communication between the ROV and the waterlinked system.
I looked at the file @williangalvani referred me to and now I think I understood the connection. Yesterday we did a test in our pool and I noticed a difference between the display in QGC and the waterlinked system.

I think it’s a little bit weird when the data comes directly from the ROV.
This difference is visible in both data streams (RAW position and filltered position).

  • blue graph --> waterlinked underwater GPS
  • orange graph --> OptiTrack Motion Capture System

The ROV was at the surface and the A1 locator was mounted at the bottom of the side of the ROV pointing downwards. The pool is 6 m in diameter and 1 m depth. The display from QGC show constant depth of -0.2 m during the whole time. We used python (see the attached code) and the modified waterlinked api example to send the data via UDP to MATLAB.

Get position from Water Linked Underwater GPS
from __future__ import print_function
import requests
import argparse
import json
import socket
import struct
import time

ID1 = 1 # RAW Data
ID2 = 2 # Filtered Data
#Remote_Address_Port   = ("", 50008)
#Local_Address_Port    = ("", 50009)
Remote_Address_Port   = ("", 50008)
Local_Address_Port    = ("", 50009)

def get_data(url):
        r = requests.get(url)
    except requests.exceptions.RequestException as exc:
        print("Exception occured {}".format(exc))
        return None

    if r.status_code != requests.codes.ok:
        print("Got error {}: {}".format(r.status_code, r.text))
        return None

    return r.json()

def get_acoustic_position_raw(base_url):
    return get_data("{}/api/v1/position/acoustic/raw".format(base_url))
def get_acoustic_position_fil(base_url):
    return get_data("{}/api/v1/position/acoustic/filtered".format(base_url))

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-u', '--url', help='Base URL to use', type=str, default='')           # getting real data
    #parser.add_argument('-u', '--url', help='Base URL to use', type=str, default='http://demo.waterlinked.com')  # for demo use only
    args = parser.parse_args()

    base_url = args.url
    print("Using base_url: %s" % args.url)

    # Create a UDP socket at client side
    UDP_Socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
    # Bind to address and ip

    while 1:
        # std --> Current acoustic position accuracy (meter)
        # temp --> Current acoustic temperature
        # x --> Current acoustic x position relative to master electronics (meter)
        # y --> Current acoustic y position relative to master electronics (meter)
        # z --> Current acoustic z position (depth) relative to master electronics (meter)
        # Get current unfiltered acoustic position relative to master acoustics.
        data_raw = get_acoustic_position_raw(base_url)
        if data_raw:
            bytesToSend = bytearray(struct.pack("ffffff",ID1,data_raw["std"],data_raw["temp"],data_raw["x"],data_raw["y"],data_raw["z"]))
            UDP_Socket.sendto(bytesToSend, Remote_Address_Port)
        # Get current Kalman filtered acoustic position relative to master acoustics.
        data_fil = get_acoustic_position_fil(base_url)
        if data_fil:
            bytesToSend = bytearray(struct.pack("ffffff",ID2,data_fil["std"],data_fil["temp"],data_fil["x"],data_fil["y"],data_fil["z"]))
            UDP_Socket.sendto(bytesToSend, Remote_Address_Port)
        time.sleep(0.01)    # 10 Hz

if __name__ == "__main__":

Something else I’d like to ask: Does anyone know what the diagnostic view of the underwater GPS should look like? There are no labels on the axis and I really don’t know what is displayed in this plot.

:point_up_2: Is this a picture of a well-functioning system or rather of a not so well-functioning system?

Thanks in advance


(Bo Koppel) #12

Hi @sl13 Sven!
Basic answer about Waterlinked diagnostic page:
X axis is distance (or time from sending pulse)
Y axis is signal strength

You have some strong reflections in your pool
ie look at the green receiver that has two receptions almost same strength
Waterlinked should calculate with the first incoming strong peak, but reflections could be a problem

(Sven) #13

Hello @Boko Bo,
Thank you for the information regarding the waterlinked diagnostic page.
I’m still struggling with understanding why there is a difference in the depth displayed on the two programs (waterlinked website / QGC).
Do you guys from Bluerobotics have maybe a list of ports used in BlueROV2 (@jwalser)? Which script sends data to the ROV on port 25102?

(Jacob) #14

The depth shown in QGC is the depth measured by the pressure sensor. The depth shown in waterlinked is the depth of the locator head measured by the acoustic methods + combined (filtered) with the pressure sensor depth. The pressure sensor depth is just given to this algorithm to help it find the locator position. The numbers are not meant to be exactly the same.

Are the pressure sensor and the transducer at exactly the same depth?

Here is the script: https://github.com/bluerobotics/companion/blob/master/tools/underwater-gps.py

(Sven) #15

Hello, Jacob,
we use the A1 locator without the depth sensor, so that the only depth information can be given by the BlueROV2 depth sensor.

Maybe, but when I compare the depth value of the raw information (without filter) from the waterlinked system with the QGC display, there is the same difference.

The locating head and the depth sensor are only 8 cm apart from each other, so this cannot be the reason. And the zero point of the underwater GPS has been configured so that the zero point in detph is on the surface of the pool.

I know the script, because @williangalvani point me to it. But I’m wondering where the ROV values come from. In this script the data is received from port 25102, but which script sends this data to this port?

(Jacob) #16

I think you are still seeing the data collected by the acoustic estimation.

I think the acoustic measurement (in z axis) may not be accurate to 8cm in your setup. So it can be the reason. Especially if the locator is oriented pointing down, and at close range in a reflective environment (pool).

(Jacob) #17

@sl13 I’ll invite @johannes from Water Linked to comment.

You may also want to reach out to Water Linked support.

(Steven LE BARS) #18

Hi All,

I have an A1 locator and after I did all the software update (waterlinked and BlueRov) I still have instability on the automatic sending of the depth from the companion to the Webgui. I need to restart my whole system several times for it to work (one out of 10 approximately). Any idea where the problem could come from please?

Steven lb

(Jacob) #19

@Stevenlb, this shouldn’t happen. How is your current situation?

(Steven LE BARS) #20

Hi Jacob,

Thanks for your reply. After several discussions with Geoffrey Fournier, software engineer working with us, and Kristian from waterlinked, we might have identified that the problem comes from the initialization loop between the webgui and the BR2. The procedure we now apply is :

  1. Turn on the master D1 electronics and connect to the GUI

  2. Wait 1 minute

  3. Plug the BR2 battery

  4. Wait for the ROV depth to be read by the GUI – this can last up to 4-5minutes sometimes.

Applying this, it seems to work better. I need to validate that procedure we further trials at sea. Nevertheless, it still seems strange to me that we struggle so much to get the depth (and heading also sometimes) into the GUI.

Geoffrey also developped a workaround in case it does not work. We can still activate an option in our python controlling IHM to send the depth through the GUI API from mavlink « module load depth ouptput » as we already use it to send the ROV depth to OV-REC.


Best regards,