2

I am trying to optimize a set of equations simulating pendulums with springs between the initial vector and eachother, in R^3.

It is actually a system of matrices and vectors, but I have written it out like this to make it easier to see each equation separately. In this case there are just two pendulums. The q's(q[0:3] and q[3:6]) are the directions of the pendulums, and the p's the speed of the two points. In this case the length of the pendulums are also set to 1 so the q's actually represent the entire pendulums.

There is a force F pushing down in the z-direction on the final point, and because that point is defined as Q=l[0]*q[:3]+l[1]q[3:], the force is included in both of the z-equations of p.

The terms of k*num/denare the springs reacting to deflection of the angle. The terms are in the form m.acos(q1@q2)/m.sqrt(1-(q1@q2)**2), which I have had some issues with, considering they are 0/0 when the pendulums are in the same direction. Especially in the starting point they were previously both in the same direction as eachother and the initial vector. I do think I fixed it with the help from this answer, and also perturbed the x-values slightly so they don't actually start in a 0/0 position.

The pendulums are also constrained by the equations under "constraints" to be of length 1, this is also the reason of including the terms q*lam in the p-equations(this is how a constrained Hamiltonian system is structured).

However, now I am onto actually implementing the control/input force u. If I run the system without u, it solves and finds the solution and evolution of the differential equations. I have tried implementing the control input with different solvers. When I implement it with m.options.SOLVER 1 and 3 I get the max iterations error. And when I try with solver 2 I get the error:

 *** WARNING MESSAGE FROM SUBROUTINE MA27BD  *** INFO(1) = 3
     MATRIX IS SINGULAR. RANK=  135
 Problem with linear solver, INFO:            3

Below is the code used, I tried to make it as barebones as possible, but please ask if something is unclear!

from gekko import GEKKO
import numpy as np
#%% Functions
def get_M(n, d, ml, l):
    M = np.zeros((n*d, n*d))
    for i in range(n):
        for j in range(n):
            M[d*i:d*(i+1), d*j:d*(j+1)] = sum(ml[max(i, j):]) * l[i] * l[j] * np.eye(d)
    return M
def get_q(theta, phi):
    theta = theta*np.pi/180
    phi = phi*np.pi/180
    x = np.sin(theta)*np.cos(phi)
    y = np.sin(theta)*np.sin(phi)
    z = np.cos(theta)
    return np.array([x, y, z])
#%% Run
m = GEKKO()

n = 2; d = 3; #n number of pendulums, d dimension
k = np.ones(n)*1e3; ml = np.ones(n); l = np.ones(n)  #k list of spring constants, ml list of masses, l list of lengths
F = 1 # Force
N = 3 # Number of steps + 1
Minv = np.linalg.inv(get_M(n=n, d=d, ml=ml, l=l)) # Inverted mass matrix

m.time = np.linspace(0, 0.01, N)

q = m.Array(m.Var, n*d, value=0)
q[0].value, q[1].value, q[2].value = get_q(90, 90.001) # Initial values of pendulum 1
q[3].value, q[4].value, q[5].value = get_q(90, 89.999) # Initial values of pendulum 2

p = m.Array(m.Var, n*d, value=0)

lam = m.Array(m.Var, n, value=0)

sv = np.zeros(3)
sv[1] = 1 # [0., 1., 0.] Initial vector, first spring is between this and the first pendulum

# control variables
u = m.MV(value=0)
u.STATUS = 1

# q equations
m.Equations(q[i].dt() == (Minv@p)[i] for i in range(n*d))

# spring terms
s1 = m.Var(lb=0)
m.Equation(s1 >= 1-(q[:3]@sv)**2)
m.Minimize(s1)
num1 = m.Intermediate(m.acos(q[:3]@sv))
den1 = m.Intermediate(m.sqrt(s1))

s2 = m.Var(lb=0)
m.Equation(s2 >= 1-(q[:3]@q[3:])**2)
m.Minimize(s2)
num2 = m.Intermediate(m.acos(q[:3]@q[3:]))
den2 = m.Intermediate(m.sqrt(s2))

# p equations
m.Equation(p[0].dt() ==                                + k[1]*num2/den2*q[3] - q[0]*lam[0]) # x
m.Equation(p[1].dt() ==             k[0]*num1/den1     + k[1]*num2/den2*q[4] - q[1]*lam[0]) # y
m.Equation(p[2].dt() == - F +  u                       + k[1]*num2/den2*q[5] - q[2]*lam[0]) # z
m.Equation(p[3].dt() ==                                + k[1]*num2/den2*q[0] - q[3]*lam[1]) # x
m.Equation(p[4].dt() ==                                + k[1]*num2/den2*q[1] - q[4]*lam[1]) # y
m.Equation(p[5].dt() == - F +  u                       + k[1]*num2/den2*q[2] - q[5]*lam[1]) # z

# constraints
m.Equation(0.5 * (q[:3]@q[:3] - 1) == 0)
m.Equation(0.5 * (q[3:]@q[3:] - 1) == 0)

m.options.IMODE = 6
m.options.SOLVER = 3

m.Obj((q[5]-2*q[2])**2)
m.solve(disp=1)

Does anyone have any tips of why it's not working? I am quite new to gekko so there might be some glaring mistakes I'm overlooking. Thanks!

John Hedengren
  • 12,068
  • 1
  • 21
  • 25
Petter
  • 81
  • 4

1 Answers1

1

You mentioned:

If I run the system without u, it solves and finds the solution and evolution of the differential equations.

One potential method for finding an optimized solution is to use the simulation solution (u.STATUS=0) as an initial guess for the optimization (u.STATUS=1). Don't forget to set TIME_SHIFT=0 to prevent Gekko from updating the initial conditions.

u.STATUS=0
m.solve()

u.STATUS=1
m.options.TIME_SHIFT=0
m.solve()

I don't have a simulation version that solves successfully so here are a few additional suggestions in case that doesn't work:

  • Constrain the MV with realistic upper and lower bounds.
u = m.MV(value=0,lb=-1,ub=1)

Without bounds, the optimizer can try any guess value. With the m.acos() and m.sqrt() functions, this may cause the optimizer to diverge.

  • Increase the number of iterations or the solver tolerance (it is almost feasible at 1e-5) with:
m.options.MAX_ITER = 1000
m.options.RTOL = 1e-5
m.options.OTOL = 1e-5
  • Remove any potential zero denominators such as:
m.Equation(den2*p[0].dt() == k[1]*num2*q[3] - den2*q[0]*lam[0]) # x
m.Equation(den2*den1*p[1].dt() == k[0]*num1*den2 + den1*k[1]*num2*q[4] - den1*den2*q[1]*lam[0]) # y
m.Equation(den2*p[2].dt() == -F*den2+u*den2 + k[1]*num2*q[5] - den2*q[2]*lam[0]) # z
m.Equation(den2*p[3].dt() == k[1]*num2*q[0] - den2*q[3]*lam[1]) # x
m.Equation(den2*p[4].dt() == k[1]*num2*q[1] - den2*q[4]*lam[1]) # y
m.Equation(den2*p[5].dt() == - den2*F+den2*u + k[1]*num2*q[2] - den2*q[5]*lam[1]) # z

You mentioned this strategy in your question so it looks like you've already tried this. Here is a paper that discusses additional initialization strategies:

Safdarnejad, S.M., Hedengren, J.D., Lewis, N.R., Haseltine, E., Initialization Strategies for Optimization of Dynamic Systems, Computers and Chemical Engineering, 2015, Vol. 78, pp. 39-50, DOI: 10.1016/j.compchemeng.2015.04.016.

Your simulation solution is likely the best way to initialize the optimization (with bounds on u).

John Hedengren
  • 12,068
  • 1
  • 21
  • 25