Main Content

Define Trajectory Using Positions and Ground Speed

This example shows how to define a trajectory using the waypointTrajectory object, especially when the time-of-arrival information is not available. This example also shows how to set the initial time before the trajectory starts, the wait time at a specific waypoint, and the jerk limit that controls the maximum jerk allowed in the trajectory.

Overview

The waypointTrajectory object describes the motion of an object through a series of waypoints. The waypointTrajectory object is best used for describing an object that moves primarily in the horizontal x-y plane with a gradual climb or descent in the vertical direction. You can specify a waypointTrajectory object with and without the time-of-arrival information. With the time-of-arrival input, the object interpolates between the waypoints to generate a smooth trajectory. You can optionally specify the velocity at each waypoint in this case. Without the time-of-arrival input, you must specify the ground speed or velocity at each waypoint so that the object can determine the trajectory and times-of-arrival.

Specify Trajectory Lifetime (Start, Stop, and Wait)

You can specify the waypointTrajectory object to let it start and end at specific times. The trajectory reports valid trajectory states for any time within the time interval and reports not-a-number (NaN) values outside of that time interval. Utilizing this, you can define trajectories that last for only a small duration of time relative to other trajectories. You can also specify pause or wait time at specific waypoints. The trajectory remains at the same waypoint while in a wait state.

Based on whether the time-of-arrival information is available, you specify trajectory lifetime and pause durations in different ways.

Known Time-of-Arrival

When time-of-arrival information is known at each waypoint, you can specify it via the TimeOfArrival property.

In this case, the TimeOfArrival property defines the lifetime of the trajectory. As mentioned, the trajectory only reports valid values when you query the trajectory states, such as position and orientation, within the trajectory lifetime. Otherwise, the trajectory returns those values as NaNs.

To specify waiting at a specific position, repeat the position at two consecutive waypoints with different times of arrival. If you additionally specify velocities or ground speeds for these waypoints, the velocities or ground speeds must be zero.

In the following, you define a trajectory that starts at 2.6 seconds and ends at 28.3 seconds. The trajectory waits at location (41.1, 19.4, 0) from 11.5 seconds to 12.5 seconds. You achieve this by specifying the same coordinates for two consecutive waypoints at 11.5 second and 12.5 seconds.

waypoints1 = [49.6 -34.7  0
              47.1 -26.8  0
              41.5 -19.4  0
              41.5 -19.4  0
              28.7  -7.2  0
              20.2   1.0  0];
timeOfArrival1 = [2.6; 5.4; 11.5; 12.5; 24.3; 28.3];
traj1 = waypointTrajectory(waypoints1,timeOfArrival1);

Next, plot the waypoints and trajectory from 0 to 30 seconds. The plot function ignores NaN values.

sampleTimes1 = linspace(0,30,1000);
[position1, orientation1, velocity1] = lookupPose(traj1,sampleTimes1);
plot(waypoints1(:,1),waypoints1(:,2),"o", ...
     position1(:,1), position1(:,2),".");
xlabel("X (m)");
ylabel("Y (m)");
axis equal
legend({"Waypoints","Position"})

You can also plot the magnitude of the velocity (speed) as a function of time. From the results, the trajectory starts at 2.6 seconds and ends at 28 seconds. The trajectory position does not change from 11.5 to 12.5 seconds.

plot(sampleTimes1,vecnorm(velocity1,2,2));
xlabel("Time (s)");
ylabel("Speed (m/s)");

Unknown Time-of-Arrival

As of R2023a, the waypointTrajectory can automatically compute the time of arrival information if the velocity information is specified for each waypoint (by using the GroundSpeed property or the Velocity property). After the waypointTrajectory object is created, you can inspect the computed times-of-arrival stored in the TimeOfArrival property.

To let the trajectory start at a specific time, specify the start time in the InitialTime property. As a consequence, the trajectory also delays the trajectory stop time by the specified initial time. The trajectory reports NaN values outside the lifetime of the trajectory.

To let the trajectory wait at specific waypoints, specify nonzero values in the corresponding entries in the WaitTime property. Meanwhile, make sure that you specify the velocities at these points as zero.

In the following, you define a waypointTrajectory object using ground speed and specify the WaitTime property so that the trajectory waits at the third waypoint for one second.

waypoints2 = [49.6 -34.7  0
              47.1 -26.8  0
              41.5 -19.4  0
              28.7  -7.2  0
              20.2   1.0  0];
groundSpeed2 = [3; 3; 0; 3; 3];
waitTime2 = [0; 0; 1; 0; 0];
initialTime2 = timeOfArrival1(1);
traj2 = waypointTrajectory(waypoints2,GroundSpeed= groundSpeed2, ...
    WaitTime=waitTime2,InitialTime=initialTime2);

As previously, plot the waypoints and the positions. Note that the plot function ignores NaN values.

sampleTimes2 = linspace(0,30,1000);
[position2, orientation2, velocity2, acceleration2] = lookupPose(traj2, sampleTimes2);
plot(waypoints2(:,1),waypoints2(:,2),"o", ...
     position2(:,1), position2(:,2),".");
xlabel("X (m)");
ylabel("Y (m)");
axis equal
legend({"Waypoints","Position"})

Next, plot the speed as a function of time.

speed2 = vecnorm(velocity2,2,2);
plot(sampleTimes2, speed2);
xlabel("Time (s)");
ylabel("Speed (m/s)");

From the results, the trajectory starts at 2.6 seconds and ends at 28 seconds. The trajectory does not change from 11.5 to 12.5 seconds. Additionally, plot the acceleration in the forward direction of the motion as a function of time.

forward2 = rotatepoint(orientation2,repmat([1 0 0],size(velocity2,1),1));
forwardAcceleration2 = dot(acceleration2,forward2,2);
plot(sampleTimes2, forwardAcceleration2);
xlabel("Time (s)");
ylabel("Acceleration (m/s^2)");

Observe that when using groundspeed (without time-of-arrival) the trajectory uses a piece-wise constant acceleration profile. The trajectory starts with no acceleration (constant speed) then decelerates after the second waypoint until it stops and pauses at 11.5 seconds. Then, it accelerates again at 12.5 seconds until the penultimate waypoint, where it resumes a constant speed.

Trajectory Smoothness

The waypointTrajectory has built-in interpolants that govern how quickly it traverses the path specified through the waypoints. The exact interpolants being used depends on whether the time-of-arrival information is available and other factors.

Known Time-of-Arrival

When time-of-arrival is known, the trajectory uses a shape preserving piecewise cubic spline to calculate the distance along the path of the curve as a function of time. If the groundspeed information is also known, the trajectory uses cubic Hermite interpolation, which allows a finer control of the speed of waypoints through which the path is traversed. You need to make sure that any groundspeed information provided to the waypointTrajectory object is consistent with the overall path and time of arrival information. Otherwise, the instantaneous groundspeeds used between waypoints may grossly overshoot or undershoot the desired speeds.

Unknown Time-of-Arrival

When the time of arrival information is unknown, the waypointTrajectory object uses piecewise constant acceleration between waypoints based on the specified groundspeed or velocity. As a result, the groundspeed reaches extrema at the waypoints, guaranteeing that the velocity will change in a monotonically increasing or decreasing fashion between waypoints.

The waypointTrajectory object also allows you to specify the limit of the jerk, which is the time derivative of the acceleration. In automotive applications, it is crucial to restrain the jerk limit over the course of the trajectory, because excessive jerks can result in an uncomfortable ride or even cause body injury for passengers. For example, passengers usually feel extremely uncomfortable when the jerk magnitude is beyond 0.9 m/s³.

If the JerkLimit property is specified, the trajectory produces a trapezoidal acceleration profile. Otherwise, it follows a piecewise-constant acceleration profile with a discontinuity in the acceleration value at the waypoints as shown in the last section.

Use the previous trajectory but set the jerk limit of the trajectory to 0.4 m/s³.

waypoints3 = waypoints2;
groundSpeed3 = groundSpeed2;
jerkLimit = 0.4;
waitTime3 = waitTime2;
initialTime3 = timeOfArrival1(1);
traj3 = waypointTrajectory(waypoints3,GroundSpeed=groundSpeed3,WaitTime=waitTime3, ...
    InitialTime=initialTime3,JerkLimit=jerkLimit);

To investigate the acceleration profile, plot the acceleration in the forward direction of motion.

sampleTimes3 = linspace(0,20,1000);
[position3, orientation3, velocity3, acceleration3] = lookupPose(traj3, sampleTimes3);
forward3 = rotatepoint(orientation3,repmat([1 0 0],size(velocity3,1),1));
forwardAcceleration3 = dot(acceleration3,forward3,2);
plot(sampleTimes3, forwardAcceleration3);
xlabel("Time (s)");
ylabel("Acceleration (m/s^2)");

From the results, the trajectory produces a trapezoidal profile, which avoids instantaneous discontinuities in acceleration. Note that it is important to use realistic values for velocities of the waypoints. If the trajectory returns an error when you adjust the jerk limit, it is possible for the trajectory to return error because the jerk limit is not achievable given the velocity or groundspeed. In this case, you can try adding more waypoints or reducing the difference in speeds between the problematic waypoints.

Summary

In this example, you learned how to construct a waypointTrajectory object with and without time-of-arrival information. You also explored ways to control trajectory lifetime and its smoothness.