Brachistochrone Problem#

Mathematical Formulation#

Problem Statement#

Find the optimal control angle θ(t) that minimizes the transit time between two specified points under gravitational acceleration.

J=tf

Subject to the dynamic constraints:

dxdt=vsinθ
dydt=vcosθ
dvdt=gcosθ

Boundary Conditions#

  • Initial conditions: x(0)=0, y(0)=0, v(0)=0

  • Final conditions: x(tf)=2.0, y(tf)=2.0, v(tf) = free

  • Control bounds: 0θπ2

Physical Parameters#

  • Gravitational acceleration: g=9.81 m/s²

State Variables#

  • x(t): Horizontal position (m)

  • y(t): Vertical position (m)

  • v(t): Speed magnitude (m/s)

Control Variable#

  • θ(t): Path angle with respect to the vertical (rad)

Running This Example#

cd examples/brachistochrone
python brachistochrone.py

Code Implementation#

examples/brachistochrone/brachistochrone.py#
  1import casadi as ca
  2import numpy as np
  3
  4import maptor as mtor
  5
  6
  7# Problem setup
  8problem = mtor.Problem("Brachistochrone Problem")
  9phase = problem.set_phase(1)
 10
 11# Variables
 12t = phase.time(initial=0.0)
 13x = phase.state("x", initial=0.0, final=2.0)
 14y = phase.state("y", initial=0.0, final=2.0)
 15v = phase.state("v", initial=0.0)
 16theta = phase.control("u", boundary=(0.0, np.pi / 2))
 17
 18# Dynamics
 19g = 9.81
 20phase.dynamics({x: v * ca.sin(theta), y: v * ca.cos(theta), v: g * ca.cos(theta)})
 21
 22# Objective
 23problem.minimize(t.final)
 24
 25# Mesh and guess
 26phase.mesh([6], [-1.0, 1.0])
 27
 28states_guess = [
 29    # Interval 1: 7 state points
 30    [
 31        [0.0, 1.0, 2.0, 3.0, 4.0, 4.5, 5.0],  # x
 32        [10.0, 9.5, 9.0, 8.5, 8.0, 7.5, 7.0],  # y
 33        [0.0, 1.0, 2.0, 2.5, 3.0, 3.5, 4.0],  # v
 34    ]
 35]
 36
 37controls_guess = (
 38    [
 39        # Interval 1: 6 control points
 40        [0.8, 0.7, 0.6, 0.5, 0.4, 0.3]
 41    ],
 42)
 43
 44
 45phase.guess(
 46    states=states_guess,
 47    controls=controls_guess,
 48    terminal_time=1.0,
 49)
 50# Solve with adaptive mesh
 51solution = mtor.solve_adaptive(
 52    problem,
 53    error_tolerance=1e-6,
 54    max_iterations=30,
 55    min_polynomial_degree=3,
 56    max_polynomial_degree=12,
 57    nlp_options={"ipopt.print_level": 0, "ipopt.max_iter": 500},
 58)
 59
 60# Results
 61if solution.status["success"]:
 62    print(f"Adaptive objective: {solution.status['objective']:.9f}")
 63    solution.plot()
 64
 65    # Compare with fixed mesh
 66    phase.mesh([6], [-1.0, 1.0])
 67
 68    states_guess = [
 69        # Interval 1: 7 state points
 70        [
 71            [0.0, 1.0, 2.0, 3.0, 4.0, 4.5, 5.0],  # x
 72            [10.0, 9.5, 9.0, 8.5, 8.0, 7.5, 7.0],  # y
 73            [0.0, 1.0, 2.0, 2.5, 3.0, 3.5, 4.0],  # v
 74        ]
 75    ]
 76
 77    controls_guess = (
 78        [
 79            # Interval 1: 6 control points
 80            [0.8, 0.7, 0.6, 0.5, 0.4, 0.3]
 81        ],
 82    )
 83
 84    phase.guess(
 85        states=states_guess,
 86        controls=controls_guess,
 87        terminal_time=1.0,
 88    )
 89
 90    fixed_solution = mtor.solve_fixed_mesh(
 91        problem, nlp_options={"ipopt.print_level": 0, "ipopt.max_iter": 500}
 92    )
 93
 94    if fixed_solution.status["success"]:
 95        print(f"Fixed mesh objective: {fixed_solution.status['objective']:.9f}")
 96        print(
 97            f"Difference: {abs(solution.status['objective'] - fixed_solution.status['objective']):.2e}"
 98        )
 99        fixed_solution.plot()
100    else:
101        print(f"Fixed mesh failed: {fixed_solution.status['message']}")
102
103else:
104    print(f"Failed: {solution.status['message']}")