How use pymavlink

Hello, I want to use jetson nano and pymavlink to make auv. I have installed the relevant library. I wrote a pytho file as follows, and there is no response at runtime.

import time
from pymavlink import mavutil

master = mavutil.mavlink_connection('/dev/ttyACM0',baud=115200)

    
master.wait_heartbeat()

# Arm
master.mav.command_long_send(
    master.target_system,
    master.target_component,
    mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,
    0,
    1, 0, 0, 0, 0, 0, 0)

def set_rc_channel_pwm(id, pwm=1500):
    if id < 1:
        print("Channel does not exist.")
        return

    if id < 9:
        rc_channel_values = [65535 for _ in range(8)]
        rc_channel_values[id - 1] = pwm
        master.mav.rc_channels_override_send(master.target_system,master.target_component, *rc_channel_values)
while True:
    time.sleep(0.2)
    set_rc_channel_pwm(2, 1600)

Hi,

First, try with the first script Autopilot (E.g: Pixhawk) connected to the computer via serial from our pymavlink documentation.
This script will make the pixhawk to reset and you’ll see the main LED going off and on again.

Hello,

This is slightly off the topic, but is it possible to interface with the companion similar to how you do in python with c++?

My boss wants me to use c++ and right now we’re just reading data from the sensors, so I’ve been able to get by on just listening on a udp port and parsing the messages with the libraries I generated with mavgen.

I guess the larger question is also, would I be able to access the sensor info any faster or more frequently if i were using python to connect to the companion rather than just listening on a port i told mavproxy to output to?

Hello,

You can use MAVlink with C++, but that is unlikely to make readings any faster. What is your goal? If you are using one of the streamed messages, you can add a --streamrate 20 (for 20hz) in http://192.168.2.2:2770/mavproxy or change the respective stream parameter (filter the parameters in QGC using SR0_).

If you are requesting a message every time, then running the script in the companion should reduce latency, and C++ could be faster.

My goal is really just to get out navigation data to a separate computer, similar to OP and his jetson Nano.

Right now we’re just using some values from SR0 and SR3 i believe (attitude, rangefinder data [we’re using BR Ping Sonar], speed, etc.).

In SITL changing the parameter for the frequency of the streams didn’t work; however, I suspect that’s just an SITL thing.

I understand that reading from the port will be the same with either language. What I meant was, with python is there a way to directly query the companion for this flight information rather than just wating for the stream and reading the mavlink messages from a port?

Would that realistically be any better than just having a thread constantly listening to the port and updating each value as it comes in from mavProxy?

If so, is there support to do so with c++?

I have found that my problem is that the rc channel does not match, but I still have a problem. When I control through mavproxy, I have to input ‘’rc all 1500 ‘’before I can use it normally, otherwise I will get out of control. but I don’t need this by running the .py file.

Hi, yes this behavior is described in our documentation.
Check our example Send RC (Joystick) and comments.

Hi,

There is a new feature that creates a REST API in mavproxy allowing an easy access to any mavlink information.
You can check the status of such feature in companion here:

1 Like

Thank you, this appears to be exactly what i was looking for.

However, this appears to just be on the dev branch for now.

Could I put this on my custom branch based off the stable version very easily?

Hi Justin,

This feature is available since mavproxy 1.8.7 (from May), to use it in companion, you need to upgrade the installed mavproxy version, install the necessary packages for the SERVER side and to configure mavproxy to provide the REST API server in the desired port that you wish to use.

1 Like

oh my bad, i just saw ardupilot: master and assumed.

Sorry i dont have access to my ROV right now, so i cant really test this and want to be thorough in making sure it works.

So i’ve just gone into my fork of the companion code (based on the bluerobotics master), and then checked out the right version of MAVProxy module. Should that work, or does it depend on other changes made on that branch of the companion?

Otherwise, will i have to manually download the dependencies for the server, or will automatically download them when I update the companion to this branch. (I usually just use the git page on the web UI to update the companion).

It’s my understanding that to start it i can use the command module load restserver and then similarly start, stop, and set the address. Can i do this from a separate mavproxy console on another computer, or must i put this on wherever the Pi starts up mavproxy?

Lastly, how might i simulate the companion? I’ve been contributing to ardusub and i’d like to be able to do the same for it. (this could belong on a thread of it’s own, idk I don’t do much web stuff)

Hello Justin,

You can install mavproxy and the optional requirements for the rest server with
pip install mavproxy[server] but I think you will run into some dependency issues (It’s been a while, I don’t remember quite well).

You can then set your mavproxy to something like this:

--master=/dev/autopilot,115200
--load-module='GPSInput,DepthOutput,restserver'
--source-system=200
--cmd="set heartbeat 0;restserver start;restserver address 0.0.0.0:5001;"
--out udpin:localhost:9000
--out udpbcast:192.168.2.255:14550
--mav20
--aircraft telemetry
--streamrate -1
1 Like

Awesome. I was able to use the module along with sitl by just installing like you said and loading it with mavproxy.

And I’ve been able to read from the server using postman, so everything works!

Thanks.

1 Like

Hi Justin,

If you can, it would be nice to share the final result of your project/code here in the forum :slight_smile:

Sadly, i cant share much about my project, but I’ve made some quick edits to my code for just this json communication in case in might help someone.

Note c++ is not my strongest language and that this is still half finished, but it works thus far.

The first file just defines some types for easy access to the data later, the second has the implementation of communication and the main method.

Info_Types.h:


// holds roll,pitch, and yaw as well as angular speed of each
struct Attitude {
    // in radians
    float roll, pitch;

    //radians from north
    float yaw;

    // in rad/s
    float rollSpeed, pitchSpeed, yawSpeed;

    Attitude() {
        Attitude(100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f);
    }

    Attitude(float r, float p, float y, float rs, float ps, float ys) {
        roll = r;
        pitch = p;
        yaw = y;

        rollSpeed = rs;
        pitchSpeed = ps;
        yawSpeed = ys;
    }
};

// holds data from accelerometers and gyroscopes
struct IMU {
    // in m/s/s
    float xacc, yacc, zacc;

    // in rad/s
    float xgyr, ygyr, zgyr;

    IMU(){
        IMU(-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f);
    };

    IMU(int xa, int ya, int za, int xg, int yg, int zg) {
        //starts in cm/s/s
        xacc = 0.01 * (float) xa;
        yacc = 0.01 * (float) ya;
        zacc = 0.01 * (float) za;

        xgyr = xg * 0.001;
        ygyr = yg * 0.001;
        zgyr = zg * 0.001;
    };
};

struct Rangefinder {
    // in meters
    float range;

    // raw voltage if available, zero otherwise
    float voltage;

    Rangefinder () {
        Rangefinder(-1.0f,-1.0f);
    };
    
    Rangefinder(float r, float v) {
        range = r;
        voltage = v;
    };
};

// scaled pressure
struct Barometer {
    // in hectopascals
    float absolutePressure;
    // in hectopascals
    float diffFromGroundPressure;

    // in celcius
    float temperature;

    Barometer() {
        Barometer(-1,-1, -1);
    };

    Barometer(float p, float d, float temp) {
        absolutePressure = p;
        diffFromGroundPressure = d;
        // starts in centi-degrees, so convert        
        temperature = temp / 100;
    };
};

// has basic flight info that is usually displayed in a HUD
struct VfrHud {
    // in degrees
    int heading;

    // throttle the motors are currently running at from 0 - 100.
    // 0 is full throttle down, 100 full throttle up, 50 for neutral
    // note that these values are effected by the input gain
    int throttle;

    //in m/s
    float speed;

    // in m
    float altitude;

    // in m/s
    float climbRate;

    VfrHud(){};
    VfrHud(int hdg, int throt, float spd, float alt, float clmbrt) {
        heading = hdg;
        throttle = throt;
        speed = spd;
        altitude = alt;
        climbRate = clmbrt;
    };
};

struct GPSData {
    // in degrees
    float heading;

    // distance from starting point in meters
    float relativeAltitude;

    // in m/s
    float velocity;
    // [x,y,z] in m/s
    float velocityComponents[3];

    // in 1E7 degrees
    long lat, lon;

    GPSData() {};

    GPSData(float head, float relAlt, float v, float vx, float vy, float vz, long la, long ln) {
        heading = head * 0.01f;    // convert from centi degrees
        relativeAltitude = relAlt * 0.001f;    // convert from mm
        // convert from cm to m
        velocity = v * 0.01f;
        velocityComponents[0] = vx * 0.01f;
        velocityComponents[1] = vy * 0.01f;
        velocityComponents[2] = vz * 0.01f;

        lat = la;
        lon = ln;
    };
};

ReadIn.cpp :

/*
    retrieved json example here:
        https://gist.github.com/connormanning/41efa6075515019e499c

    compile with:
        g++ readIn.cpp -ljsoncpp -lcurl -o reader

    command to launch sitl:
Tools/autotest/sim_vehicle.py -l 33.810313,-118.393000,10,270 --out=udp:0.0.0.0:14550 -v ArduSub --no-mavproxy

    command to launch mav-proxy:
mavproxy.py --master=tcp:0.0.0.0:5760 --sitl=0.0.0.0:5501 --out=udpout:0.0.0.0:14551 --out=udpout:0.0.0.0:14550

then use mavproxy commands:
    module load restserver
    restserver start
    restserver address 0.0.0.0:5001

 */

#include <cstdint>
#include <iostream>
#include <memory>
#include <string>

#include <curl/curl.h>
#include <jsoncpp/json/json.h>
#include <sys/types.h>
#include <stdio.h>

#include "Info_Types.h"

/* QNX timer version */
#if (defined __QNX__) | (defined __QNXNTO__)
u_int64_t microsSinceEpoch()
{
	
	struct timespec time;
	
	uint64_t micros = 0;
	
	clock_gettime(CLOCK_REALTIME, &time);  
	micros = (uint64_t)time.tv_sec * 1000000 + time.tv_nsec/1000;
	
	return micros;
}
#else
u_int64_t microsSinceEpoch()
{
	struct timeval tv;

	u_int64_t micros = 0;

	gettimeofday(&tv, NULL);
	micros =  ((u_int64_t)tv.tv_sec) * 1000000 + tv.tv_usec;
	
	return micros;
}
#endif

using namespace std;

namespace
{
    size_t callback(
            const char* in,
            size_t size,
            size_t num,
            string* out)
    {
        const size_t totalBytes(size * num);
        out->append(in, totalBytes);
        return totalBytes;
    }
}

const string baseURL = "0.0.0.0:5001/rest/mavlink/";

Json::Value getThing(string whatToGet)
{   
    const string url(baseURL + whatToGet);

    CURL* curl = curl_easy_init();

    // Set remote URL.
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    // Don't bother trying IPv6, which would increase DNS resolution time.
    curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);

    // Don't wait forever, time out after 10 seconds.
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

    // Follow HTTP redirects if necessary.
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

    // Response information.
    long httpCode(0);
    std::unique_ptr<string> httpData(new string());

    // Hook up data handling function.
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);

    // Hook up data container (will be passed as the last parameter to the
    // callback handling function).  Can be any pointer type, since it will
    // internally be passed as a void pointer.
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());

    // Run our HTTP GET command, capture the HTTP response code, and clean up.
    curl_easy_perform(curl);
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
    curl_easy_cleanup(curl);

    if (httpCode == 200)
    {
        // std::cout << "\nGot successful response from " << url << std::endl;

        // Response looks good - done using Curl now.  Try to parse the results
        // and print them out.
        Json::Value jsonData;
        Json::Reader jsonReader;

        if (jsonReader.parse(*httpData.get(), jsonData))
        {
            // std::cout << "Successfully parsed JSON data" << std::endl;
            cout << "\nJSON data received:" << endl;
            cout << jsonData.toStyledString() << endl;
            
            return jsonData;

        }
        else
        {
            cout << "Could not parse HTTP data as JSON" << endl;
            cout << "HTTP data was:\n" << *httpData.get() << endl;
            return Json::Value();
        }
    }
    else
    {
        cout << "Couldn't GET from " << url << " - exiting" << endl;
        return Json::Value();
    }

    // return 0;
}

// updates the attitude parameters
bool getAttitude (Attitude &a) {
    Json::Value att = getThing("ATTITUDE");
    if (att.empty()) {
        return false;
    }
    a = Attitude(
        stof(att["roll"].asString()),
        stof(att["pitch"].asString()),
        stof(att["yaw"].asString()),
        stof(att["rollspeed"].asString()),
        stof(att["pitchspeed"].asString()),
        stof(att["yawspeed"].asString())
    );
    return true;
}

bool getIMU(bool raw, IMU &imu) {
    Json::Value data = getThing(raw ? "RAW_IMU" : "SCALED_IMU2");
    if (data.empty()) {
        return false;
    }
    imu = IMU (
        stoi(data["xacc"].asString()),
        stoi(data["yacc"].asString()),
        stoi(data["zacc"].asString()),
        stoi(data["xgyro"].asString()),
        stoi(data["ygyro"].asString()),
        stoi(data["zgyro"].asString())
    );
    return true;
}

bool getRawImu (IMU &imu) {
    return getIMU(true, imu);
}

bool getScaledImu (IMU &imu) {
    return getIMU(false, imu);
}

bool getRangefinder(Rangefinder &rngfnd) {
        Json::Value data = getThing("RANGEFINDER");
    if (data.empty()) {
        return false;
    }
    rngfnd = Rangefinder(
        stof(data["distance"].asString()),
        stof(data["voltage"].asString())
    );
    return true;
}

int main () {
    u_int64_t total = 0;
    int samples = 10;
    for (int i = 0; i < samples; i++) {
        u_int64_t start = microsSinceEpoch();
        Attitude att;
        if (getAttitude(att)) {                
            printf("Attitude: \n");
            printf("roll: %f, pitch: %f, yaw: %f\n", att.roll, att.pitch, att.yaw);
            printf("rollSpeed: %f, pitchSpeed: %f, yawSpeed: %f\n", att.rollSpeed, att.pitchSpeed, att.yawSpeed);
        }
        total += (microsSinceEpoch() - start);
    }
    printf("\nAverage time in micro seconds: %lu \n", (total / samples));
}

Sory, there’s a bit of bloat in there.

When I tried to use

--cmd="module load restserver; restserver start; restserver address 0.0.0.0:5001;"

as an argument when starting mavProxy, it would throw and error, work, but then the restserver would stay up even if i closed mavProxy. As such, i just use the console to do commands manually for now.

Hope this helps someone :slight_smile:

1 Like

Hi Justin,

Thank you very much for sharing your work, It’ll probably help someone!
We are going to provide examples of how to use this new feature in future versions of companion, in ardusub.com
PS: Take a look in nlohmann/json library, is the best one for json that I know :wink:

best how?

I’ve already got this working pretty okay, so as long as it doesn’t start shooting errors, this should be fine.

Hey @williangalvani,

As I wrote below, when starting mavproxy with the argument:

 --cmd="restserver start;restserver address 0.0.0.0:5001;" 

it would throw an error, here’s the error:

Loaded module restserver
Rest server running: localhost:5000
Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/aName/.local/lib/python2.7/site-packages/MAVProxy/modules/mavproxy_restserver.py", line 90, in run
    self.server = make_server(self.address, self.port, self.app, threaded=True)
  File "/home/aName/.local/lib/python2.7/site-packages/werkzeug/serving.py", line 805, in make_server
    host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
  File "/home/aName/.local/lib/python2.7/site-packages/werkzeug/serving.py", line 698, in __init__
    HTTPServer.__init__(self, server_address, handler)
  File "/usr/lib/python2.7/SocketServer.py", line 417, in __init__
    self.server_bind()
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 108, in server_bind
    SocketServer.TCPServer.server_bind(self)
  File "/usr/lib/python2.7/SocketServer.py", line 431, in server_bind
    self.socket.bind(self.server_address)
  File "/usr/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
error: [Errno 98] Address already in use

Despite this, it would still work until i stopped that instance of mavproxy (had to use ctrl+z); however, the port would still stay in use until I manually stopped it (meaning that subsequent instances of mavproxy could not use it).

I could not fix this issue by moving around the arguments (i think maybe it has to do with both commands trying to change something at the same time).

I did notice that I didnt seem to need the restserver start command. For example, if i just started mavproxy with

mavproxy.py --master=tcp:0.0.0.0:5760 --sitl=0.0.0.0:5501 --out=udpout:0.0.0.0:14551 --out=udpout:0.0.0.0:14550 --load-module='restserver'

and then used the command: restserver address 0.0.0.0:5001
I was able to use the restserver just fine.

Note that all of this is using SITL. As such, it would be nice if someone could confirm if these problems do or don’t happen on an actual companion.

This means TCP port 5001 is already taken by someone (another instance of mavproxy?). You probably need only one instance of the server. If you need all mavproxy instances to have a rest server, you should run each server on it’s own port(5001, 5002, …).

Yes, I understand that.

My problem is not so much about the port already being in use as it is about the process being unstoppable and becoming a zombie.

The more i think about it, it seems like it may be less of an issue and more of just something interesting that I’d like to know

so, if I launch with

mavproxy.py --master=tcp:0.0.0.0:5760 --sitl=0.0.0.0:ut=udpout:0.0.0.0:14551 --out=udpout:0.0.0.0:14550 --load-module='restserver'

and then use the command restserver address 0.0.0.0:5432, I can stop mavproxy with crtl+c and everthing works fine and I could even do the exact same commands again in the same terminal.

However, if I start it with :

mavproxy.py --master=tcp:0.0.0.0:5760 --sitl=0.0.0.0:ut=udpout:0.0.0.0:14551 --out=udpout:0.0.0.0:14550 --load-module='restserver' --cmd="restserver address 0.0.0.0:5432;"

a restserver is started on that port and it works fine, but i can no longer stop mavproxy with ctrl+c unless i first use the restserver stop command. And if you use ctrl+c before using that command, you’re unable to use it after, as mavproxy stopped processing your commands (since you told it to stop with ctrl+c).

Then if i use ctrl+z to force stop it, the server will stay up, but is no longer connected to an active instance of mavproxy and will not have access to the values.