背景及问题分析

我们在使用 Waypoint 进行飞行时,默认情况下飞机将以直线从一个航点移动到下一个航点,在航点的拐角处,就会有比较大的段落感。在大部分场景下,这样是没有什么问题的。但是如果我们想在航点拐角时没有那么明显的段落感,而是能比较顺滑地过渡,能不能做到呢?下面就带着这个问题查看 DJI 的文档。

查看 DJI 的 API 可以发现,DJIWaypointMission 有一个类型为 DJIWaypointMissionFlightPathModeflightPathMode 属性,我们可以通过它来设置 Waypoint mission 飞行路线的模式。

DJIWaypointMissionFlightPathMode 有下面两种 mode:

  1. typedef NS_ENUM (NSUInteger, DJIWaypointMissionFlightPathMode){
  2. /**
  3. * The flight path will be normal and the aircraft will move from one waypoint to
  4. * the next in straight lines.
  5. */
  6. DJIWaypointMissionFlightPathNormal,
  7. /**
  8. * The flight path will be curved and the aircraft will move from one waypoint to
  9. * the next in a curved motion, adhering to the `cornerRadiusInMeters`, which is
  10. * set in `DJIWaypoint`.
  11. */
  12. DJIWaypointMissionFlightPathCurved
  13. };

在默认情况下 Waypoint mission 使用的是 normal mode,这样飞机就是以直线从一个航点移动到下一个航点。所有如果我们想让飞机飞弧线,只要设置 Waypoint mission 的 flightPathMode 为 curved,并通过 cornerRadiusInMeters 属性给 Waypoint 设置一个圆角半径就可以了。

我们再去看看 DJIWaypoint 的 cornerRadiusInMeters 属性:

  1. /**
  2. * Corner radius of the waypoint. When the flight path mode is
  3. * `DJIWaypointMissionFlightPathCurved` the flight path near a waypoint will be a
  4. * curve (rounded corner) with radius [0.2,1000]. When there is a corner radius,
  5. * the aircraft will never go through the waypoint. By default, the radius is 0.2
  6. * m. If the corner is made of three adjacent waypoints (Short for A,B,C) . Then
  7. * the radius of A(short for Ra) plus radius of B(short for Rb) must be smaller
  8. * than the distance between A and B. The radius of the first and the last
  9. * waypoint in a mission does not affect the flight path and it should keep the
  10. * default value (0.2m).
  11. */
  12. @property(nonatomic, assign) float cornerRadiusInMeters;

从上面的描述中我们可以了解到以下几点:

  • cornerRadiusInMeters 的范围是从 0.2米到1000米,并且默认值是 0.2米。
  • 两个航点之间的圆角半径之和必须小于两点之间的距离。在测试中我发现,如果没有满足这个条件,Waypoint mission 是无法执行的,而 DJISDK 并没有给出任何错误信息。这一点在后面开发过程中需要注意一下。
  • 给 Waypoint mission 的第一个航点和最后一个航点设置圆角半径是没有必要的,因为第一个航点和最后一个航点不会影响飞机的飞行路径。这里保持默认值就可以了。我在测试中尝试给第一个和最后一航点设置圆角半径确实是没有任何作用的。
  • 一旦我们设置了圆角,那么飞机就不会穿过 waypoint 了。

飞行路径测试


我们现在知道通过设置 flightPathModecornerRadiusInMeters 可以让飞机在 waypoint 中飞弧线了。但是飞机在飞行中的弧线路径到底是什么样的呢?在测试之前,我给出了两种可能的路径:

path1.pngpath2.png

上图1中的飞行路径能完整地覆盖我们在地图上添加的点,而图2中我们添加的点在飞行的圆弧之外。

现在我们可以通过代码来验证一下到底是哪种飞行路径了。下面的代码显示了飞机以50米的半径在 waypoint 中进行弧线飞行:

  1. waypointMission.flightPathMode = .curved
  2. ...
  3. for (index, coor) in coordinates.enumerated() {
  4. let waypoint = DJIWaypoint(coordinate: coor)
  5. waypoint.altitude = 60
  6. //第一个waypoint 和最后一个 waypoint 应该保持默认值(0.2m)
  7. if case 1..<waypointsCount-1 = index {
  8. // 两个 waypoint的 cornerRadiusInMeters 之和必须小于两点之间的距离
  9. waypoint.cornerRadiusInMeters = 50
  10. }
  11. waypoints.append(waypoint)
  12. }

为了能达到上图中的显示效果,我在地图上绘制出了我们选择的多边形区域,并将飞机的飞行路径实时地显示在地图上。下图是在模拟器中的飞行结果:

IMG_0132.jpeg

在上面的地图中,我添加了四个点,右边两个分别为起点和终点。Demo 最终的结果显示飞机的飞行路径跟第二种猜测是吻合的。飞机最终飞行的圆弧是 waypoint 所对的内切圆的弧,也就是下图红色部分:
Artboard@2x.png

这种飞行路径的一个问题就是最终我们选择的点并不在我们实际的飞行区域内了。在后面的开发过程中我们可以通过计算,将我们所有的点都包含在弧形区域内,并在地图上绘制出最终的飞行路径。

弧线 Waypoint 中,Heading 的方向


现在我们已经很明确飞机在飞 waypoint 时是可以飞弧线的。在没有对 waypoint 做特殊设置 ,飞机在飞弧线的过程中,我发现 Heading 的方向是沿着弧线的切线方向的,这样我们就无法拍到中间物体的影像了。那么有没有办法让飞机的 Heading始终指向某个位置呢?就像下面图片中显示的样子:

1557287433180-9ee8010f-d1cc-47c2-a3c0-5700cb6fde70.png

查看 DJI 的 API 后发现其实是可以做到的。我们只要设置 DJIWaypointMission 的 headingMode 为 DJIWaypointMissionHeadingTowardPointOfInterest 就行了:

  1. waypointMission.headingMode = .towardPointOfInterest
  2. waypointMission.pointOfInterest = pointOfInterest

上面代码中我们同时设置了 DJIWaypointMission 的 pointOfInterest ,这里的 pointOfInterest 可以使用多边形的重心坐标,当然这里的坐标肯定是 CLLocationCoordinate2D 坐标了。关于多边形重心坐标的计算可以看这里

实地测试结果

最后通过实地飞行测试后可以得出结论:

  • 飞机在飞行 waypoint 时是可以飞行弧线的
  • Heading 的方向可以指向兴趣点

最终结果还是符合预期的。当然,后面还是需要使用多种机型进行测试,看看还有什么其他的因素会影响飞行。

待解决的问题

因为在给 waypoint 设置 cornerRadiusInMeters 时,两个 waypoint 的 cornerRadiusInMeters 之和必须小于两者之间的距离,所以在实际的开发中我们要根据用户选择的区域算出比较合适的圆角半径来。

另外一个问题就是有一些区域(弧线部分)是不包含在最终的飞行路径中的。所以在用户选择好区域后,需要将区域做一定的外扩,使用户选择的所有区域都包含在最终的弧行区域中。