예제 #1
0
def test_optimal_logging(capsys):
    """Test logging functions (mainly for code coverage)"""
    sys = ct.ss2io(ct.ss(np.eye(2), np.eye(2), np.eye(2), 0, 1))

    # Set up the optimal control problem
    cost = opt.quadratic_cost(sys, 1, 1)
    state_constraint = opt.state_range_constraint(sys, [-np.inf, 1], [10, 1])
    input_constraint = opt.input_range_constraint(sys, [-100, -100],
                                                  [100, 100])
    time = np.arange(0, 3, 1)
    x0 = [-1, 1]

    # Solve it, with logging turned on (with warning due to mixed constraints)
    with pytest.warns(sp.optimize.optimize.OptimizeWarning,
                      match="Equality and inequality .* same element"):
        res = opt.solve_ocp(sys,
                            time,
                            x0,
                            cost,
                            input_constraint,
                            terminal_cost=cost,
                            terminal_constraints=state_constraint,
                            log=True)

    # Make sure the output has info available only with logging turned on
    captured = capsys.readouterr()
    assert captured.out.find("process time") != -1
예제 #2
0
def time_steering_bezier_basis(nbasis, ntimes):
    # Set up costs and constriants
    Q = np.diag([.1, 10, .1])  # keep lateral error low
    R = np.diag([1, 1])  # minimize applied inputs
    cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf)
    constraints = [opt.input_range_constraint(vehicle, [0, -0.1], [20, 0.1])]
    terminal = [opt.state_range_constraint(vehicle, xf, xf)]

    # Set up horizon
    horizon = np.linspace(0, Tf, ntimes, endpoint=True)

    # Set up the optimal control problem
    res = opt.solve_ocp(
        vehicle,
        horizon,
        x0,
        cost,
        constraints,
        terminal_constraints=terminal,
        initial_guess=bend_left,
        basis=flat.BezierFamily(nbasis, T=Tf),
        # solve_ivp_kwargs={'atol': 1e-4, 'rtol': 1e-2},
        minimize_method='trust-constr',
        minimize_options={'finite_diff_rel_step': 0.01},
        # minimize_method='SLSQP', minimize_options={'eps': 0.01},
        return_states=True,
        print_summary=False)
    t, u, x = res.time, res.inputs, res.states

    # Make sure we found a valid solution
    assert res.success
예제 #3
0
def time_steering_terminal_constraint(integrator_name, minimizer_name):
    # Get the integrator and minimizer parameters to use
    integrator = integrator_table[integrator_name]
    minimizer = minimizer_table[minimizer_name]

    # Input cost and terminal constraints
    R = np.diag([1, 1])  # minimize applied inputs
    cost = opt.quadratic_cost(vehicle, np.zeros((3, 3)), R, u0=uf)
    constraints = [opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1])]
    terminal = [opt.state_range_constraint(vehicle, xf, xf)]

    res = opt.solve_ocp(
        vehicle,
        horizon,
        x0,
        cost,
        constraints,
        terminal_constraints=terminal,
        initial_guess=bend_left,
        log=False,
        solve_ivp_method=integrator[0],
        solve_ivp_kwargs=integrator[1],
        minimize_method=minimizer[0],
        minimize_options=minimizer[1],
    )
    # Only count this as a benchmark if we converged
    assert res.success
예제 #4
0
def test_equality_constraints():
    """Test out the ability to handle equality constraints"""
    # Create the system (double integrator, continuous time)
    sys = ct.ss2io(ct.ss(np.zeros((2, 2)), np.eye(2), np.eye(2), 0))

    # Shortest path to a point is a line
    Q = np.zeros((2, 2))
    R = np.eye(2)
    cost = opt.quadratic_cost(sys, Q, R)

    # Set up the terminal constraint to be the origin
    final_point = [opt.state_range_constraint(sys, [0, 0], [0, 0])]

    # Create the optimal control problem
    time = np.arange(0, 3, 1)
    optctrl = opt.OptimalControlProblem(sys,
                                        time,
                                        cost,
                                        terminal_constraints=final_point)

    # Find a path to the origin
    x0 = np.array([4, 3])
    res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
    t, u1, x1 = res.time, res.inputs, res.states

    # Bug prior to SciPy 1.6 will result in incorrect results
    if NumpyVersion(sp.__version__) < '1.6.0':
        pytest.xfail("SciPy 1.6 or higher required")

    np.testing.assert_almost_equal(x1[:, -1], 0, decimal=4)

    # Set up terminal constraints as a nonlinear constraint
    def final_point_eval(x, u):
        return x

    final_point = [(sp.optimize.NonlinearConstraint, final_point_eval, [0, 0],
                    [0, 0])]

    optctrl = opt.OptimalControlProblem(sys,
                                        time,
                                        cost,
                                        terminal_constraints=final_point)

    # Find a path to the origin
    x0 = np.array([4, 3])
    res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
    t, u2, x2 = res.time, res.inputs, res.states
    np.testing.assert_almost_equal(x2[:, -1], 0, decimal=4)
    np.testing.assert_almost_equal(u1, u2)
    np.testing.assert_almost_equal(x1, x2)

    # Try passing and unknown constraint type
    final_point = [(None, final_point_eval, [0, 0], [0, 0])]
    with pytest.raises(TypeError, match="unknown constraint type"):
        optctrl = opt.OptimalControlProblem(sys,
                                            time,
                                            cost,
                                            terminal_constraints=final_point)
        res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
예제 #5
0
    def test_flat_cost_constr(self):
        # Double integrator system
        sys = ct.ss([[0, 1], [0, 0]], [[0], [1]], [[1, 0]], 0)
        flat_sys = fs.LinearFlatSystem(sys)

        # Define the endpoints of the trajectory
        x0 = [1, 0]
        u0 = [0]
        xf = [0, 0]
        uf = [0]
        Tf = 10
        T = np.linspace(0, Tf, 500)

        # Find trajectory between initial and final conditions
        traj = fs.point_to_point(flat_sys,
                                 Tf,
                                 x0,
                                 u0,
                                 xf,
                                 uf,
                                 basis=fs.PolyFamily(8))
        x, u = traj.eval(T)

        np.testing.assert_array_almost_equal(x0, x[:, 0])
        np.testing.assert_array_almost_equal(u0, u[:, 0])
        np.testing.assert_array_almost_equal(xf, x[:, -1])
        np.testing.assert_array_almost_equal(uf, u[:, -1])

        # Solve with a cost function
        timepts = np.linspace(0, Tf, 10)
        cost_fcn = opt.quadratic_cost(flat_sys,
                                      np.diag([0, 0]),
                                      1,
                                      x0=xf,
                                      u0=uf)

        traj_cost = fs.point_to_point(
            flat_sys,
            timepts,
            x0,
            u0,
            xf,
            uf,
            cost=cost_fcn,
            basis=fs.PolyFamily(8),
            # initial_guess='lstsq',
            # minimize_kwargs={'method': 'trust-constr'}
        )

        # Verify that the trajectory computation is correct
        x_cost, u_cost = traj_cost.eval(T)
        np.testing.assert_array_almost_equal(x0, x_cost[:, 0])
        np.testing.assert_array_almost_equal(u0, u_cost[:, 0])
        np.testing.assert_array_almost_equal(xf, x_cost[:, -1])
        np.testing.assert_array_almost_equal(uf, u_cost[:, -1])

        # Make sure that we got a different answer than before
        assert np.any(np.abs(x - x_cost) > 0.1)

        # Re-solve with constraint on the y deviation
        lb, ub = [-2, -0.1], [2, 0]
        lb, ub = [-2, np.min(x_cost[1]) * 0.95], [2, 1]
        constraints = [opt.state_range_constraint(flat_sys, lb, ub)]

        # Make sure that the previous solution violated at least one constraint
        assert np.any(x_cost[0, :] < lb[0]) or np.any(x_cost[0, :] > ub[0]) \
            or np.any(x_cost[1, :] < lb[1]) or np.any(x_cost[1, :] > ub[1])

        traj_const = fs.point_to_point(
            flat_sys,
            timepts,
            x0,
            u0,
            xf,
            uf,
            cost=cost_fcn,
            constraints=constraints,
            basis=fs.PolyFamily(8),
        )

        # Verify that the trajectory computation is correct
        x_const, u_const = traj_const.eval(T)
        np.testing.assert_array_almost_equal(x0, x_const[:, 0])
        np.testing.assert_array_almost_equal(u0, u_const[:, 0])
        np.testing.assert_array_almost_equal(xf, x_const[:, -1])
        np.testing.assert_array_almost_equal(uf, u_const[:, -1])

        # Make sure that the solution respects the bounds (with some slop)
        for i in range(x_const.shape[0]):
            assert np.all(x_const[i] >= lb[i] * 1.02)
            assert np.all(x_const[i] <= ub[i] * 1.02)

        # Solve the same problem with a nonlinear constraint type
        nl_constraints = [(sp.optimize.NonlinearConstraint, lambda x, u: x, lb,
                           ub)]
        traj_nlconst = fs.point_to_point(
            flat_sys,
            timepts,
            x0,
            u0,
            xf,
            uf,
            cost=cost_fcn,
            constraints=nl_constraints,
            basis=fs.PolyFamily(8),
        )
        x_nlconst, u_nlconst = traj_nlconst.eval(T)
        np.testing.assert_almost_equal(x_const, x_nlconst)
        np.testing.assert_almost_equal(u_const, u_nlconst)
예제 #6
0
def test_terminal_constraints(sys_args):
    """Test out the ability to handle terminal constraints"""
    # Create the system
    sys = ct.ss2io(ct.ss(*sys_args))

    # Shortest path to a point is a line
    Q = np.zeros((2, 2))
    R = np.eye(2)
    cost = opt.quadratic_cost(sys, Q, R)

    # Set up the terminal constraint to be the origin
    final_point = [opt.state_range_constraint(sys, [0, 0], [0, 0])]

    # Create the optimal control problem
    time = np.arange(0, 3, 1)
    optctrl = opt.OptimalControlProblem(
        sys, time, cost, terminal_constraints=final_point)

    # Find a path to the origin
    x0 = np.array([4, 3])
    res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
    t, u1, x1 = res.time, res.inputs, res.states

    # Bug prior to SciPy 1.6 will result in incorrect results
    if NumpyVersion(sp.__version__) < '1.6.0':
        pytest.xfail("SciPy 1.6 or higher required")

    np.testing.assert_almost_equal(x1[:,-1], 0, decimal=4)

    # Make sure it is a straight line
    Tf = time[-1]
    if ct.isctime(sys):
        # Continuous time is not that accurate on the input, so just skip test
        pass
    else:
        # Final point doesn't affect cost => don't need to test
        np.testing.assert_almost_equal(
            u1[:, 0:-1],
            np.kron((-x0/Tf).reshape((2, 1)), np.ones(time.shape))[:, 0:-1])
    np.testing.assert_allclose(
        x1, np.kron(x0.reshape((2, 1)), time[::-1]/Tf), atol=0.1, rtol=0.01)

    # Re-run using initial guess = optional and make sure nothing changes
    res = optctrl.compute_trajectory(x0, initial_guess=u1)
    np.testing.assert_almost_equal(res.inputs, u1)

    # Re-run using a basis function and see if we get the same answer
    res = opt.solve_ocp(sys, time, x0, cost, terminal_constraints=final_point,
                       basis=flat.BezierFamily(4, Tf))
    np.testing.assert_almost_equal(res.inputs, u1, decimal=2)

    # Impose some cost on the state, which should change the path
    Q = np.eye(2)
    R = np.eye(2) * 0.1
    cost = opt.quadratic_cost(sys, Q, R)
    optctrl = opt.OptimalControlProblem(
        sys, time, cost, terminal_constraints=final_point)

    # Turn off warning messages, since we sometimes don't get convergence
    with warnings.catch_warnings():
        warnings.filterwarnings(
            "ignore", message="unable to solve", category=UserWarning)
        # Find a path to the origin
        res = optctrl.compute_trajectory(
            x0, squeeze=True, return_x=True, initial_guess=u1)
        t, u2, x2 = res.time, res.inputs, res.states

        # Not all configurations are able to converge (?)
        if res.success:
            np.testing.assert_almost_equal(x2[:,-1], 0)

            # Make sure that it is *not* a straight line path
            assert np.any(np.abs(x2 - x1) > 0.1)
            assert np.any(np.abs(u2) > 1)       # Make sure next test is useful

        # Add some bounds on the inputs
        constraints = [opt.input_range_constraint(sys, [-1, -1], [1, 1])]
        optctrl = opt.OptimalControlProblem(
            sys, time, cost, constraints, terminal_constraints=final_point)
        res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
        t, u3, x3 = res.time, res.inputs, res.states

        # Check the answers only if we converged
        if res.success:
            np.testing.assert_almost_equal(x3[:,-1], 0, decimal=4)

            # Make sure we got a new path and didn't violate the constraints
            assert np.any(np.abs(x3 - x1) > 0.1)
            np.testing.assert_array_less(np.abs(u3), 1 + 1e-6)

    # Make sure that infeasible problems are handled sensibly
    x0 = np.array([10, 3])
    with pytest.warns(UserWarning, match="unable to solve"):
        res = optctrl.compute_trajectory(x0, squeeze=True, return_x=True)
        assert not res.success
plot_results(t2, y2, u2, figure=2, yf=xf[0:2])

#
# Approach 3: terminal constraints
#
# We can also remove the cost function on the state and replace it
# with a terminal *constraint* on the state.  If a solution is found,
# it guarantees we get to exactly the final state.
#
print("Approach 3: terminal constraints")

# Input cost and terminal constraints
R = np.diag([1, 1])  # minimize applied inputs
cost3 = opt.quadratic_cost(vehicle, np.zeros((3, 3)), R, u0=uf)
constraints = [opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1])]
terminal = [opt.state_range_constraint(vehicle, xf, xf)]

# Reset logging to its default values
logging.basicConfig(level=logging.DEBUG,
                    filename="./steering-terminal_constraint.log",
                    filemode='w',
                    force=True)

# Compute the optimal control
start_time = time.process_time()
result3 = opt.solve_ocp(
    vehicle,
    horizon,
    x0,
    cost3,
    constraints,