How to make a drone follow scripted trajectories

This tutorial shows how to use handling plugins to create scripted animations. They are mostly used to simulate actions that would otherwise require a human operator.

Each drone may come with one or several instances of the handling plugin. For example, the Disco comes with a plugin to simulate a user shaking the drone and tossing it into the air, but also a plugin to simulate a user calibrating the drone.

Plugin parameters can be visualized in the Web interface. See Tuning of drone internals at runtime - interfaces for more information on how to access the Web interface.

A PID is used to control the value of each coordinate during a world iteration. The proportional and derivative gains can be modified with force_p_gain and force_d_gain for the forces, and torque_p_gain and torque_d_gain for the torques. The loop parameter determines whether the script should loop. The script itself is defined in the script parameter in json format. It contains a list of steps executed successively, where each step contains two sets of exprtk expressions used to animate the drone and a stop condition to move to the next step.

A script can be started, stopped and paused, by sending the start, stop, and pause actions to the corresponding handling plugin.

Let’s take a look at a simple handling script example which comes with the Bebop2 to simulate a hand launch:

Example

[
  {
    "stop_condition_expr": "abs(motor_speed_0) > 300 or step_time > 40",
    "start_pose": {
      "x_expr": "0",
      "y_expr": "0",
      "z_expr": "1",
      "roll_expr": "0",
      "pitch_expr": "0",
      "yaw_expr": "val"
    },
    "pose": {
      "x_expr": "noise()*0.01",
      "y_expr": "noise()*0.01",
      "z_expr": "noise()*0.01",
      "roll_expr": "start_val + noise()*0.01",
      "pitch_expr": "start_val + noise()*0.01",
      "yaw_expr": "start_val + noise()*0.01"
    }
  },
  {
    "stop_condition_expr": "step_time > 2",
    "start_pose": {
      "x_expr": "0",
      "y_expr": "0",
      "z_expr": "0",
      "roll_expr": "val",
      "pitch_expr": "val",
      "yaw_expr": "val"
    },
    "pose": {
      "x_expr": "noise()*0.01",
      "y_expr": "noise()*0.01",
      "z_expr": "noise()*0.01",
      "roll_expr": "start_val + noise()*0.01",
      "pitch_expr": "start_val + noise()*0.01",
      "yaw_expr": "start_val + noise()*0.01"
    }
  },
  {
    "stop_condition_expr": "step_time > 0.1",
    "start_pose": {
      "x_expr": "0",
      "y_expr": "0",
      "z_expr": "0",
      "roll_expr": "val",
      "pitch_expr": "val",
      "yaw_expr": "val"
    },
    "pose": {
      "x_expr": "0",
      "y_expr": "0",
      "z_expr": "2*step_time",
      "roll_expr": "start_val",
      "pitch_expr": "start_val",
      "yaw_expr": "start_val"
    }
  }
]

The code explained

Let’s go through it by parts.

We start by defining a first step:

[
  {
    "stop_condition_expr": "abs(motor_speed_0) > 300 or step_time > 40",

The stop_condition_expr key is an exprtk expression which determines when the step will end. In this example, we check for the variable motor_speed_0 to detect that the motors have started and set a timeout of 40 seconds to prevent the script from running indefinitely. For each exprtk expression, step_time is set to the elapsed time since the step has started, and script_time is the elapsed time since the beginning of the entire script.

"start_pose": {
  "x_expr": "0",
  "y_expr": "0",
  "z_expr": "1",
  "roll_expr": "0",
  "pitch_expr": "0",
  "yaw_expr": "val"
},

The start_pose key defines the starting pose of the drone. Each coordinate is described as an exprtk expression. The x_expr, y_expr, and z_expr expressions are used to translate the drone and are relative to its body. In this example, the drone will start 1 meter above its original position. The roll_expr, pitch_expr, and yaw_expr expressions are used to set the world orientation of the drone and are relative to the world frame. In this example, the drone will maintain its original yaw orientation and have its pitch and roll set to zero. Note that for each expression, the val variable is set to the value of current corresponding drone coordinate, while the start_val variable is set to corresponding drone coordinate before launching the script.

  "pose": {
    "x_expr": "noise()*0.01",
    "y_expr": "noise()*0.01",
    "z_expr": "noise()*0.01",
    "roll_expr": "start_val + noise()*0.01",
    "pitch_expr": "start_val + noise()*0.01",
    "yaw_expr": "start_val + noise()*0.01"
  }
},

The pose key contains expressions that are applied to modify the pose of the drone during each world iteration after the drone has been positioned at its starting pose. Note that for each expression, the val variable is set to the value of corresponding drone coordinate, while the start_val variable is set to the corresponding “step” starting coordinate determined with start_pose. In this example, the drone pose is slightly disturbed in all directions with a gaussian noise.

We now define a second step which starts immediately after the first one meets its stop condition:

{
  "stop_condition_expr": "step_time > 2",
  "start_pose": {
    "x_expr": "0",
    "y_expr": "0",
    "z_expr": "0",
    "roll_expr": "val",
    "pitch_expr": "val",
    "yaw_expr": "val"
  },
  "pose": {
    "x_expr": "noise()*0.01",
    "y_expr": "noise()*0.01",
    "z_expr": "noise()*0.01",
    "roll_expr": "start_val + noise()*0.01",
    "pitch_expr": "start_val + noise()*0.01",
    "yaw_expr": "start_val + noise()*0.01"
  }
},

The second step simply continues shaking the drone for 2 seconds. After that time, the third step starts immediately:

{
  "stop_condition_expr": "step_time > 0.1",
  "start_pose": {
    "x_expr": "0",
    "y_expr": "0",
    "z_expr": "0",
    "roll_expr": "val",
    "pitch_expr": "val",
    "yaw_expr": "val"
  },

The third step is used to launch the drone into the air in 1/10th of a second. The start_pose expressions are defined to keep the same coordinates as the ones obtained at the end of the first step.

    "pose": {
      "x_expr": "0",
      "y_expr": "0",
      "z_expr": "2*step_time",
      "roll_expr": "start_val",
      "pitch_expr": "start_val",
      "yaw_expr": "start_val"
    }
  }
]

During each world iteration, a new target height is obtained from the elapsed step time and is used by the corresponding PID to generate a force which results in the drone being thrown into the air.