在 DJI SDK 中 Mission 的执行是由各自对应的 operator 管理的,WaypointMission 的生命周期由 DJIWaypointMissionOperator 管理。

基本流程

WaypointMission生命周期.jpg

检查任务

在上一篇文章中介绍了 mission 和 waypoint 可以进行的配置。很多配置项要在对应的模式下才有效,因此在开始任务前需要检查一下任务的配置是否正确。这里的检查函数有两个,一个检查 mission 的配置,一个检查 waypoint 的配置:

  1. // 检查任务的参数配置是否有效
  2. mission.checkParameters()
  3. // 检查 waypoint 的配置是否有效
  4. mission.checkValidity()

加载、上传任务

任务的执行是由 DJIWaypointMissionOperator 管理的,所以首先需要把任务加载到 operator 中:

  1. guard let missionOperator = DJISDKManager.missionControl()?.waypointMissionOperator() else { return }
  2. missionOperator.load(mission)

加载成功后需要把任务数据上传到无人机的飞控中。Waypoint 是一个一个上传的,如果 waypoint 比较多可能上传时间会久一些。要注意和无人机的通信状况是否良好,这一步很容易因为通信状况不佳失败。

  1. missionOperator.uploadMission { (error) in
  2. if let error = error {
  3. // 处理上传失败的错误
  4. }
  5. }

考虑到可能有多个对象关心航点的上传进度,DJI 使用观察者模式来处理上传的进度通知。

  1. // 添加观察者
  2. /**
  3. * Adds listener to receive the event related to upload.
  4. */
  5. - (void)addListenerToUploadEvent:(id)listener
  6. withQueue:(nullable dispatch_queue_t)queue
  7. andBlock:(DJIWaypointMissionOperatorUploadEventBlock)block;
  8. // 移除观察者
  9. - (void)removeListenerOfUploadEvents:(id)listener;

通过回调的 DJIWaypointMissionUploadEvent 实例可以获取到当前的上传进度和任务的状态。

  1. missionOperator.addListener(toUploadEvent: self, with: listenerQueue) { (uploadEvent) in
  2. if let progress = uploadEvent.progress {
  3. print("progress: \(progress.uploadedWaypointIndex)/\(progress.totalWaypointCount)")
  4. }
  5. if uploadEvent.currentState == .readyToExecute {
  6. missionOperator.removeListener(ofUploadEvents: self)
  7. }
  8. }

当 uploadEvent 中的当前任务状态是 readyToExecute 时说明航点已经上传结束,无人机准备就绪。

任务控制

任务的控制有:开始、暂停、继续、停止四种操作。

  1. - (void)startMissionWithCompletion:(DJICompletionBlock)completion;
  2. - (void)pauseMissionWithCompletion:(DJICompletionBlock)completion;
  3. - (void)resumeMissionWithCompletion:(DJICompletionBlock)completion;
  4. - (void)stopMissionWithCompletion:(DJICompletionBlock)completion;

只要任务在对应正确的状态时这些操作才会成功。比如只有当任务是在执行的状态时暂停和停止才能正确执行,否则会返回一个错误。

任务进度监听

与上传航点的监听方式一致,任务的执行进度通知也采用了一样的的观察者模式。

  1. /**
  2. * Adds listener to receive the event related to execution.
  3. */
  4. - (void)addListenerToExecutionEvent:(id)listener
  5. withQueue:(nullable dispatch_queue_t)queue
  6. andBlock:(DJIWaypointMissionOperatorExecutionEventBlock)block;

ExecutionEvent 返回的 DJIWaypointMissionExecutionEvent 实例。DJIWaypointMissionExecutionEvent 包含了任务当前的状态和任务执行的进度。

  1. @interface DJIWaypointMissionExecutionEvent : NSObject
  2. /**
  3. * The previous state of the operator.
  4. */
  5. @property (nonatomic, readonly) DJIWaypointMissionState previousState;
  6. /**
  7. * The current state of the operator.
  8. */
  9. @property (nonatomic, readonly) DJIWaypointMissionState currentState;
  10. /**
  11. * The execution progress of the mission. It is `nil` if there is an error during
  12. * the execution.
  13. */
  14. @property (nonatomic, readonly, nullable) DJIWaypointExecutionProgress *progress;
  15. /**
  16. * The encountered error during the execution if there is any. Otherwise, it is
  17. * `nil`.
  18. */
  19. @property (nonatomic, readonly, nullable) NSError *error;
  20. @end

通过 ExecutionEvent 中的 error 属性可以判断执行是否遇到错误,currentState 和 previousState 反应了此时的任务状态。progress 可以知道当前无人机飞过的航点进度。

  1. @interface DJIWaypointExecutionProgress : NSObject
  2. /**
  3. * Index of the waypoint for the next mission to which the aircraft will proceed.
  4. */
  5. @property(nonatomic, readonly) NSInteger targetWaypointIndex;
  6. /**
  7. * YES when the aircraft reaches a waypoint. After the waypoint actions and heading
  8. * change is complete, the `targetWaypointIndex` will increment and this property
  9. * will become NO.
  10. */
  11. @property(nonatomic, readonly) BOOL isWaypointReached;
  12. /**
  13. * Current execution state of the aircraft.
  14. */
  15. @property(nonatomic, readonly) DJIWaypointMissionExecuteState execState;
  16. @end

targetWaypointIndex 表示飞机正在飞向的航点的索引,在飞行过程中 isWaypointReached 的值是 false。当无人机到达航点航点位置时 isWaypointReached 的值会变成 true。接着无人机根据设置调整 heading,执行 waypoint action。执行完成后 targetWaypointIndex 加 1,朝下一个航点飞去。
Waypoint回调状态.jpg
通常我们认为任务结束时 targetWaypointIndex 会是最后一个点,但是如果刚好 finishAction 是 goFirstWaypoint,无人机飞到最后一个点后会返回第一个航点,所以最后任务结束时的 targetWaypointIndex 会是 0。
另外一个容易理解错误的是 ExecutionEvent 是一个定时回调的事件,不是在 value changed 的时候才回调。所以无人机飞行过程中你可能会一直收到同样的值的 ExecutionEvent。

任务结束监听

任务结束的通知也采用了一样的的观察者模式。

  1. /**
  2. * Adds listener to receive the notification when a waypoint mission is finished.
  3. */
  4. - (void)addListenerToFinished:(id)listener
  5. withQueue:(nullable dispatch_queue_t)queue
  6. andBlock:(DJICompletionBlock)block;

任务结束的回调有三种可能:

  • 回调有 error,任务执行过程中遇到了错误,任务终止
  • 回调无 error,到达过最后一个航点,任务正常结束
  • 回调无 error,没有到达过最后一个航点,任务过程中用户主动停止了任务或者过程中触发无人机返航

    任务状态

    在任务生命周期中很多操作都和任务的状态有关。Operator 上的 currentState 能取到最近一次同步的任务状态。任务状态 DJIWaypointMissionState 总共有以下几种状态:

  • Unknown:和无人机没有正常的通信,无法确认任务当前状态

  • Disconnected:无人机断开连接
  • Recovering:手机和遥控器、无人机间的连接正在重连,任务状态正在同步中
  • NotSupported:连接的设备不支持该任务的状态同步
  • ReadyToUpload:无人机现在可以上传航点数据
  • Uploading:上传航点数据中
  • ReadyToExecute:航点数据上传成功,准备好开始执行任务
  • Executing:任务执行中
  • Paused:任务被暂停

    下载任务

    Waypoint mission 因为航点是一次性上传到飞控中,默认又是遥控器丢失信号任务继续执行。所以可能出现任务过程中应用退出,应用重新连接上无人机后,无人机虽然在执行任务,但是 sdk 中已经取不到 waypointMission 中的航点信息了。如果这个时候你需要在任务结束后重新进行一次同样的任务,那么任务 start 的时候就会报错,因为本地没有航点信息可以上传。针对这个情况 Operator 提供了下载航点的方法,和上传航点类似,需要注意的是只有在任务暂停和执行状态时下载航点才有效。 ```objectivec /**
    • Downloads information of each waypoint from aircraft and save it to
    • loadedMission. If a download operation is started, the operator will download
    • the information of waypoints missing in loadedMission one-by-one in ascending
    • order. If loadedMission is already complete (containing all the waypoints),
    • this method will call completion immediately without error. It can only be
    • called when the currentState is one of the following: -
    • DJIWaypointMissionStateExecuting - DJIWaypointMissionStateExecutionPaused */
  • (void)downloadMissionWithCompletion:(DJICompletionBlock)completion; // 监听下载的进度
  • (void)addListenerToDownloadEvent:(id)listener
    1. withQueue:(nullable dispatch_queue_t)queue
    2. andBlock:(DJIWaypointMissionOperatorDownloadEventBlock)block;
    ```