基于无人机的目标检测平台——数据中转(安卓App)

一、简述

本文将设计实现一个基于无人机的目标检测平台,以下为平台的整体结构图,本文为第一个模块,实现无人机信息通过传输到服务器的功能。
在这里插入图片描述

二、准备工作

数据获取以及传输功能基于大疆官方提供的Mobile_SDK_Android_Demo进行实现,如何部署使用请参考上述链接内官方教程。

三、无人机控制模块

  • 开启控制线程并添加到线程池
Runnable runnable=()-> {
    try {
        Socket c_s_socket = new Socket(IP_DEST, 10000);
        PrintWriter out = new PrintWriter(c_s_socket.getOutputStream()); //向客户端发送数据
        out.println("Drone is ready!");
        out.flush();//将缓冲流中的数据全部输出

        //读取服务器返回的消息
        BufferedReader br = new BufferedReader(new InputStreamReader(c_s_socket.getInputStream()));
        String temp_msg = br.readLine();
        if(temp_msg.equals("start")){
            mBaseMovement = new BaseMovement(mFightControler);
            mConnectServer = new ConnectServer(13000,mBaseMovement);
            threadPool.submit(mConnectServer);

            out.close();
            br.close();
            c_s_socket.close();
        }

    } catch (IOException e){
        e.printStackTrace();
    }

};
threadPool.submit(runnable);
  • 接收控制命令,发布无人机控制指令 ConnectServer.java
package com.dji.ux.sample.func;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ConnectServer implements Runnable{

    private int PORT;
    private BaseMovement mBaseMovement;

    public ConnectServer(int port, BaseMovement baseMovement){
        this.PORT = port;
        this.mBaseMovement = baseMovement;
    }
    
    @Override
    public void run() {
        try {
            ServerSocket mServerSocket = new ServerSocket(PORT);
            Socket client = mServerSocket.accept();
            BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
            //读取客户端发送来的消息
            String msg = null;
            while ((msg = br.readLine()) != null){
                mBaseMovement.updateControl(msg);
                if(msg == "exit"){
                    br.close();
                    client.close();
                    mBaseMovement.destroyControl();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 无人机控制类 BaseMovement.java
package com.dji.ux.sample.func;

import static com.dji.ux.sample.utils.ModuleVerificationUtil.isFlightControllerAvailable;
import static dji.common.flightcontroller.FlightOrientationMode.AIRCRAFT_HEADING;

import java.util.Timer;
import java.util.TimerTask;

import dji.common.flightcontroller.virtualstick.FlightControlData;
import dji.common.flightcontroller.virtualstick.FlightCoordinateSystem;
import dji.common.flightcontroller.virtualstick.RollPitchControlMode;
import dji.common.flightcontroller.virtualstick.VerticalControlMode;
import dji.common.flightcontroller.virtualstick.YawControlMode;
import dji.sdk.flightcontroller.FlightController;

// roll pitch yaw分别代表三个轴的速度(或角速度),throttle 代表垂直的高度
public class BaseMovement {
    private FlightController mFlightController;
    private float pitch;
    private float roll;
    private float yaw;
    private float throttle;
    private Timer sendVirtualStickDataTimer = null;
    private SendVirtualStickDataTask sendVirtualStickDataTask = null;
    public BaseMovement(FlightController flightController){
    	//获取控制器
        mFlightController = flightController;
		//开启虚拟摇杆功能
        mFlightController.setVirtualStickModeEnabled(true,null);
        mFlightController.setVirtualStickAdvancedModeEnabled(true);
        mFlightController.setFlightOrientationMode(AIRCRAFT_HEADING,null);
		//设置无人机遵循的坐标系以及控制模式
        mFlightController. setRollPitchCoordinateSystem(FlightCoordinateSystem.BODY);
        mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY);
        mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
        mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
        //定时器用来定时,200ms发送一次数据
        if (null == sendVirtualStickDataTimer) {
            sendVirtualStickDataTask = new SendVirtualStickDataTask();
            sendVirtualStickDataTimer = new Timer();
            sendVirtualStickDataTimer.schedule(sendVirtualStickDataTask, 100, 200);
        }
    }
    //控制函数
    public void updateControl(String mCommand){
        switch (mCommand){
            case "takeoff": {
                yaw = 0f;
                pitch = 0f;
                roll = 0f;
                throttle = 0f;
                mFlightController.startTakeoff(null);
                break;
            }
            case "land": {
                yaw = 0f;
                pitch = 0f;
                roll = 0f;
                throttle = 0f;
                mFlightController.startLanding(null);
                break;
            }
            case "left_up": {
                yaw = 0f;
                pitch = 0f;
                roll = 0f;
                throttle = 4;
                break;
            }
            case "left_left":{
                yaw = -30f;
                pitch = 0f;
                roll = 0f;
                throttle = 0f;
                break;
            }
            case "left_center":
            case "right_center": {
                yaw = 0f;
                pitch = 0f;
                roll = 0f;
                throttle = 0f;
                break;
            }
            case "left_right":{
                yaw = 30f;
                pitch = 0f;
                roll = 0f;
                throttle = 0f;
                break;
            }
            case "left_down":{
                yaw = 0f;
                pitch = 0f;
                roll = 0f;
                throttle = -4;
                break;
            }
            case "right_up":{
                yaw = 0f;
                pitch = 0f;
                roll = 3f;
                throttle = 0f;
                break;
            }
            case "right_left":{
                yaw = 0f;
                pitch = -3f;
                roll = 0;
                throttle = 0f;
                break;
            }
            case "right_right":{
                yaw = 0f;
                pitch = 3f;
                roll = 0f;
                throttle = 0f;
                break;
            }
            case "right_down":{
                yaw = 0f;
                pitch = 0f;
                roll = -3f;
                throttle = 0f;
                break;
            }
            default:
                break;
        }
    }
    private class SendVirtualStickDataTask extends TimerTask {
        @Override
        public void run() {
            if (isFlightControllerAvailable()) {
                mFlightController.sendVirtualStickFlightControlData(new FlightControlData(pitch,
                                roll,
                                yaw,
                                throttle),
                        null);
            }
        }
    }
    public void destroyControl(){
        mFlightController.setVirtualStickModeEnabled(false, null);
    }
}

四、视频流传输模块

	//开启发送视频流线程
	SendStream mSendStream = new SendStream(fpvWidget,IP_DEST);
	mSendStream.start();
  • SendStream.java
package com.dji.ux.sample.func;

import android.util.Log;

import dji.sdk.camera.VideoFeeder;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.sdkmanager.LiveVideoResolution;
import dji.ux.widget.FPVWidget;

public class SendStream extends Thread {

    private FPVWidget mFPVWidget;
    private String IP_DEST;

    public SendStream(FPVWidget tmFPVWidget, String mIP_DEST){
        this.mFPVWidget = tmFPVWidget;
        this.IP_DEST = mIP_DEST;
    }
    @Override
    public void run() {
        mFPVWidget.registerLiveVideo(VideoFeeder.getInstance().getPrimaryVideoFeed(),true);
        // 设置视频流发送地址
        DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl(
                "rtmp://"+IP_DEST+"/live/livestream");
        // 强制启用/禁用实时流的视频编码。处理来自飞机的视频输入的基本工作流程是先解码,然后以特定比特率再次编码成 H.264 流。因此,默认需要视频编码。
        // Mavic Pro 等一些无人机,由于 Mavic Pro 遥控器的原始视频可以直接流式传输到 RTMP 服务器,因此 Mavic Pro 禁用了视频编码,
        // 但是原始视频的码率非常高,通过启用此标志,原始视频将被解码并编码为较低速度的视频流。
        DJISDKManager.getInstance().getLiveStreamManager()
                .setVideoEncodingEnabled(true);
        // 设置发送视频分辨率
        DJISDKManager.getInstance().getLiveStreamManager()
                .setLiveVideoResolution(LiveVideoResolution.VIDEO_RESOLUTION_1920_1080);
        // 以 kbps 为单位设置实时视频流比特率。
        // 实时视频比特率的浮点值。该值应在 [0.5*1024, 3.5*1024] kpbs 范围内。
        DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoBitRate(1024);
        // 开始发送视频流
        int result = DJISDKManager.getInstance().getLiveStreamManager()
                        .startStream();
        Log.e("Test", "startLive:" + result + DJISDKManager.getInstance()
                .getLiveStreamManager().isStreaming() +
                "\n isVideoStreamSpeedConfigurable:" + DJISDKManager
                .getInstance().getLiveStreamManager()
                .isVideoStreamSpeedConfigurable() +
                "\n isLiveAudioEnabled:" + DJISDKManager.getInstance()
                .getLiveStreamManager().isLiveAudioEnabled());

    }
}

/*
 * 该类用于将视频流传输到RTMP服务器,以使用DJI产品进行实时流传输。
 *
public class LiveStreamManager {
    * 确定直播是否开始。启动后,此标志将不受RTMP服务器状态的影响。
    public boolean isStreaming() {
        return false;
    }
    * 设置RTMP服务器的URL地址。此方法应在调用之前调用startStream。
    public void setLiveUrl(String var1) {
    }

    * 获取RTMP服务器的当前URL地址。
    public String getLiveUrl() {
        return null;
    }

    * 开始直播。如果成功启动,isStreaming将返回true。如果需要,编码器将开始对视频帧进行
    * 编码。如果服务器可用,视频将以流式传输到RTMP服务器。如果启用了音频设置,则可以将音频
    * 与视频一起以流式传输。
    public int startStream() {
        return 0;
    }
    *
    * 停止直播。该操作是异步的。
    public void stopStream() {
    }

    public void setVideoSource(LiveStreamManager.LiveStreamVideoSource var1) {
    }

    * 返回实时流的开始时间。startStream 成功时将更新开始时间
    public long getStartTime() {
        return 0L;

    * 启用/禁用音频流。启用后,由移动设备的麦克风接收的音频将与视频一起流式传输到RTMP服务器。
    * 此设置将在startStream调用之前生效。startStream调用后更改此设置需要重新启动流。
    * 可以在流传输期间通过调用将音频静音setAudioMuted。
    public void setAudioStreamingEnabled(boolean var1) {
    }

    * 使音频静音或取消静音。
    * 注意:此方法仅在启用音频功能(isLiveAudioEnable dreturn true)时有效。
    public void setAudioMuted(boolean var1) {
    }

    * 强制启用/禁用实时流式传输的视频编码。
    * 飞机视频输入的处理基本工作流程:
    * 1、先解码
    * 2、以特定的比特率再次编码为H.264流。
    *
    * 因此,默认情况下需要视频编码。对于某些无人机,例如Mavic Pro,
    * 可以将Mavic Pro遥控器的原始视频提要直接传输到RTMP服务器,
    * 因此Mavic Pro的视频编码被禁用,但是,原始视频的比特率非常高,启用此标志后,
    * 原始视频将被解码并编码为低速视频流。
    public void setVideoEncodingEnabled(boolean var1) {
    }

    * 返回实时视频fps。
    public float getLiveVideoFps() {
        return 0.0F;
    }

    * 返回以kpbs为单位的实时视频流比特率
    public int getLiveVideoBitRate() {
        return 0;
    }

    * 返回以kpbs为单位的实时音频流比特率。
    public int getLiveAudioBitRate() {
        return 0;
    }

    * 返回实时视频缓存列表大小,单位:帧。
    public int getLiveVideoCacheSize() {
        return 0;
    }
}
*/

五、飞行信息传输模块

	//开启发送数据线程
	SendDroneInfo mSendDroneInfo = new SendDroneInfo(mBaseDroneInfo,IP_DEST);
	threadPool.submit(mSendDroneInfo);
  • 信息类 BaseDroneInfo.java
package com.dji.ux.sample.func.Info;

//class FlightControllerState--此类表示飞行控制器的当前状态。

//boolean areMotorsOn()--如果电机打开,则为真。
//boolean isFlying()--如果飞机正在飞行,则为真。
//LocationCoordinate3D getAircraftLocation()--获取飞机的当前位置作为坐标。如果位置无效,则为零。
//float getTakeoffLocationAltitude()--飞机原地位置相对于海平面的相对高度,以米为单位。
//Attitude getAttitude()--获取飞机的姿态,其中俯仰、滚动和偏航值将在 [-180, 180] 度的范围内。如果其俯仰、横滚和偏航值为 0,
//                        则飞机将以真北航向悬停在水平面上。
//float getVelocityX()--飞机在 x 方向上的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系。
//float getVelocityY()--飞机在 y 方向的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系。
//float getVelocityZ()--飞机在 z 方向上的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系。
//int getFlightTimeInSeconds()--自飞机发动机开启以来的累计飞行时间(以秒为单位)。
//boolean isLandingConfirmationNeeded()--如果飞行器与地面的间隙小于0.3m,则为true,需要用户确认才能继续降落。
//                                       当需要确认时,可以使用 FlightController 中的 confirmLanding 继续降落。
//                                       飞控固件3.2.0.0及以上版本支持。
//FlightMode getFlightMode()--飞控飞行模式。有关详细信息,请参阅飞行模式部分
//                             https://developer.dji.com/document/a3bd20d1-9f1c-4f76-a9aa-46eb0d23cd82。
//boolean isMultipleModeOpen()--确定是否打开多个飞行模式。这将反映导航模式(地面站)是否启用。
//int getSatelliteCount()--返回 GPS 卫星计数。
//GPSSignalLevel getGPSSignalLevel()--获取飞行器当前 GPS 信号质量。如果返回值在[6,10]内,表示RTK信号强。
///boolean isIMUPreheating()--如果 IMU 正在预热,则为 true。
//boolean isUltrasonicBeingUsed()--如果正在使用超声波传感器,则为 true。可能影响超声测量质量或是否正在使用的变量是离地高度和地面类型
//                                (如果它很好地反射声波)。通常,超声波传感器在飞行器离地高度小于 8m 时工作。
//float getUltrasonicHeightInMeters()--由超声波传感器测量的飞机高度,以米为单位。仅当 isUltrasonicBeingUsed 返回 true 时数据才可用。
//                                     高度精度为0.1m。该值在高度低于 5 米时具有参考意义。
//boolean doesUltrasonicHaveError()--如果超声波传感器有错误,则为真。
//boolean isVisionPositioningSensorBeingUsed()--如果正在使用视觉传感器,则为 true。可能影响视觉测量质量或是否正在使用的变量是离地高度和地面类型
//                                              (如果它具有足够丰富的纹理)。通常,视觉传感器在飞行器离地小于 3m 时工作。
//FlightOrientationMode getOrientationMode()--飞行器当前的定向模式。
//boolean isFailsafeEnabled()--如果遥控器和飞行器之间信号丢失,并且启用了FailSafe,则为true。
//BatteryThresholdBehavior getBatteryThresholdBehavior()--基于剩余电池寿命的建议操作。Mavic Air 2、DJI Air 2S 不支持。
//boolean isLowerThanBatteryWarningThreshold()--如果电池电量低于低电量警告阈值,则为 true。
//boolean isLowerThanSeriousBatteryWarningThreshold()--如果电池电量低于严重低电量警告阈值,则为 true。
//FlightWindWarning getFlightWindWarning()--与大风有关的警告。
//int getFlightCount()--The count of flights within the battery life cycle. Cleared when power-on.
//                    --电池生命周期内的飞行次数。上电时清零。
//int getFlightLogIndex()--飞机上飞行日志的当前索引。查找相应的飞行日志很有用。
//boolean isActiveBrakeEngaged()--当启用主动制动以避免障碍物时为真。仅 Matrice 300 RTK 支持。
//boolean isHomeLocationSet()--true if the home point has been set.
//LocationCoordinate2D getHomeLocation()--以坐标返回飞机的home位置。
//GoHomeAssessment getGoHomeAssessment()--获取飞行器智能返航数据。如果启用了智能回家,则所有智能回家数据都将在 GoHomeAssessment 中可用。
//GoHomeExecutionState getGoHomeExecutionState()--回家执行的当前状态。
//boolean isGoingHome()--true if the aircraft is going home.
//int getGoHomeHeight()--获取飞机返回时的高度(以米为单位)。

import dji.common.flightcontroller.Attitude;
import dji.common.flightcontroller.FlightControllerState;
import dji.common.flightcontroller.GPSSignalLevel;
import dji.common.flightcontroller.LocationCoordinate3D;
import dji.common.model.LocationCoordinate2D;

//class Attitude
//The attitude of the aircraft where the pitch, roll, and yaw values will be in the range of [-180, 180] degrees. If its pitch, roll, and yaw values are 0, the aircraft will be hovering level with a True North heading.
//俯仰、横滚和偏航值将在 [-180, 180] 度范围内的飞机姿态。如果其俯仰、横滚和偏航值为 0,则飞机将以真北航向悬停在水平面上。
public class BaseDroneInfo {

    private String deviceName = "";                                 //
    private boolean MotorsOnOrNot = false;
    private boolean FlyingOnorNot = false;
    private LocationCoordinate3D mLocationCoordinate3D = null;      //
    private float mTakeoffLocationAltitude = 0;
    private Attitude mAttitude = null;                              //
    float VelocityX = 0;                                            //
    float VelocityY = 0;                                            //
    float VelocityZ = 0;                                            //
    private int mSatelliteCount = 0;
    private GPSSignalLevel mGPSSignalLevel = null;
    private float mUltrasonicHeightInMeters = 0;
    private LocationCoordinate2D mLocationCoordinate2D = null;

    //获取最新无人机飞行数据信息
    public void updateData(String DroneName, FlightControllerState mFlightControllerState){
        //获取设备名称
        deviceName = DroneName;
        //如果电机打开,则为真
        MotorsOnOrNot = mFlightControllerState.areMotorsOn();
        //如果飞机正在飞行,则为真
        FlyingOnorNot = mFlightControllerState.isFlying();
        //获取飞机的当前位置作为坐标。如果位置无效,则为零
        mLocationCoordinate3D = mFlightControllerState.getAircraftLocation();
        //飞机原地位置相对于海平面的相对高度,以米为单位
        mTakeoffLocationAltitude = mFlightControllerState.getTakeoffLocationAltitude();
        //获取飞机的姿态,其中俯仰、滚动和偏航值将在 [-180, 180] 度的范围内。如果其俯仰、横滚和偏航值为 0,
        //则飞机将以真北航向悬停在水平面上
        mAttitude = mFlightControllerState.getAttitude();
        //飞机在 x 方向上的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系
        VelocityX = mFlightControllerState.getVelocityX();
        //飞机在 y 方向上的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系
        VelocityY = mFlightControllerState.getVelocityY();
        //飞机在 z 方向上的当前速度,以米/秒为单位,使用 N-E-D(North-East-Down)坐标系
        VelocityZ = mFlightControllerState.getVelocityZ();
        //返回 GPS 卫星计数
        mSatelliteCount = mFlightControllerState.getSatelliteCount();
        //获取飞行器当前 GPS 信号质量。如果返回值在[6,10]内,表示RTK信号强
        mGPSSignalLevel = mFlightControllerState.getGPSSignalLevel();
        //由超声波传感器测量的飞机高度,以米为单位。仅当 isUltrasonicBeingUsed 返回 true 时数据才可用。
        //高度精度为0.1m。该值在高度低于 5 米时具有参考意义
        mUltrasonicHeightInMeters = mFlightControllerState.getUltrasonicHeightInMeters();
        //以坐标返回飞机的home位置。
        mLocationCoordinate2D = mFlightControllerState.getHomeLocation();
    }

    public String getDeviceName() {
        return deviceName;
    }

    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName;
    }

    public boolean isMotorsOnOrNot() {
        return MotorsOnOrNot;
    }

    public void setMotorsOnOrNot(boolean motorsOnOrNot) {
        MotorsOnOrNot = motorsOnOrNot;
    }

    public boolean isFlyingOnorNot() {
        return FlyingOnorNot;
    }

    public void setFlyingOnorNot(boolean flyingOnorNot) {
        FlyingOnorNot = flyingOnorNot;
    }

    public LocationCoordinate3D getmLocationCoordinate3D() {
        return mLocationCoordinate3D;
    }

    public void setmLocationCoordinate3D(LocationCoordinate3D mLocationCoordinate3D) {
        this.mLocationCoordinate3D = mLocationCoordinate3D;
    }

    public float getmTakeoffLocationAltitude() {
        return mTakeoffLocationAltitude;
    }

    public void setmTakeoffLocationAltitude(float mTakeoffLocationAltitude) {
        this.mTakeoffLocationAltitude = mTakeoffLocationAltitude;
    }

    public Attitude getmAttitude() {
        return mAttitude;
    }

    public void setmAttitude(Attitude mAttitude) {
        this.mAttitude = mAttitude;
    }

    public float getVelocityX() {
        return VelocityX;
    }

    public void setVelocityX(float velocityX) {
        VelocityX = velocityX;
    }

    public float getVelocityY() {
        return VelocityY;
    }

    public void setVelocityY(float velocityY) {
        VelocityY = velocityY;
    }

    public float getVelocityZ() {
        return VelocityZ;
    }

    public void setVelocityZ(float velocityZ) {
        VelocityZ = velocityZ;
    }

    public int getmSatelliteCount() {
        return mSatelliteCount;
    }

    public void setmSatelliteCount(int mSatelliteCount) {
        this.mSatelliteCount = mSatelliteCount;
    }

    public GPSSignalLevel getmGPSSignalLevel() {
        return mGPSSignalLevel;
    }

    public void setmGPSSignalLevel(GPSSignalLevel mGPSSignalLevel) {
        this.mGPSSignalLevel = mGPSSignalLevel;
    }

    public float getmUltrasonicHeightInMeters() {
        return mUltrasonicHeightInMeters;
    }

    public void setmUltrasonicHeightInMeters(float mUltrasonicHeightInMeters) {
        this.mUltrasonicHeightInMeters = mUltrasonicHeightInMeters;
    }

    public LocationCoordinate2D getmLocationCoordinate2D() {
        return mLocationCoordinate2D;
    }

    public void setmLocationCoordinate2D(LocationCoordinate2D mLocationCoordinate2D) {
        this.mLocationCoordinate2D = mLocationCoordinate2D;
    }

//    @Override
//    public String toString() {
//        return "BaseDroneInfo{" +
//                "deviceName='" + deviceName + '\'' +
//                ", mLocationCoordinate3D=" + mLocationCoordinate3D +
//                ", mTakeoffLocationAltitude=" + mTakeoffLocationAltitude +
//                ", mAttitude=" + mAttitude +
//                ", VelocityX=" + VelocityX +
//                ", VelocityY=" + VelocityY +
//                ", VelocityZ=" + VelocityZ +
//                ", mSatelliteCount=" + mSatelliteCount +
//                ", mGPSSignalLevel=" + mGPSSignalLevel +
//                ", mUltrasonicHeightInMeters=" + mUltrasonicHeightInMeters +
//                ", mLocationCoordinate2D=" + mLocationCoordinate2D +
//                '}';
//    }


    @Override
    public String toString() {
        return "{\"DeviceName\":" + "\"" + deviceName + "\"" +
                ",\"Roll\":" + "\""+mAttitude.roll+"\"" +
                ",\"Yaw\":" + "\""+mAttitude.yaw+"\"" +
                ",\"Latitude\":" + "\""+mLocationCoordinate2D.getLatitude()+"\"" +
                ",\"Longitude\":" + "\""+mLocationCoordinate2D.getLongitude()+"\"" +
                ",\"VelocityX\":" + "\""+VelocityX+"\"" +
                ",\"VelocityY\":" + "\""+VelocityY+"\"" +
                ",\"VelocityZ\":" + "\""+VelocityZ+"\"" + "}";
    }
}

  • 发送数据 SendDroneInfo.java
package com.dji.ux.sample.func.Info;

//import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class SendDroneInfo implements Runnable{

    private BaseDroneInfo mBaseDroneInfo = null;
    private String send_json = "";
    private String IP_DEST;

    public SendDroneInfo(BaseDroneInfo tBaseDroneInfo, String mIP_DEST) throws IOException {
        this.mBaseDroneInfo = tBaseDroneInfo;
        this.IP_DEST = mIP_DEST;
    }


    @Override
    public void run() {

        try {
            //建立Socket连接
            Socket socket = new Socket(IP_DEST, 37634);
            OutputStream out = socket.getOutputStream();
            while(true){
                if(socket.isBound()) {
                    //发送数据
                    out.write(mBaseDroneInfo.toString().getBytes());
                }
                //缓冲 1s
                Thread.sleep(1000);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

六、汇总

界面展示

  • 在官方提供的CompleteWidgetActivity.java中添加以下函数
	//服务模块
	ServerButton = findViewById(R.id.ServerButton);
	ServerButton.setOnClickListener(new View.OnClickListener(){
	    @Override
	    public void onClick(View view) {
	        try {
	            onServerButtonClick(view);
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
	});
private void onServerButtonClick(View view) throws IOException {
    if (!DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
        threadPool = Executors.newFixedThreadPool(10);
        Toast.makeText(this, "实时视频流已推送:"+IP_DEST,Toast.LENGTH_LONG).show();
        ServerButton.setText("S");
        //开启发送视频流线程
        SendStream mSendStream = new SendStream(fpvWidget,IP_DEST);
        mSendStream.start();

        //开启发送数据线程
        SendDroneInfo mSendDroneInfo = new SendDroneInfo(mBaseDroneInfo,IP_DEST);
        threadPool.submit(mSendDroneInfo);

        Runnable runnable=()-> {
            try {
                Socket c_s_socket = new Socket(IP_DEST, 10000);
                PrintWriter out = new PrintWriter(c_s_socket.getOutputStream()); //向客户端发送数据
                out.println("Drone is ready!");
                out.flush();//将缓冲流中的数据全部输出

                //读取服务器返回的消息
                BufferedReader br = new BufferedReader(new InputStreamReader(c_s_socket.getInputStream()));
                String temp_msg = br.readLine();
                if(temp_msg.equals("start")){
                    mBaseMovement = new BaseMovement(mFightControler);
                    mConnectServer = new ConnectServer(13000,mBaseMovement);
                    threadPool.submit(mConnectServer);

                    out.close();
                    br.close();
                    c_s_socket.close();
                }

            } catch (IOException e){
                e.printStackTrace();
            }

        };
        threadPool.submit(runnable);

    }
    else {
        Toast.makeText(CompleteWidgetActivity.this, "实时视频流已关闭",Toast.LENGTH_LONG).show();
        new Thread() {
            @Override
            public void run() {
                DJISDKManager.getInstance().getLiveStreamManager()
                        .stopStream();
            }
        }.start();
        threadPool.shutdownNow();
        ServerButton.setText("-");
    }
}

七、问题与改进

  • 视频流、飞行数据传输、无人机控制全部由一个使能按键控制。
  • App偶尔会闪退。
  • 终端同服务器主动或被动断开连接,相应的线程应同时关闭。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值