Source code for rtd.planner.trajopt.ScipyOptimizationEngine

from rtd.planner.trajopt import OptimizationEngine, TrajOptProps
from scipy.optimize import minimize
import numpy as np
from typing import Callable
from rtd.util.mixins.Typings import Vecnp



[docs]class ScipyOptimizationEngine(OptimizationEngine): ''' Optimization Engine based on scipy.optimize.fsolve '''
[docs] def __init__(self, trajOptProps: TrajOptProps, **options): # initialize base classes OptimizationEngine.__init__(self) # initialize other attributes self.trajOptProps: TrajOptProps = trajOptProps
[docs] def performOptimization(self, initialGuess: Vecnp, objectiveCallback: Callable, constraintCallback: Callable, bounds: dict) -> tuple[bool, Vecnp, float]: ''' Use scipy solve to perform the optimization Arguments: initialGuess: An initial guess Vecnp used for the optimization. May not be the correct size objectiveCallback: A callback for the objective function of this specific optimization constraintCallback: A callback for the nonlinear constraints, where the return time is expected to be [c, ceq, gc, gceq]. bounds: A dict containing input and output bounds Returns: (success: bool, parameters: Vecnp, cost: float): `success` is if the optimization was successful or didn't time out. `parameters` are the trajectory parameters to use. `cost` is the final cost for the parameters found ''' # how many extra variables we need n_remainder = np.size(bounds["param_limits"], 0) - len(initialGuess) # get bounds lb = bounds["param_limits"][:,0] ub = bounds["param_limits"][:,1] # generate random values if requested, otherwise zeros for any # thing not in our initial guess if self.trajOptProps.randomInit: initial_extra = np.random.uniform(lb[-n_remainder:], ub[-n_remainder:]) else: initial_extra = np.zeros(n_remainder) # build initial guess initialGuess = np.concatenate((initialGuess, initial_extra)) # optimization call ineqConstraint = lambda k: -constraintCallback(k)[0] eqConstraint = lambda k: constraintCallback(k)[1] ineqConstraintJac = lambda k: -constraintCallback(k)[2] eqConstraintJac = lambda k: constraintCallback(k)[3] result = minimize( fun=objectiveCallback, x0=initialGuess, method='SLSQP', #jac=True, # use second return of fun as jacobean bounds=bounds["param_limits"], constraints = [ { "type": 'eq', "fun": eqConstraint, "jac": eqConstraintJac, }, { "type": 'ineq', "fun": ineqConstraint, "jac": ineqConstraintJac, }, ], ) return (result.success, result.x, result.fun)