This document describes how the Crazyflie highlevel commander stores trajectories in the internal trajectory memory.
Trajectories for the highlevel commander may be stored in one of two formats. either as a sequence of raw 7th degree polynomials, or as a more spaceefficient representation using the same polynomials in Bernstein form. In both cases, a single segment stores a duration and one polynomial for each of the X, Y, Z and yaw coordinates of the trajectory.
Raw representation
The raw representation stores the coefficients of the polynomial describing the X coordinate of the trajectory first, starting with the constant term and ending with the 7th degree term, followed by the terms of the polynomials describing the Y, Z and yaw coordinates, and the duration of the segment itself in seconds. Each term and the duration is encoded as standard IEEE singleprecision floats, meaning that a single segment requires 132 bytes: 8x4 floats for the X, Y, Z and yaw components per trajectory segment, plus one additional float per segment to store its duration. Given that the default size of the trajectory memory is 4 Kbytes, you can only store 31 segments.
Compressed representation
The compressed representation was designed to be more spaceefficient than the raw representation while still trying to maintain almost the same degree of accuracy as the raw representation.
The general guidelines of this representation are as follows:

Durations are represented as milliseconds and are stored as signed 2byte integers.

Spatial coordinates (X, Y and Z) are represented as millimeters and are stored as signed 2byte integers, meaning that the maximum spatial volume that this representation can cover is roughly 64m x 64m x 64m, assuming that the origin is at the center.

Angles (for the yaw coordinate) are represented as 1/10th of degrees and are stored as signed 2byte integers.

Trajectory segments are described with varying degrees of Bézier curves, ranging from degree 0 (constant) to degree 7 (full 7D polynomial, as in the raw representation).
The compressed representation starts with the description of the starting point of the trajectory:
+++++
 X coordinate  Y coordinate  Z coordinate  Initial yaw 
+++++
This is then followed by data blocks describing the individual segments  one data block per segment:
++++++
 Segment 1 header  Segment 1 body  Segment 2 header  Segment 2 body  ... 
++++++
The header of a segment is three bytes long. The first byte describes how the X, Y, Z and yaw coordinates will be encoded in the body of the data block. The remaining two bytes contain the duration of the segment:
Bits 67 Bits 45 Bits 23 Bits 01 Byte 2 Byte 3
+++++ +++
 Yaw format  Z format  Y format  X format   Duration LSB  Duration MSB 
+++++ +++
For each of the X, Y, Z and yaw coordiates, there are two bits in the first byte
of the header. 00
means that the coordinate is constant throughout the
section (i.e. it is a zeroorder Bézier curve). 01
means that the coordinate
changes linearly (it is a Bézier curve of order 1). 10
means that the
coordinate changes according to a cubic Bézier curve. 11
means that the
coordinate changes according to a full 7th degree polynomial, expressed again
as a 7th degree Bézier curve.
For instance, a header containing 0x0a 0xd0 0x07
means that the segment is
2000 milliseconds long (because 0x07d0
= 2000), and it will have a constant
yaw and Z coordinate. Cubic Bézier curves will be used to describe the X and
Y coordinates of the segment. (This is because 0x0a
= 00 00 10 10
in
binary).
Note that the choice of Bézier curves means that we need to store not the raw coefficients of the corresponding polynomials (which may be anywhere in the full 4byte float range), but the coordinates of the control points of the Bézier curves, whose range is much more predictable and easier to represent with a fixed millimeterscale quantization.
The body of the segment is then simply a concatenation of the control points for the X, Y, Z and yaw coordinates, omitting the first control point because it is always the same as the last control point of the previous segment. (That’s why we needed to store the starting point of the trajectory separately).
+++++
 Control points for X  Control points for Y  Control points for Z  Control points for yaw 
+++++
Obviously, when the header specifies that a coordinate is constant, it means that we do not need to store the control points for that coordinate at all (there would be only a single control point, but we already know that it is the same as the last control point of the previous segment). For linear segments, we only need to store the end of the segment. For cubic Bézier segments, we need to store the first and second control point and the end of the segment. This can result in significant space savings if the trajectory consists mostly of linear segments and cubic Bézier curves, especially if the yaw does not change, or if the movement mostly occurs in the XY, XZ or YZ plane. Note that cubic Bézier curves are enough to ensure C0, C1 and C2 continuity (inposition, velocity and acceleration) for the trajectories.
Internally, whenever the highlevel commander starts processing a trajectory segment that is encoded using the compressed representation, it converts the Bézier curve back into its raw polynomial representation. It means that most of the codebase only needs to work with raw 7th degree polynomials.
A downside of the compressed representation is that it is hard to play the trajectory backwards. The current implementation does not support reverse traversal at all.