UART串口->PX4飞控->QGC显示

Posted XXX_UUU_XXX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UART串口->PX4飞控->QGC显示相关的知识,希望对你有一定的参考价值。

目的:通过UART串口给PX4飞控发送信息,通过Mavlink协议在QGC界面上显示出来。

  • PX4版本:1.11.0
  • QGC版本:3.5.6
  • QT版本:5.12.6
  • Ubuntu:18.04

文件:mavlink/message_definitions/v1.0/read_sensor.xml

<?xml version="1.0"?>
<mavlink>
  <version>3</version>
  <messages>
    <message id="223" name="READ_UART_SENSOR">
      <description>Test all field types</description>
      <field type="char[20]" name="voltage_str">string</field>
      <field type="char[20]" name="current_str">string</field>
      <field type="char[20]" name="temperature_str">string</field>
      <field type="float" name="voltage_sensor">float</field>
      <field type="float" name="current_sensor">float</field>
      <field type="float" name="temperature_sensor">float</field>
    </message>
  </messages>
</mavlink>

根据示例:添加与编译一个mavlink(test.xml)创建文件mavlink/read_sensor/mavlink_msg_read_uart_sensor.h,并将该文件放到Firmware/mavlink/include/mavlink/v2.0/common目录下,在common.h中添加

//第2113行
#include "./mavlink_msg_read_uart_sensor.h"

修改 Firmware/src/modules/mavlink/mavlink_messages.cpp文件,添加类 

/* read_uart_sensor */
class MavlinkStreamread_uart_sensor : public MavlinkStream
{
public:
        void update(orb_advert_t *mavlink_log_pub);

        const char *get_name() const override
        {
                return MavlinkStreamread_uart_sensor::get_name_static();
        }

        static constexpr const char *get_name_static()
        {
                return "READ_UART_SENSOR";
        }

        static constexpr uint16_t get_id_static()
        {
                return MAVLINK_MSG_ID_READ_UART_SENSOR;
        }

        uint16_t get_id() override
        {
                return get_id_static();
        }

        static MavlinkStream *new_instance(Mavlink *mavlink)
        {
                return new MavlinkStreamread_uart_sensor(mavlink);
        }

        unsigned get_size() override
        {
            return MAVLINK_MSG_ID_READ_UART_SENSOR_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
        }

private:
        // MavlinkOrbSubscription *_read_uart_sensor_sub;
	uORB::Subscription _read_uart_sensor_sub{ORB_ID(read_uart_sensor)};
        // uint64_t _read_uart_sensor_time;


        /* do not allow top copying this class */
        MavlinkStreamread_uart_sensor(MavlinkStreamread_uart_sensor &) = delete;
        MavlinkStreamread_uart_sensor &operator = (const MavlinkStreamread_uart_sensor &) = delete;

protected:
        explicit MavlinkStreamread_uart_sensor(Mavlink *mavlink) : MavlinkStream(mavlink)
		// ,
                // _read_uart_sensor_sub(_mavlink->add_orb_subscription(ORB_ID(read_uart_sensor))),
                // _read_uart_sensor_time(0)
        {}
           ~MavlinkStreamread_uart_sensor()
        {}

        bool send(const hrt_abstime t) override
        {

                read_uart_sensor_s orbtest = {};
        	// if(  _read_uart_sensor_sub->update(&_read_uart_sensor_time,&orbtest)){
		if(_read_uart_sensor_sub.update(&orbtest)){
			__mavlink_read_uart_sensor_t msg = {};

		   	msg.voltage_sensor = orbtest.voltage_sensor;
			msg.current_sensor = orbtest.current_sensor;
			msg.temperature_sensor = orbtest.temperature_sensor;

			// char a1[20];
			// char b1[20];
			// char c1[20];

			// sprintf(a1,"%s",msg.voltage_str);
                        // sprintf(b1,"%s",msg.current_str);
                        // sprintf(c1,"%s",msg.temperature_str);

                        mavlink_msg_read_uart_sensor_send_struct(_mavlink->get_channel(), &msg);
                        return true;
                }
         return false;
        }
};
//第5334行
static const StreamListItem streams_list[] = {
	create_stream_list_item<MavlinkStreamread_uart_sensor>(),//添加一行创建流列表

修改Firmware/src/modules/mavlink/mavlink_main.cpp文件,设置频率

configure_stream("READ_UART_SENSOR", 20.0f);

文件:src/modules/read_uart_sensor/read_uart_sensor.c

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <drivers/drv_hrt.h>
#include <string.h>
#include <systemlib/err.h>
#include <nuttx/config.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/read_uart_sensor.h>
#include <px4_platform_common/tasks.h>

static int daemon_task;
static bool thread_should_exit = true;
static bool thread_running = false;

__EXPORT int read_uart_sensor_main(int argc, char *argv[]);
int read_uart_thread_main(int argc, char *argv[]);

static void usage(const char *reason);
static int uart_init(char * uart_name);
static int set_uart_baudrate(const int fd, unsigned int baud);

int set_uart_baudrate(const int fd, unsigned int baud)
{
    int speed;

    switch (baud) {
    case 9600:   speed = 9600;   break;
    case 19200:  speed = 19200;  break;
    case 38400:  speed = 38400;  break;
    case 57600:  speed = 57600;  break;
    case 115200: speed = 115200; break;
    default:
        warnx("ERR: baudrate: %d\\n", baud);
        return -EINVAL;
    }

    struct termios uart_config;
    /*
    termios 函数族提供了一个常规的终端接口,用于控制非同步通信端口。 这个结构包含了至少下列成员:
    tcflag_t c_iflag;      输入模式
    tcflag_t c_oflag;      输出模式
    tcflag_t c_cflag;      控制模式
    tcflag_t c_lflag;      本地模式
    cc_t c_cc[NCCS];       控制字符
    */

    int termios_state;

    /* 以新的配置填充结构体 */
    /* 设置某个选项,那么就使用"|="运算,
     * 如果关闭某个选项就使用"&="和"~"运算
     * */
    tcgetattr(fd, &uart_config); // 获取终端参数

    /* clear ONLCR flag (which appends a CR for every LF) */
    uart_config.c_oflag &= ~ONLCR;// 将NL转换成CR(回车)-NL后输出。

    /* 无偶校验,一个停止位 */
    uart_config.c_cflag &= ~(CSTOPB | PARENB);// CSTOPB 使用两个停止位,PARENB 表示偶校验

    /* 设置波特率 */
    if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) {
        warnx("ERR: %d (cfsetispeed)\\n", termios_state);
        return false;
    }

    if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
        warnx("ERR: %d (cfsetospeed)\\n", termios_state);
        return false;
    }
    // 设置与终端相关的参数,TCSANOW 立即改变参数
    if ((termios_state = tcsetattr(fd, TCSANOW, &uart_config)) < 0) {
        warnx("ERR: %d (tcsetattr)\\n", termios_state);
        return false;
    }
    return true;
}

int uart_init(char * uart_name)
{
    int serial_fd = open(uart_name, O_RDWR | O_NOCTTY);
    // printf("serial_fd = %d", serial_fd);
    if (serial_fd < 0)
    {
        err(1, "failed to open port: %s", uart_name);
        return false;
    }
    return serial_fd;
}

static void usage(const char *reason)
{
    if (reason) {
        fprintf(stderr, "%s\\n", reason);
    }

    fprintf(stderr, "usage: read_uart_sensor {start|stop|status} [param]\\n\\n");
    exit(1);
}

int read_uart_sensor_main(int argc, char *argv[])
{
    // printf("argv_0 = %s,argv_1 = %s, argv_2 = %s",argv[0],argv[1], argv[2]);
    if (argc < 2)
    {
        usage("missing command");
    }

    if (!strcmp(argv[1], "start")){
        if (thread_running){
            warnx("already running\\n");
            // exit(0);
            return 0;
        }

        thread_should_exit = false;
        daemon_task = px4_task_spawn_cmd("read_uart_sensor",//任务接口句柄
                                         SCHED_DEFAULT,
                                        //  SCHED_PRIORITY_DEFAULT,
                                         SCHED_PRIORITY_MAX - 5,
                                         2000,
                                         read_uart_thread_main,
                                         (argv) ? (char * const *)&argv[2] : (char * const *)NULL);
        // exit(0);
        return 0;
    }

    if (!strcmp(argv[1], "stop")){
        thread_should_exit = true;
        // exit(0);
        return 0;
    }

    if (!strcmp(argv[1], "status")){
        if (thread_running)
        {
            warnx("running");
            // return 0;//

        } else
        {
            warnx("stopped");
            // return 1;//
        }
        return 0;
    }
    usage("unrecognized command");
    PX4_INFO("exiting");
    return 1;
}

int read_uart_thread_main(int argc,char *argv[])
{

    int index = 0;
    char data = '0';
    char buffer[20] = "";
    const char douhao[1] = ","; //逗号
    char *result = NULL;

    /*
     * TELEM1 : /dev/ttyS1
     * TELEM2 : /dev/ttyS2
     * GPS    : /dev/ttyS3
     * NSH    : /dev/ttyS5
     * SERIAL4: /dev/ttyS6
     * N/A    : /dev/ttyS4
     * IO DEBUG (RX only):/dev/ttyS0
     */
    // int uart_read = uart_init(uart_name);
    int uart_read = uart_init("/dev/ttyS3");

    if(false == uart_read){
        printf("read uart failed\\n");
        return -1;
    }
    if(false == set_uart_baudrate(uart_read,115200))
    {
        printf("set_uart_baudrate is failed\\n");
        return -1;
    }
    printf("uart init is successful\\n");

    thread_running = true;

    /*初始化数据结构体 */
    struct read_uart_sensor_s orbtest;
    memset(&orbtest,0,sizeof(orbtest));

    //公告,公告必须在发布之前,否则即使订阅也得不到任何数据
    orb_advert_t pub_fd = orb_advertise(ORB_ID(read_uart_sensor),&orbtest);

    while (!thread_should_exit){
        read(uart_read,&data,1);
        // printf("data = %s\\n", data); 不能打印data,打印会崩溃

        /* 例:串口输入R14.68,10.23,25.67,   输出14.68,10.23,25.67, */
        if(data == 'R'){
            for(int i = 0;i <17;++i){
                read(uart_read,&data,1);
                buffer[i] = data;
                data = '0';
            }
            //逗号分割,返回下一个分割后的字符串指针,如果没有可检索的字符串,则返回一个空指针。
            result = strtok(buffer, douhao);
            while(result != NULL) {
                index++;
                switch(index){
                    case 1:
                        printf("case 1\\n");
                        //定义的数组不够大越界操作NULL被覆盖掉,每个数组起码加大1容纳NULL结尾
                        // strncpy(orbtest.voltage_str, result, 5);//5+1=6
                        strncpy(orbtest.voltage_str, result, 6);
                        break;
                    case 2:
                        printf("case 2\\n");
                        strncpy(orbtest.current_str, result, 6);
                        break;
                    case 3:
                        printf("case 3\\n");
                        strncpy(orbtest.temperature_str, result, 6);
                        break;
                }
                printf("result = %s\\n", result);
                result = strtok(NULL, douhao);
            }
            index = 0;

            //指定长度的字符串复制到字符数组中
            // strncpy(orbtest.datastr, buffer, 19);

            //atoi()把字符串转换成整型数
            // atof()把字符串转换成浮点数,默认为6位小数
            orbtest.voltage_sensor = atof(orbtest.voltage_str);
            orbtest.current_sensor = atof(orbtest.current_str);
            orbtest.temperature_sensor = atof(orbtest.temperature_str);

            //发布
            orb_publish(ORB_ID(read_uart_sensor), pub_fd, &orbtest);

            // PX4_INFO("orbtest.voltage_str = %s", orbtest.voltage_str);
            // PX4_INFO("orbtest.current_str = %s", orbtest.current_str);
            // PX4_INFO("orbtest.temperature_str = %s", orbtest.temperature_str);
            printf("orbtest.voltage_sensor = %.2f\\r\\n", (double)orbtest.voltage_sensor);
            printf("orbtest.current_sensor = %.2f\\r\\n", (double)orbtest.current_sensor);
            printf("orbtest.temperature_sensor = %.2f\\r\\n", (double)orbtest.temperature_sensor);
        }
    }

    warnx("exiting");
    thread_running = false;
    close(uart_read);
    fflush(stdout);
    return 0;
}

文件:src/modules/read_uart_sensor/CMakeLists.txt

set(MODULE_CFLAGS)
px4_add_module(
        MODULE modules_read_uart_sensor
        MAIN read_uart_sensor
    COMPILE_FLAGS
        -Os
    SRCS
        read_uart_sensor.c
    DEPENDS
)

文件:Firmware/mavlink/inclde/mavlink/v2.0/standard/standard.h

 至此无人机设置完毕,下面设置QGC地面站的Qt程序

将mavlink_msg_read_uart_sensor.h放置到qgroundcontrol/libs/mavlink/include/mavlink/v2.0/common/目录下

文件:qgroundcontrol/libs/mavlink/include/mavlink/v2.0/common/common.h

//第1428行
// MESSAGE DEFINITIONS
#include "./mavlink_msg_read_uart_sensor.h"

 文件:qgroundcontrol/libs/mavlink/include/mavlink/v2.0/ardupilotmega/ardupilotmega.h

//第27行
#define MAVLINK_MESSAGE_CRCS //添加{223, 70, 72, 0, 0, 0},同理飞控的standard.h

文件:qgroundcontrol/src/ui/toolbar/BatteryIndicator.qml

 //第99行
                    QGCLabel {text: qsTr("BMS_voltage:") }
                    QGCLabel {text:(_activeVehicle && _activeVehicle.battery.BMS_voltage.value != -1) ? (_activeVehicle.battery.BMS_voltage.valueString + " " + _activeVehicle.battery.BMS_voltage.units) : "N/A" }

文件:qgroundcontrol/src/Vehicle/Vehicle.h

class VehicleBatteryFactGroup : public FactGroup
{
    Q_OBJECT

public:
    VehicleBatteryFactGroup(QObject* parent = nullptr);
    //add BMS voltage, current, temperature
    //******************************************************************
    Q_PROPERTY(Fact* BMS_voltage        READ BMS_voltage        CONSTANT)
    Q_PROPERTY(Fact* BMS_current        READ BMS_current        CONSTANT)
    Q_PROPERTY(Fact* BMS_temperature    READ BMS_temperature    CONSTANT)
    //******************************************************************
    Q_PROPERTY(Fact* voltage            READ voltage            CONSTANT)
    Q_PROPERTY(Fact* percentRemaining   READ percentRemaining   CONSTANT)
    Q_PROPERTY(Fact* mahConsumed        READ mahConsumed        CONSTANT)
    Q_PROPERTY(Fact* current            READ current            CONSTANT)
    Q_PROPERTY(Fact* temperature        READ temperature        CONSTANT)
    Q_PROPERTY(Fact* cellCount          READ cellCount          CONSTANT)
    Q_PROPERTY(Fact* instantPower       READ instantPower       CONSTANT)
    Q_PROPERTY(Fact* timeRemaining      READ timeRemaining      CONSTANT)
    Q_PROPERTY(Fact* chargeState        READ chargeState        CONSTANT)

    //******************************************************************
    Fact* BMS_voltage               (void) { return &_BMS_voltageFact; }
    Fact* BMS_current               (void) { return &_BMS_currentFact; }
    Fact* BMS_temperature           (void) { return &_BMS_temperatureFact; }
    //******************************************************************
    Fact* voltage                   (void) { return &_voltageFact; }
    Fact* percentRemaining          (void) { return &_percentRemainingFact; }
    Fact* mahConsumed               (void) { return &_mahConsumedFact; }
    Fact* current                   (void) { return &_currentFact; }
    Fact* temperature               (void) { return &_temperatureFact; }
    Fact* cellCount                 (void) { return &_cellCountFact; }
    Fact* instantPower              (void) { return &_instantPowerFact; }
    Fact* timeRemaining             (void) { return &_timeRemainingFact; }
    Fact* chargeState               (void) { return &_chargeStateFact; }

    //******************************************************************
    static const char* _BMS_voltageFactName;
    static const char* _BMS_currentFactName;
    static const char* _BMS_temperatureFactName;
    //******************************************************************
    static const char* _voltageFactName;
    static const char* _percentRemainingFactName;
    static const char* _mahConsumedFactName;
    static const char* _currentFactName;
    static const char* _temperatureFactName;
    static const char* _cellCountFactName;
    static const char* _instantPowerFactName;
    static const char* _timeRemainingFactName;
    static const char* _chargeStateFactName;

    static const char* _settingsGroup;

    //******************************************************************
    static const double _BMS_voltageUnavailable;
    static const int    _BMS_currentUnavailable;
    static const int    _BMS_temperatureUnavailable;
    //******************************************************************
    static const double _voltageUnavailable;
    static const int    _percentRemainingUnavailable;
    static const int    _mahConsumedUnavailable;
    static const int    _currentUnavailable;
    static const double _temperatureUnavailable;
    static const int    _cellCountUnavailable;
    static const double _instantPowerUnavailable;

private:
    //******************************************************************
    Fact            _BMS_voltageFact;
    Fact            _BMS_currentFact;
    Fact            _BMS_temperatureFact;
    //******************************************************************
    Fact            _voltageFact;
    Fact            _percentRemainingFact;
    Fact            _mahConsumedFact;
    Fact            _currentFact;
    Fact            _temperatureFact;
    Fact            _cellCountFact;
    Fact            _instantPowerFact;
    Fact            _timeRemainingFact;
    Fact            _chargeStateFact;
};

文件:qgroundcontrol/src/Vehicle/Vehicle.cc

//第786行
    case MAVLINK_MSG_ID_READ_UART_SENSOR: 
        _handleReadUartSensor(message);
        break;
//第1050行
void Vehicle::_handleReadUartSensor(mavlink_message_t& message)
{
    mavlink_read_uart_sensor_t readUartSensor;
    mavlink_msg_read_uart_sensor_decode(&message, &readUartSensor);

    VehicleBatteryFactGroup& batteryFactGroup =  _battery1FactGroup;

    if (readUartSensor.voltage_sensor == -1) {
        batteryFactGroup.BMS_voltage()->setRawValue(VehicleBatteryFactGroup::_BMS_voltageUnavailable);
    } else {
        batteryFactGroup.BMS_voltage()->setRawValue(readUartSensor.voltage_sensor);
    }

    qDebug() << "Receive read_uart_sensor_t" << readUartSensor.voltage_sensor;

}
//第3877行
//******************************************************************
const char* VehicleBatteryFactGroup::_BMS_voltageFactName =                 "BMS_voltage";
const char* VehicleBatteryFactGroup::_BMS_currentFactName =                 "BMS_current";
const char* VehicleBatteryFactGroup::_BMS_temperatureFactName =             "BMS_temperature";
//******************************************************************
const char* VehicleBatteryFactGroup::_voltageFactName =                     "voltage";
const char* VehicleBatteryFactGroup::_percentRemainingFactName =            "percentRemaining";
const char* VehicleBatteryFactGroup::_mahConsumedFactName =                 "mahConsumed";
const char* VehicleBatteryFactGroup::_currentFactName =                     "current";
const char* VehicleBatteryFactGroup::_temperatureFactName =                 "temperature";
const char* VehicleBatteryFactGroup::_cellCountFactName =                   "cellCount";
const char* VehicleBatteryFactGroup::_instantPowerFactName =                "instantPower";
const char* VehicleBatteryFactGroup::_timeRemainingFactName =               "timeRemaining";
const char* VehicleBatteryFactGroup::_chargeStateFactName =                 "chargeState";

const char* VehicleBatteryFactGroup::_settingsGroup =                       "Vehicle.battery";

//******************************************************************
const double VehicleBatteryFactGroup::_BMS_voltageUnavailable =           -1.0;
const int    VehicleBatteryFactGroup::_BMS_currentUnavailable =           -1.0;
const int    VehicleBatteryFactGroup::_BMS_temperatureUnavailable =       -1.0;
//******************************************************************
const double VehicleBatteryFactGroup::_voltageUnavailable =           -1.0;
const int    VehicleBatteryFactGroup::_percentRemainingUnavailable =  -1;
const int    VehicleBatteryFactGroup::_mahConsumedUnavailable =       -1;
const int    VehicleBatteryFactGroup::_currentUnavailable =           -1;
const double VehicleBatteryFactGroup::_temperatureUnavailable =       -1.0;
const int    VehicleBatteryFactGroup::_cellCountUnavailable =         -1.0;
const double VehicleBatteryFactGroup::_instantPowerUnavailable =      -1.0;

VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent)
    : FactGroup(1000, ":/json/Vehicle/BatteryFact.json", parent)
    //******************************************************************
    , _BMS_voltageFact              (0, _BMS_voltageFactName,               FactMetaData::valueTypeDouble)
    , _BMS_currentFact              (0, _BMS_currentFactName,               FactMetaData::valueTypeDouble)
    , _BMS_temperatureFact          (0, _BMS_temperatureFactName,           FactMetaData::valueTypeDouble)
    //******************************************************************
    , _voltageFact                  (0, _voltageFactName,                   FactMetaData::valueTypeDouble)
    , _percentRemainingFact         (0, _percentRemainingFactName,          FactMetaData::valueTypeInt32)
    , _mahConsumedFact              (0, _mahConsumedFactName,               FactMetaData::valueTypeInt32)
    , _currentFact                  (0, _currentFactName,                   FactMetaData::valueTypeFloat)
    , _temperatureFact              (0, _temperatureFactName,               FactMetaData::valueTypeDouble)
    , _cellCountFact                (0, _cellCountFactName,                 FactMetaData::valueTypeInt32)
    , _instantPowerFact             (0, _instantPowerFactName,              FactMetaData::valueTypeFloat)
    , _timeRemainingFact            (0, _timeRemainingFactName,             FactMetaData::valueTypeInt32)
    , _chargeStateFact              (0, _chargeStateFactName,               FactMetaData::valueTypeUint8)
{
    //******************************************************************
    _addFact(&_BMS_voltageFact,             _BMS_voltageFactName);
    _addFact(&_BMS_currentFact,             _BMS_currentFactName);
    _addFact(&_BMS_temperatureFact,         _BMS_temperatureFactName);
    //******************************************************************
    _addFact(&_voltageFact,                 _voltageFactName);
    _addFact(&_percentRemainingFact,        _percentRemainingFactName);
    _addFact(&_mahConsumedFact,             _mahConsumedFactName);
    _addFact(&_currentFact,                 _currentFactName);
    _addFact(&_temperatureFact,             _temperatureFactName);
    _addFact(&_cellCountFact,               _cellCountFactName);
    _addFact(&_instantPowerFact,            _instantPowerFactName);
    _addFact(&_timeRemainingFact,           _timeRemainingFactName);
    _addFact(&_chargeStateFact,             _chargeStateFactName);

    // Start out as not available
    //******************************************************************
    _BMS_voltageFact.setRawValue        (_BMS_voltageUnavailable);
    _BMS_currentFact.setRawValue        (_BMS_currentUnavailable);
    _BMS_temperatureFact.setRawValue    (_BMS_temperatureUnavailable);
    //******************************************************************
    _voltageFact.setRawValue            (_voltageUnavailable);
    _percentRemainingFact.setRawValue   (_percentRemainingUnavailable);
    _mahConsumedFact.setRawValue        (_mahConsumedUnavailable);
    _currentFact.setRawValue            (_currentUnavailable);
    _temperatureFact.setRawValue        (_temperatureUnavailable);
    _cellCountFact.setRawValue          (_cellCountUnavailable);
    _instantPowerFact.setRawValue       (_instantPowerUnavailable);
}

演示效果:

 

 

以上是关于UART串口->PX4飞控->QGC显示的主要内容,如果未能解决你的问题,请参考以下文章

PX4APM无人机仿真连接QGC地面站记录

PX4APM无人机仿真连接QGC地面站记录

PX4APM无人机仿真连接QGC地面站记录(udp连接更改home点等)

PX4APM无人机仿真连接QGC地面站记录(udp连接更改home点等)

QGC 地面站中获取电压完整教程(QGC中无法获取APM电压)

QGC 地面站中获取电压完整教程(QGC中无法获取APM电压)