drone无人机应用_drone无人机教程视频

drone无人机应用_drone无人机教程视频目录文章目录目录摘要1.增加姿态信息界面2.在activity中显示更新姿态数据摘要本节主要记录打造自己的HelloDrone无人机APP过程《2》—如何获取并且显示姿态信息。1.增加姿态信息界面<?xmlversion=”1.0″encoding=”utf-8″?><ScrollViewxmlns:android=”http://schemas.android.com/apk/res/android”xmlns:tools=”http://schemas.

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

目录

摘要

本节主要记录打造自己的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;,所以

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170565.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 搜索引擎使用技巧

    1、双引号把搜索词放在双引号中,代表完全匹配搜索,也就是说搜索结果返回的页面包含双引号中出现的所有的词,连顺序也必须完全匹配。百度和Google都支持这个指令。例如搜索:“Python”。2、减号减号代表搜索不包含减号后面的词的页面。使用这个指令时减号前面必须是空格,减号后面没有空格,紧跟着需要排除的词。Google和bd都支持这个指令。例如:搜索-引擎返回的则是包含“搜索”这…

  • export添加环境变量不生效_如何添加环境变量里面的路径

    export添加环境变量不生效_如何添加环境变量里面的路径export命令功能说明:设置或显示环境变量。语  法:export[-fnp][变量名称]=[变量设置值]补充说明:在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该此登陆操作。参  数:   -f 代表[变量名称]中为函数名称。 -n 删除指定的变量。变量实际上…

  • 开启Redis

    开启Redis

  • Android 程序员等级要求

    Android 程序员等级要求很多Android开发者已经度过了初级、中级,如何成为一个Android高手呢?Android123就各个级别的程序员应该掌握哪些内容作为下面分类。  一、初级  1.拥有娴熟的Java基础,理解设计模式,比如OOP语言的工厂模式要懂得。   2.掌握AndroidUI控件、AndroidJava层API相关使用。   迈向中级,最好再次更新下Ja

  • landsat 8 卫星 波段介绍 及组合

    landsat 8 卫星 波段介绍 及组合    Landsat8卫星包含OLI(OperationalLandImager陆地成像仪)和TIRS(ThermalInfraredSensor热红外传感器)两种传感器。OLI包括了

  • js算法初窥01(排序算法01-冒泡、选择、插入)

    排序,我想大家一定经历过或者正在经历着。或许你不懂算法,对排序算法一无所知,但是你一定用过一些第三方库的api来一键排序,那么,在你享受便捷的同时,你是否想过它的底层是如何实现的?这样的算法实现方式是

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号