打造自己的HelloDrone 无人机APP过程《2》

本文详细介绍了如何在无人机APP中创建并显示姿态信息界面,以及如何更新和展示从无人机获取的横滚、俯仰和偏航角度。通过解析 MAVLink 消息并处理 `msg_attitude`,将数据传递给 `Attitude` 类,最终在UI界面上更新这些值。此外,还讨论了 `getAttribute()` 方法在获取实时姿态数据对象中的作用,它是数据从 MAVLink 解析到 UI 更新的关键步骤。
摘要由CSDN通过智能技术生成

目录

摘要

本节主要记录打造自己的HelloDrone 无人机APP过程《2》—如何获取并且显示姿态信息。

1.增加姿态信息界面

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="hello.MainActivity">

        <androidx.cardview.widget.CardView
            android:id="@+id/connection_type_card"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp">

            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp">

                <Button
                    android:id="@+id/btnConnect"
                    android:layout_width="150dp"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/connectionTypeLabel"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentRight="true"
                    android:onClick="onBtnConnectTap"
                    android:text="Connect" />

                <Spinner
                    android:id="@+id/selectConnectionType"
                    android:layout_width="120dp"
                    android:layout_height="44dp"
                    android:layout_below="@+id/connectionTypeLabel"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_toLeftOf="@+id/btnConnect"
                    android:entries="@array/drone_connection_types"
                    android:spinnerMode="dropdown" />

                <TextView
                    android:id="@+id/connectionTypeLabel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentTop="true"
                    android:layout_centerHorizontal="true"
                    android:layout_marginBottom="10dp"
                    android:text="Connection Type"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

            </RelativeLayout>

        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp">

            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp">

                <TextView
                    android:id="@+id/telemetryLabel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="10dp"
                    android:text="Vehicle Telemetry"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TableLayout
                    android:id="@+id/telemetryInfo"
                    android:layout_width="fill_parent"
                    android:layout_height="200dp"
                    android:layout_below="@+id/telemetryLabel"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_marginTop="10dp">

                    <TableRow
                        android:id="@+id/vehTelemRow1"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/vehicleModeLabelTextView"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="Mode:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <Spinner
                            android:id="@+id/modeSelect"
                            android:layout_width="fill_parent"
                            android:layout_height="44dp"
                            android:layout_column="1"
                            android:layout_below="@+id/connectionTypeLabel"
                            android:layout_alignParentStart="true"
                            android:layout_alignParentLeft="true"
                            android:layout_toLeftOf="@+id/btnConnect"
                            android:spinnerMode="dropdown" />
                    </TableRow>

                    <TableRow
                        android:id="@+id/vehTelemRow2"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/altitudeLabelTextView"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="Altitude:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/altitudeValueTextView"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0m"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>

                    <TableRow
                        android:id="@+id/vehTelemRow3"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/speedLabelTextView"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="Speed:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/speedValueTextView"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0m/s"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>

                    <TableRow
                        android:id="@+id/vehTelemRow4"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/distanceLabelTextView"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="Distance:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/distanceValueTextView"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0m"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>

                    <TableRow
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <Button
                            android:id="@+id/btnArmTakeOff"
                            android:layout_width="120dp"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_below="@+id/connectionTypeLabel"
                            android:layout_alignParentEnd="true"
                            android:layout_alignParentRight="true"
                            android:onClick="onArmButtonTap"
                            android:visibility="invisible" />
                    </TableRow>
                </TableLayout>

                <TableLayout
                    android:id="@+id/ahrs"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/telemetryInfo"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true">

                    <TableRow
                        android:layout_width="match_parent"
                        android:layout_height="match_parent">

                        <TextView
                            android:id="@+id/roll"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="横滚角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/roll_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>

                    <TableRow
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/pitch"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="俯仰角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/pitch_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>
                    <TableRow
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/yaw"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="偏航角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/yaw_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>
                </TableLayout>

            </RelativeLayout>

        </androidx.cardview.widget.CardView>


    </LinearLayout>
</ScrollView>

姿态UI布局代码,主要如下所示,采用表格布局

                <TableLayout
                    android:id="@+id/ahrs"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/telemetryInfo"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true">

                    <TableRow
                        android:layout_width="match_parent"
                        android:layout_height="match_parent">

                        <TextView
                            android:id="@+id/roll"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="横滚角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/roll_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>

                    <TableRow
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/pitch"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="俯仰角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/pitch_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>
                    <TableRow
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent">

                        <TextView
                            android:id="@+id/yaw"
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:layout_column="0"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="偏航角:"
                            android:textAppearance="?android:attr/textAppearanceMedium" />

                        <TextView
                            android:id="@+id/yaw_value"
                            android:layout_width="fill_parent"
                            android:layout_height="wrap_content"
                            android:layout_column="1"
                            android:layout_gravity="left"
                            android:paddingTop="5dp"
                            android:paddingBottom="5dp"
                            android:text="0度"
                            android:textAppearance="?android:attr/textAppearanceMedium" />
                    </TableRow>
                </TableLayout>

2.在activity中显示更新姿态数据

首先在主activity中重写void onDroneEvent(String event, Bundle extras);方法
在这里插入图片描述

    @Override
    public void onDroneEvent(String event, Bundle extras)
    {
        switch (event) {
            case AttributeEvent.STATE_CONNECTED:
                alertUser("Drone Connected");
                updateConnectedButton(this.drone.isConnected());
                //更新解锁连接按钮
                updateArmButton();
                break;

            case AttributeEvent.STATE_DISCONNECTED:
                alertUser("Drone Disconnected");
                updateConnectedButton(this.drone.isConnected());
                //更新解锁断开按钮
                updateArmButton();
                break;

            case AttributeEvent.STATE_UPDATED:
            case AttributeEvent.STATE_ARMING:
                //更新解锁按钮
                updateArmButton();
                break;

            case AttributeEvent.TYPE_UPDATED:
                Type newDroneType = this.drone.getAttribute(AttributeType.TYPE);
                if (newDroneType.getDroneType() != this.droneType) {
                    this.droneType = newDroneType.getDroneType();
                    updateVehicleModesForType(this.droneType);
                }
                break;

            case AttributeEvent.STATE_VEHICLE_MODE:
                //更新模式
                updateVehicleMode();
                break;

            case AttributeEvent.SPEED_UPDATED:
                //更新姿态
                updateAttitude();
                break;
            case AttributeEvent.ATTITUDE_UPDATED:
                //更新速度
                updateSpeed();
                break;
            case AttributeEvent.ALTITUDE_UPDATED:
                //更新高度
                updateAltitude();
                break;

            case AttributeEvent.HOME_UPDATED:
                //更新到home点距离
                updateDistanceFromHome();
                break;

            default:
                // Log.i("DRONE_EVENT", event); //Uncomment to see events from the drone
                break;
        }
    }

在这里插入图片描述
这里我们看下更新姿态是如何实现的

    /**
     * 更新姿态信息
     */
    protected void updateAttitude() {
        //找到对应要显示的横滚位置的ID
        TextView rollValue = (TextView) findViewById(R.id.roll_value);
        //找到对应要显示的俯仰位置的ID
        TextView pitchValue = (TextView) findViewById(R.id.pitch_value);
        //找到对应要显示的偏航位置的ID
        TextView yawValue = (TextView) findViewById(R.id.yaw_value);
        //关键代码,获取姿态信息实例
        Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);
        //获取信息后,修改需要显示的位置
        //显示横滚角的值
        rollValue.setText(String.format("%3.1f", attitude.getRoll()) + "度");
       //显示横滚俯仰角的值
        pitchValue.setText(String.format("%3.1f", attitude.getPitch()) + "度");
       //显示偏航角的值
        yawValue.setText(String.format("%3.1f", attitude.getYaw()) + "度");
    }

因此这里比较重要的函数是:

Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);


    /**
     *获取属性
     * @param type
     * @param <T>
     * @return
     */
    public <T extends Parcelable> T getAttribute(String type)
    {
        final IDroneApi droneApi = droneApiRef.get();
        if (!isStarted(droneApi) || type == null)
        {
            return this.getAttributeDefaultValue(type);
        }

        T attribute = null;
        Bundle carrier = null;
        try 
        {
            carrier = droneApi.getAttribute(type);
        } catch (RemoteException e) 
        {
            handleRemoteException(e);
        }

        if (carrier != null) 
        {
            try 
            {
                carrier.setClassLoader(contextClassLoader);
                attribute = carrier.getParcelable(type);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }
        }

        return attribute == null ? this.<T>getAttributeDefaultValue(type) : attribute;
    }

从这里直接寻找怎么找到如何拿取姿态数据不太好找,我们可以从协议直接倒着找。我们先看下app接收到飞控的数据进行的处理

    @Override
    public void onMavLinkMessageReceived(MAVLinkMessage message) {
        Log.i("lxw","ardupilot message.sysid :"+message.sysid );

        if ((message.sysid != this.getSysid()) && !isMavLinkMessageException(message)) {
            // Reject Messages that are not for the system id
            return;
        }

        // Filter Components IDs to be specifically the IDs that can be processed
        int compId = message.compid;
        if (compId != AUTOPILOT_COMPONENT_ID
                && compId != ARTOO_COMPONENT_ID
                && compId != SiK_RADIO_FIXED_COMPID ){
            return;
        }

        if (!getParameterManager().processMessage(message)) {

            getWaypointManager().processMessage(message);
            getCalibrationSetup().processMessage(message);

            switch (message.msgid) {
                //msg-id=253  文本信息
                case msg_statustext.MAVLINK_MSG_ID_STATUSTEXT:
                    // These are any warnings sent from APM:Copter with
                    // gcs_send_text_P()
                    // This includes important thing like arm fails, prearm fails, low
                    // battery, etc.
                    // also less important things like "erasing logs" and
                    // "calibrating barometer"
                    msg_statustext msg_statustext = (msg_statustext) message;
                    processStatusText(msg_statustext);
                    break;
                //msg-id=74 固定翼飞机平视显示器上通常显示的指标
                case msg_vfr_hud.MAVLINK_MSG_ID_VFR_HUD:
                    processVfrHud((msg_vfr_hud) message);
                    break;
                //msg-id=27 原始IMU信息
                case msg_raw_imu.MAVLINK_MSG_ID_RAW_IMU:
                    msg_raw_imu msg_imu = (msg_raw_imu) message;
                    mag.newData(msg_imu);
                    break;
                //msg-id=166 遥控状态信息
                case msg_radio.MAVLINK_MSG_ID_RADIO:
                    msg_radio m_radio = (msg_radio) message;
                    processSignalUpdate(m_radio.rxerrors, m_radio.fixed, m_radio.rssi,
                            m_radio.remrssi, m_radio.txbuf, m_radio.noise, m_radio.remnoise);
                    break;
                //msg-id=35 遥控通道信息
                case msg_rc_channels_raw.MAVLINK_MSG_ID_RC_CHANNELS_RAW:
                    rc.setRcInputValues((msg_rc_channels_raw) message);
                    break;
                //msg-id=36 伺服通道信息
                case msg_servo_output_raw.MAVLINK_MSG_ID_SERVO_OUTPUT_RAW:
                    rc.setRcOutputValues((msg_servo_output_raw) message);
                    break;
                //msg-id=36 相机信息
                case msg_camera_feedback.MAVLINK_MSG_ID_CAMERA_FEEDBACK:
                    getCamera().newImageLocation((msg_camera_feedback) message);
                    break;
                //msg-id=158 挂载信息
                case msg_mount_status.MAVLINK_MSG_ID_MOUNT_STATUS:
                    processMountStatus((msg_mount_status) message);
                    break;
                //msg-id=252 名字值信息
                case msg_named_value_int.MAVLINK_MSG_ID_NAMED_VALUE_INT:
                    processNamedValueInt((msg_named_value_int) message);
                    break;

                //***************罗盘校准信息处理 Magnetometer calibration messages handling *************//
                //msg-id=191 罗盘校准信息
                //msg-id=192 罗盘校准取消信息
                case msg_mag_cal_progress.MAVLINK_MSG_ID_MAG_CAL_PROGRESS:
                case msg_mag_cal_report.MAVLINK_MSG_ID_MAG_CAL_REPORT:
                    getMagnetometerCalibration().processCalibrationMessage(message);
                    break;

                default:
                    break;
            }
        }
        Log.i("lxw","super message.sysid :"+message.sysid );
        //这里开始调用父类的实现,因此我们需要继续看相关msg->id的处理
        super.onMavLinkMessageReceived(message);
    }

    /**
     * 处理mavlink接收信息
     * @param message
     */
    @Override
    public void onMavLinkMessageReceived(MAVLinkMessage message) {
        
        Log.i("lxw","message.sysid :"+message.sysid );
        if ( (message.sysid != this.getSysid()) && !isMavLinkMessageException(message) )
        {
            // Reject messages that are not for this drone's system id
            return;
        }
        //处理心跳信息
        onHeartbeat(message);

        switch (message.msgid) {
            //msg-id=109 遥控器状态信息
            case msg_radio_status.MAVLINK_MSG_ID_RADIO_STATUS:
                msg_radio_status m_radio_status = (msg_radio_status) message;
                processSignalUpdate(m_radio_status.rxerrors, m_radio_status.fixed, m_radio_status.rssi,
                        m_radio_status.remrssi, m_radio_status.txbuf, m_radio_status.noise, m_radio_status.remnoise);
                break;
            //msg-id=30 姿态信息
            case msg_attitude.MAVLINK_MSG_ID_ATTITUDE:
                msg_attitude m_att = (msg_attitude) message;
                processAttitude(m_att);
                break;
            //msg-id=0 心跳信息
            case msg_heartbeat.MAVLINK_MSG_ID_HEARTBEAT:
                msg_heartbeat msg_heart = (msg_heartbeat) message;
                processHeartbeat(msg_heart);
                break;
            //msg-id=241 振动级和加速度计夹持
            case msg_vibration.MAVLINK_MSG_ID_VIBRATION:
                msg_vibration vibrationMsg = (msg_vibration) message;
                processVibrationMessage(vibrationMsg);
                break;

            //*************** EKF State handling ******************//
            msg-id=193 EKF状态处理
            case msg_ekf_status_report.MAVLINK_MSG_ID_EKF_STATUS_REPORT:
                processEfkStatus((msg_ekf_status_report) message);
                break;
            //msg-id=1 系统状态
            case msg_sys_status.MAVLINK_MSG_ID_SYS_STATUS:
                msg_sys_status m_sys = (msg_sys_status) message;
                processSysStatus(m_sys);
                break;
            //msg-id=33 全局位置信息
            case msg_global_position_int.MAVLINK_MSG_ID_GLOBAL_POSITION_INT:
                processGlobalPositionInt((msg_global_position_int) message);
                break;
            //msg-id=24 系统状态原始GPS信息
            case msg_gps_raw_int.MAVLINK_MSG_ID_GPS_RAW_INT:
                processGpsState((msg_gps_raw_int) message);
                break;
            //msg-id=39 任务相关信息
            case msg_mission_item.MAVLINK_MSG_ID_MISSION_ITEM:
                processHomeUpdate((msg_mission_item) message);
                break;
            //msg-id=42 当前任务信息
            case msg_mission_current.MAVLINK_MSG_ID_MISSION_CURRENT:
                missionStats.setWpno(((msg_mission_current) message).seq);
                break;
            //msg-id=46 任务到达信息
            case msg_mission_item_reached.MAVLINK_MSG_ID_MISSION_ITEM_REACHED:
                missionStats.setLastReachedWaypointNumber(((msg_mission_item_reached) message).seq);
                break;
            //msg-id=62 控制输出信息
            case msg_nav_controller_output.MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT:
                msg_nav_controller_output m_nav = (msg_nav_controller_output) message;
                setDisttowpAndSpeedAltErrors(m_nav.wp_dist, m_nav.alt_error, m_nav.aspd_error);
                break;
        }
    }

从这里我们可以看到姿态数据的处理
在这里插入图片描述
核心在于:processAttitude(m_att);但是注意的还是先把message转换成姿态相关的协议信息

    /**
     * 处理姿态信息
     * @param m_att
     */
    private void processAttitude(msg_attitude m_att) {
        //设置横滚角
        attitude.setRoll(Math.toDegrees(m_att.roll));
        //设置横滚角速度
        attitude.setRollSpeed((float) Math.toDegrees(m_att.rollspeed));
        //设置俯仰角
        attitude.setPitch(Math.toDegrees(m_att.pitch));
        //设置俯仰角速度
        attitude.setPitchSpeed((float) Math.toDegrees(m_att.pitchspeed));
        //设置偏航角
        attitude.setYaw(Math.toDegrees(m_att.yaw));
        //设置偏航角速度
        attitude.setYawSpeed((float) Math.toDegrees(m_att.yawspeed));
        //通知无人机事件
        notifyDroneEvent(DroneInterfaces.DroneEventsType.ATTITUDE);
    }

加粗样式我们看下姿态信息的协议定义:

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * java mavlink generator tool. It should not be modified by hand.
 */

// MESSAGE ATTITUDE PACKING
package com.MAVLink.common;
import com.MAVLink.MAVLinkPacket;
import com.MAVLink.Messages.MAVLinkMessage;
import com.MAVLink.Messages.MAVLinkPayload;
        
/**
* 这个姿态是在航空坐标系下(右手坐标系:Z-向下,X-前,Y-右)
* The attitude in the aeronautical frame (right-handed, Z-down, X-front, Y-right).
*/
public class msg_attitude extends MAVLinkMessage{

    //姿态协议msg-id=30
    public static final int MAVLINK_MSG_ID_ATTITUDE = 30;
    //姿态协议长度
    public static final int MAVLINK_MSG_LENGTH = 28;
    //串口版本UID
    private static final long serialVersionUID = MAVLINK_MSG_ID_ATTITUDE;


      
    /**
    * 时间戳(系统启动后的毫秒数)
    */
    public long time_boot_ms;
      
    /**
    * 横滚角 (rad, -pi..+pi)
    */
    public float roll;
      
    /**
    * 俯仰角 (rad, -pi..+pi)
    */
    public float pitch;
      
    /**
    * 偏航角 (rad, -pi..+pi)
    */
    public float yaw;
      
    /**
    * 横滚角速度 (rad/s)
    */
    public float rollspeed;
      
    /**
    * 俯仰角速度 (rad/s)
    */
    public float pitchspeed;
      
    /**
    *偏航角速度 (rad/s)
    */
    public float yawspeed;
    

    /**
    * 为这种类型的消息生成mavlink消息的有效负载,组包数据
    * @return
    */
    public MAVLinkPacket pack(){
        MAVLinkPacket packet = new MAVLinkPacket(MAVLINK_MSG_LENGTH);
        packet.sysid = 255;
        packet.compid = 190;
        packet.msgid = MAVLINK_MSG_ID_ATTITUDE;
              
        packet.payload.putUnsignedInt(time_boot_ms);
              
        packet.payload.putFloat(roll);
              
        packet.payload.putFloat(pitch);
              
        packet.payload.putFloat(yaw);
              
        packet.payload.putFloat(rollspeed);
              
        packet.payload.putFloat(pitchspeed);
              
        packet.payload.putFloat(yawspeed);
        
        return packet;
    }

    /**
    * Decode a attitude message into this class fields
    * 将姿态信息解码到该类字段中
    * @param payload The message to decode
    */
    public void unpack(MAVLinkPayload payload) {
        payload.resetIndex();
              
        this.time_boot_ms = payload.getUnsignedInt();
        //横滚角
        this.roll = payload.getFloat();
        //俯仰角
        this.pitch = payload.getFloat();
        //偏航角
        this.yaw = payload.getFloat();
        //横滚角速度
        this.rollspeed = payload.getFloat();
        //俯仰角速度
        this.pitchspeed = payload.getFloat();
        //偏航角速度
        this.yawspeed = payload.getFloat();
        
    }

    /**
    * 构造函数,只需初始化msgid
    * Constructor for a new message, just initializes the msgid
    */
    public msg_attitude(){
        msgid = MAVLINK_MSG_ID_ATTITUDE;
    }

    /**
    * Constructor for a new message, initializes the message with the payload
    * from a mavlink packet
    * 构造函数,用有效负载初始化消息,从mavlink数据包
    */
    public msg_attitude(MAVLinkPacket mavLinkPacket){
        this.sysid = mavLinkPacket.sysid;
        this.compid = mavLinkPacket.compid;
        this.msgid = MAVLINK_MSG_ID_ATTITUDE;
        unpack(mavLinkPacket.payload);        
    }

                  
    /**
    * 返回包含消息名称和数据的字符串
    * Returns a string with the MSG name and data
    */
    public String toString(){
        return "MAVLINK_MSG_ID_ATTITUDE - " +
                "sysid:"+sysid+
                " compid:"+compid+
                " time_boot_ms:"+time_boot_ms+
                " roll:"+roll+
                " pitch:"+pitch+
                " yaw:"+yaw+
                " rollspeed:"+rollspeed+
                " pitchspeed:"+pitchspeed+
                " yawspeed:"+yawspeed+"";
    }
}
        

我们现在在回去看下刚刚看到的姿态更新函数:

    /**
     * 更新姿态信息
     */
    protected void updateAttitude() {
        //找到对应要显示的横滚位置的ID
        TextView rollValue = (TextView) findViewById(R.id.roll_value);
        //找到对应要显示的俯仰位置的ID
        TextView pitchValue = (TextView) findViewById(R.id.pitch_value);
        //找到对应要显示的偏航位置的ID
        TextView yawValue = (TextView) findViewById(R.id.yaw_value);
        //关键代码,获取姿态信息实例
        Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);
        //获取信息后,修改需要显示的位置
        //显示横滚角的值
        rollValue.setText(String.format("%3.1f", attitude.getRoll()) + "度");
       //显示横滚俯仰角的值
        pitchValue.setText(String.format("%3.1f", attitude.getPitch()) + "度");
       //显示偏航角的值
        yawValue.setText(String.format("%3.1f", attitude.getYaw()) + "度");
    }

其中:attitude.getRoll()获取姿态横滚角数据;attitude.getPitch()获取姿态俯仰角数据;获取姿态偏航角数据。
其中attitude,是Attitude attitude的对象,我们去看下Attitude类

package com.o3dr.services.android.lib.drone.property;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by fhuya on 10/28/14.
 */
public class Attitude implements DroneAttribute {

    /**
     * 横滚角 (deg, -180..+180)
     */
    private double roll;

    /**
     * 横滚角速度 (deg/s)
     */
    private float rollSpeed;

    /**
     * 俯仰角 (deg, -180 to 180)
     */
    private  double pitch;

    /**
     * 俯仰角速度 (deg / s)
     */
    private float pitchSpeed;

    /**
     * 偏航(deg, -180 to 180)
     */
    private  double yaw;

    /**
     * 偏航角速度 (deg/ s)
     */
    private float yawSpeed;

    public Attitude(){}
    //构造函数
    public Attitude(double roll, double pitch, double yaw, float rollSpeed, float pitchSpeed, float yawSpeed) 
    {
        this.roll = roll;
        this.pitch = pitch;
        this.yaw = yaw;
        this.rollSpeed = rollSpeed;
        this.pitchSpeed = pitchSpeed;
        this.yawSpeed = yawSpeed;
    }

    /**
     * 更新横滚角---
     * Updates the roll angle
     * @param roll Roll angle (deg, -180..+180)
     */
    public void setRoll(double roll) {
        this.roll = roll;
    }

    /**
     * 更新俯仰角Updates the pitch angle
     * @param pitch Pitch angle (deg, -180..+180)
     */
    public void setPitch(double pitch) {
        this.pitch = pitch;
    }

    /**
     *更新偏航角 Updates the yaw angle
     * @param yaw Yaw angle (deg, -180..+180)
     */
    public void setYaw(double yaw) {
        this.yaw = yaw;
    }

    /**
     * 获取横滚角
     * @return Vehicle roll angle (deg, -180..+180)
     */
    public double getRoll() {
        return roll;
    }

    /**
     * 获取俯仰角
     * @return Vehicle pitch angle (deg, -180 to 180)
     */
    public double getPitch() {
        return pitch;
    }

    /**
     * 获取偏航角
     * @return Vehicle yaw angle (deg, -180 to 180)
     */
    public double getYaw() {
        return yaw;
    }

    /**
     * 获取俯仰角速度
     * @return Vehicle pitch angular speed (deg / s)
     */
    public float getPitchSpeed() {
        return pitchSpeed;
    }

    /**
     * 更新俯仰角速度
     * Updates the pitch angular speed
     * @param pitchSpeed Pitch angular speed (deg/s)
     */
    public void setPitchSpeed(float pitchSpeed) {
        this.pitchSpeed = pitchSpeed;
    }

    /**
     * 获取横滚角速度
     * @return Vehicle roll angular speed (deg/s)
     */
    public float getRollSpeed() {
        return rollSpeed;
    }

    /**
     * 设置横滚角速度
     * Updates the roll angular speed
     * @param rollSpeed Roll angular speed (deg/s)
     */
    public void setRollSpeed(float rollSpeed) {
        this.rollSpeed = rollSpeed;
    }

    /**
     * 获取设备偏航角速度
     * @return Vehicle yaw angular speed (deg/ s)
     */
    public float getYawSpeed() {
        return yawSpeed;
    }

    /**
     * 更新偏航角速度
     * Updates the yaw angular speed
     * @param yawSpeed Yaw angular speed (deg/s)
     */
    public void setYawSpeed(float yawSpeed) {
        this.yawSpeed = yawSpeed;
    }

    @Override
    public String toString() {
        return "Attitude{" +
                "pitch=" + pitch +
                ", roll=" + roll +
                ", rollSpeed=" + rollSpeed +
                ", pitchSpeed=" + pitchSpeed +
                ", yaw=" + yaw +
                ", yawSpeed=" + yawSpeed +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Attitude)) return false;

        Attitude attitude = (Attitude) o;

        if (Double.compare(attitude.roll, roll) != 0) return false;
        if (Float.compare(attitude.rollSpeed, rollSpeed) != 0) return false;
        if (Double.compare(attitude.pitch, pitch) != 0) return false;
        if (Float.compare(attitude.pitchSpeed, pitchSpeed) != 0) return false;
        if (Double.compare(attitude.yaw, yaw) != 0) return false;
        return Float.compare(attitude.yawSpeed, yawSpeed) == 0;

    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(roll);
        result = (int) (temp ^ (temp >>> 32));
        result = 31 * result + (rollSpeed != +0.0f ? Float.floatToIntBits(rollSpeed) : 0);
        temp = Double.doubleToLongBits(pitch);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (pitchSpeed != +0.0f ? Float.floatToIntBits(pitchSpeed) : 0);
        temp = Double.doubleToLongBits(yaw);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (yawSpeed != +0.0f ? Float.floatToIntBits(yawSpeed) : 0);
        return result;
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeDouble(this.roll);
        dest.writeDouble(this.pitch);
        dest.writeDouble(this.yaw);
        dest.writeFloat(this.rollSpeed);
        dest.writeFloat(this.pitchSpeed);
        dest.writeFloat(this.yawSpeed);
    }

    private Attitude(Parcel in) {
        this.roll = in.readDouble();
        this.pitch = in.readDouble();
        this.yaw = in.readDouble();
        this.rollSpeed = in.readFloat();
        this.pitchSpeed = in.readFloat();
        this.yawSpeed = in.readFloat();
    }

    public static final Parcelable.Creator<Attitude> CREATOR = new Parcelable.Creator<Attitude>() {
        public Attitude createFromParcel(Parcel source) {
            return new Attitude(source);
        }

        public Attitude[] newArray(int size) {
            return new Attitude[size];
        }
    };
}

从Attitude类的实现可以看出我们获取的姿态角相关信息,是从
在这里插入图片描述
传递进去,因此要想建立mavlink解析到的数据和UI获取数据建立联系,这个set函数是关键。我们进行全局查找,只有找到了下面的使用
在这里插入图片描述
可以看出只有mavlink协议解析出进行了这个设置,这个跟我们想象的是一致的,就是通过set函数实现。

在这里插入图片描述
其中:对象attitude是 protected final Attitude attitude = new Attitude();的对象,而Attitude正好是我们刚刚在UI中使用的类。所以他们之间建立了联系。只需要进行mavlink姿态数据的解析,然后进行更新这个值,UI界面通过get函数就可以获取到数据
在这里插入图片描述
综上可以得到,Attitude类是建立UI和后端数据进行解析的桥梁。


3.根源问题

到这里我们还有一个问题没有解决,就是之前说的那个疑问?只要解决这个疑问一切就打通

    /**
     * 更新姿态信息
     */
    protected void updateAttitude() {
        //找到对应要显示的横滚位置的ID
        TextView rollValue = (TextView) findViewById(R.id.roll_value);
        //找到对应要显示的俯仰位置的ID
        TextView pitchValue = (TextView) findViewById(R.id.pitch_value);
        //找到对应要显示的偏航位置的ID
        TextView yawValue = (TextView) findViewById(R.id.yaw_value);
        //关键代码,获取姿态信息实例
        Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);
        //获取信息后,修改需要显示的位置
        //显示横滚角的值
        rollValue.setText(String.format("%3.1f", attitude.getRoll()) + "度");
       //显示横滚俯仰角的值
        pitchValue.setText(String.format("%3.1f", attitude.getPitch()) + "度");
       //显示偏航角的值
        yawValue.setText(String.format("%3.1f", attitude.getYaw()) + "度");
    }

结合前面的讲述,目前主要是获取实时更新姿态的对象实例,也就是这个代码
Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);。为什么这样说呢?因为从第二部分我们可以看出数据之间的关联是通过Attitude attitude的对象建立了联系,而Attitude attitude = this.drone.getAttribute(AttributeType.ATTITUDE);正好是获取一个对象给attitude,只要说明:



!!!
this.drone.getAttribute(AttributeType.ATTITUDE)返回的是一个可以实时获取姿态更新的数据对象就可以。
!!!



其中: private Drone drone;
在这里插入图片描述
在这里插入图片描述
我们可以看出PACKAGE_NAME是被定义成:

private static final String PACKAGE_NAME = “com.o3dr.services.android.lib.attribute”;

而: public static final String ALTITUDE = PACKAGE_NAME + “.ALTITUDE”;
则表示的是com.o3dr.services.android.lib.attribute.ATTITUDE
在这里插入图片描述
现在关键还是那个public T getAttribute(String type)方法,
在这里插入图片描述

 /**
     * 获取默认值
     * @param attributeType
     * @param <T>
     * @return
     */
    private <T extends Parcelable> T getAttributeDefaultValue(String attributeType) 
    {
        if (attributeType == null) {
            return null;
        }

        switch (attributeType) {
            case AttributeType.ALTITUDE:
                return (T) new Altitude();

            case AttributeType.GPS:
                return (T) new Gps();

            case AttributeType.STATE:
                return (T) new State();

            case AttributeType.PARAMETERS:
                return (T) new Parameters();

            case AttributeType.SPEED:
                return (T) new Speed();

            case AttributeType.ATTITUDE:
                //可以看出这里new了一个对象,而Attitude正好是我们想要得到的值
                return (T) new Attitude();

            case AttributeType.HOME:
                return (T) new Home();

            case AttributeType.BATTERY:
                return (T) new Battery();

            case AttributeType.MISSION:
                return (T) new Mission();

            case AttributeType.SIGNAL:
                return (T) new Signal();

            case AttributeType.GUIDED_STATE:
                return (T) new GuidedState();

            case AttributeType.TYPE:
                return (T) new Type();

            case AttributeType.FOLLOW_STATE:
                return (T) new FollowState();

            case AttributeType.MAGNETOMETER_CALIBRATION_STATUS:
                return (T) new MagnetometerCalibrationStatus();

            case AttributeType.RETURN_TO_ME_STATE:
                return (T) new ReturnToMeState();

            case AttributeType.CAMERA:
            case SoloAttributes.SOLO_STATE:
            case SoloAttributes.SOLO_GOPRO_STATE:
            case SoloAttributes.SOLO_GOPRO_STATE_V2:
            default:
                return null;
        }
    }

因此按照这种简单的理解思路是对的,也就解释了我们的疑惑,对于attribute如果不等于空,那么将会有
在这里插入图片描述
为了验证具体执行哪里我们增加log进行监视。

在这里插入图片描述


    /**
     *获取属性
     * @param type
     * @param <T>
     * @return
     */
    public <T extends Parcelable> T getAttribute(String type)
    {
        final IDroneApi droneApi = droneApiRef.get();
        Log.i("zqj"," droneApiRef:"+droneApiRef.get());
        if (!isStarted(droneApi) || type == null)
        {
            Log.i("zqj"," droneApiRef null:");
            return this.getAttributeDefaultValue(type);
        }

        T attribute = null;
        Bundle carrier = null;
        try
        {
            carrier = droneApi.getAttribute(type);
            Log.i("zqj"," carrier:"+carrier);
        } catch (RemoteException e)
        {
            handleRemoteException(e);
            Log.i("zqj"," carrier1:"+carrier);
        }

        if (carrier != null)
        {
            try
            {
                carrier.setClassLoader(contextClassLoader);
                attribute = carrier.getParcelable(type);
                Log.i("zqj"," attribute:"+attribute);
            } catch (Exception e)
            {
                Log.i("zqj"," Exception:");
                Log.e(TAG, e.getMessage(), e);
            }
        }
        Log.i("zqj"," attribute2:"+attribute);
        return attribute == null ? this.<T>getAttributeDefaultValue(type) : attribute;
    }

在这里插入图片描述
从日志可以看出数据是从: attribute = carrier.getParcelable(type);获取。而Bundle carrier = null;,所以

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔城烟雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值