A Python library to sample polynomial trajectories in a curvilinear coordinate system and evaluate them for feasibility and cost.
Binary wheels are automatically built for releases. As a result,
you can install a Frenetix release by simply running pip install frenetix.
Frenetix is designed to be built locally with minimal user intervention.
Apart from a Python environment of your choice,
you only need a C++ compiler as well as a Fortran compiler.
gfortran is commonly available on Linux distributions.
To build Frenetix locally, pip install . should be sufficient.
The fundamental operating principle of Frenetix is based on the following paper:
M. Werling, J. Ziegler, S. Kammel and S. Thrun, "Optimal trajectory generation for dynamic street scenarios in a Frenét Frame," 2010 IEEE International Conference on Robotics and Automation, Anchorage, AK, USA, 2010, pp. 987-993, doi: 10.1109/ROBOT.2010.5509799.
In short, planning using Frenetix consists of these steps:
- Specifying which trajectories should be sampled using a sampling matrix, consisting of longitudinal, lateral and temporal initial and final values
- Generating the trajectories
- Converting the trajectories from curvilinear to cartesian space
- Evaluating feasability functions
- Evaluating cost functions
- Retrieving the trajectories, possibly sorted by feasability and cost
First create a trajectory handler object, specifying the desired time step dt:
import frenetix
handler = frenetix.TrajectoryHandler(dt=self.config_plan.planning.dt)Then create a special curvilinear coordinate system object -
this uses the same curvilinear coordinate system as the
CommonRoad Drivability Checker under the hood, but
we currently require this wrapper to provide some additional functions.
reference_path should be a sequence of points describing the reference path. Please make sure that the points are neither too far apart nor too close together, experience suggests that keeping the points about 1 meter apart is a good idea.
coordinate_system_cpp = frenetix.CoordinateSystemWrapper(reference_path)
You always need to add the FillCoordinates function
which converts the polynomial trajectories (in curvilinear coordinates) to usable cartesian coordinates.
handler.add_function(
frenetix.trajectory_functions.FillCoordinates(
lowVelocityMode,
initialOrientation,
coordinateSystem=coordinate_system_cpp,
horizon=horizon
)
)lowVelocityModeenables low velocity mode as described in the paper mentioned above.initialOrientationis the initial global orientation of the ego vehiclehorizonis the sampling horizon
You can then add feasability functions like this:
import frenetix.trajectory_functions.feasability_functions as ff
handler.add_feasability_function(
ff.CheckYawRateConstraint(
deltaMax,
wheelbase,
wholeTrajectory
)
)The following feasability functions are available:
-
CheckYawRateConstraint(deltaMax, wheelbase, wholeTrajectory): Checks whether yaw rate is feasible under the Kinematic Bicycle model, based on maximum steering angledeltaMaxandwheelbase.wholeTrajectoryis a bool that specifies whether the feasability check should include the enlarged parts of the trajectory if trajectory enlargement is enabled. -
CheckAccelerationConstraint(switchingVelocity, maxAcceleration, wholeTrajectory): Checks whether acceleration is feasible. -
CheckCurvatureConstraint(deltaMax, wheelbase, wholeTrajectory) -
CheckCurvatureRateConstraint(wheelbase, velocityDeltaMax, wholeTrajectory)
You can add most cost functions like this:
import frenetix.trajectory_functions.cost_functions as cf
handler.add_cost_function(
cf.CalculateAccelerationCost(name, cost_weight)
)The name can later be used to retrieve the cost value from a sampled trajectory.
The following cost functions follow the signature above:
CalculateAccelerationCostCalculateJerkCostCalculateLateralJerkCostCalculateLongitudinalJerkCostCalculateOrientationOffsetCostCalculateLaneCenterOffsetCostCalculateDistanceToReferencePathCost
The prediction cost function needs additional steps:
handler.add_cost_function(
cf.CalculateCollisionProbabilityFast(
name,
weight,
predictions,
vehicle_length,
vehicle_width,
wb_rear_axle
)
)vehicle_length/vehicle_widthspecify ego vehicle dimensionswb_rear_axleshould be the distance from the center reference point of the vehicle to the rear axlepredictionsexpects a list offrenetix.PredictedObjects. They can be built usingPredictedObject(id, predicted_path, length, width).predicted_pathshould be a list offrenetix.PoseWithCovarianceobjects, which are derived from the ROS geometry message of the same name. They can be built usingPoseWithCovariance(position, orientation, covariance_matrix), wherepositionis a three-dimensional position,orientationis a quaternion andcovariance_matrixis a 6x6 matrix.
CalculateDistanceToObstacleCost(name, weight, obstacle_positions) expects a list of obstacle
positions.
Finally, you can generate and evaluate trajectories like this:
# Reset state
handler.reset_Trajectories()
# Generate trajectories
handler.generate_trajectories(sampling_matrix, low_velocity_mode)
# Run concurrent feasability and cost function evaluation
handler.evaluate_all_current_functions_concurrent(True)The trajectories can then be retrieved:
for trajectory in self.handler.get_sorted_trajectories():
# Some examples of what you could do:
# Skip iterating over infeasible trajectories
if not trajectory.feasible:
break
print(trajectory.cost)
# Print longitudinal coordinates
print(trajectory.curvilinear.s)
# Print cost and feasability values
print(trajectory.costMap)
print(trajectory.feasabilityMap)
