Элемент управления Python не может имитировать статическую нелинейность

Я новичок в использовании python для разработки элементов управления (из MATLAB). Он кажется достаточно способным для решения основных задач, но более сложные вещи становятся громоздкими. Я пытаюсь создать систему обратной связи для управления ориентацией спутника, используя взаимосвязанные системы ввода-вывода. Нелинейные системы создаются с помощью&определения и. Затем подключился с помощью . Проблемы возникают при попытке смоделировать эту взаимосвязанную замкнутую систему с использованием. Похоже, что решатель RK45 вызывает подключенные системы несколько раз за временной шаг, часто (несколько раз за временной шаг) входные данные равны нулю (не только в начальной части моделирования) вместо значений предшествующих «блоков». состояния/вывод.

Это нормальное поведение для решателя? Во всяком случае, я ожидал, чтовызываться несколько раз, а нев соответствии с моим пониманием того, как работают решатели Рунге-Кутта.

Функции могут выполнять деление на входе (разрешено, поскольку некоторые переменные, передаваемые между связанными системами, никогда не могут быть равны нулю, кватернионы), если эти функции вызываются с нулевым входом, симуляция прерывается, но, что более важно, в общем случае выход может не быть релевантен для моделирования, если используется неверный (нулевой) ввод.

Ниже (часть) вывода, когда код запускается с операторами печати для отладки. Возникновение outfcn относительно updfcn вызывает беспокойство и передача входных данных (u), которые имеют нулевое значение.

      Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.53  0.53  0.053]

Called markleyPD_outfcn with u: 
[0. 0. 0. 0. 0. 0. 0.]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.53  0.53  0.053]

Called markleyPD_outfcn with u: 
[0.53   0.53   0.053  0.6853 0.6953 0.1531 0.1531]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.53  0.53  0.053]

Called markleyPD_outfcn with u: 
[0.53   0.53   0.053  0.6853 0.6953 0.1531 0.1531]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.53  0.53  0.053]

Called rotKin_updfcn with u: 
[0.53  0.53  0.053]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52958536 0.5297074  0.0532223 ]

Called markleyPD_outfcn with u: 
[0. 0. 0. 0. 0. 0. 0.]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52958536 0.5297074  0.0532223 ]

Called markleyPD_outfcn with u: 
[0.52958536 0.5297074  0.0532223  0.6854992  0.69598091 0.15311521
 0.14910081]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52958536 0.5297074  0.0532223 ]

Called markleyPD_outfcn with u: 
[0.52958536 0.5297074  0.0532223  0.6854992  0.69598091 0.15311521
 0.14910081]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52958536 0.5297074  0.0532223 ]

Called rotKin_updfcn with u: 
[0.52958536 0.5297074  0.0532223 ]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52922002 0.52944959 0.05341816]

Called markleyPD_outfcn with u: 
[0. 0. 0. 0. 0. 0. 0.]

Called rotKin_outfcn with u: 
[0. 0. 0.]

Called rotKin_outfcn with u: 
[0.52922002 0.52944959 0.05341816]

Я знаю, что могу кодировать деление на ноль, но это не решает основную проблему, связанную с тем, что выходные функции изначально обнуляются. По мере того, как я создаю более сложные системы, кажется, что большее количество взаимосвязанных систем вносит еще большую непредсказуемость.

Я спрашиваю себя:

  • Я используюправильный?
  • Почему системный решатель пытается решить статическую нелинейность (PD-регулятор без состояний) с помощью решателя RK45? Он может просто выполнять арифметику один раз за шаг?
  • Зачем столько раз звонить в outfcn? Это, кажется, только увеличивается, когда количество взаимосвязанных систем увеличивается.
  • Может ли быть проблема с лежащей в основе библиотекой управления?

Пример След ошибки, которую я получаю при использовании ввода (u) для операций кватерниона:

      ---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [16], in <cell line: 117>()
    114 omega_0 = [0.53, 0.53, 0.053]
    115 x_0 = np.concatenate([omega_0, q_0])
--> 117 y = control.input_output_response(system, time, X0=x_0)

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/control/iosys.py:1839, in input_output_response(sys, T, U, X0, params, transpose, return_x, squeeze, solve_ivp_kwargs, t_eval, **kwargs)
   1836 if not hasattr(sp.integrate, 'solve_ivp'):
   1837     raise NameError("scipy.integrate.solve_ivp not found; "
   1838                     "use SciPy 1.0 or greater")
-> 1839 soln = sp.integrate.solve_ivp(
   1840     ivp_rhs, (T0, Tf), X0, t_eval=t_eval,
   1841     vectorized=False, **solve_ivp_kwargs)
   1842 if not soln.success:
   1843     raise RuntimeError("solve_ivp failed: " + soln.message)

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/scipy/integrate/_ivp/ivp.py:546, in solve_ivp(fun, t_span, y0, method, t_eval, dense_output, events, vectorized, args, **options)
    543 if method in METHODS:
    544     method = METHODS[method]
--> 546 solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
    548 if t_eval is None:
    549     ts = [t0]

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/scipy/integrate/_ivp/rk.py:94, in RungeKutta.__init__(self, fun, t0, y0, t_bound, max_step, rtol, atol, vectorized, first_step, **extraneous)
     92 self.max_step = validate_max_step(max_step)
     93 self.rtol, self.atol = validate_tol(rtol, atol, self.n)
---> 94 self.f = self.fun(self.t, self.y)
     95 if first_step is None:
     96     self.h_abs = select_initial_step(
     97         self.fun, self.t, self.y, self.f, self.direction,
     98         self.error_estimator_order, self.rtol, self.atol)

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/scipy/integrate/_ivp/base.py:138, in OdeSolver.__init__.<locals>.fun(t, y)
    136 def fun(t, y):
    137     self.nfev += 1
--> 138     return self.fun_single(t, y)

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/scipy/integrate/_ivp/base.py:20, in check_arguments.<locals>.fun_wrapped(t, y)
     19 def fun_wrapped(t, y):
---> 20     return np.asarray(fun(t, y), dtype=dtype)

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/control/iosys.py:1832, in input_output_response.<locals>.ivp_rhs(t, x)
   1831 def ivp_rhs(t, x):
-> 1832     return sys._rhs(t, x, ufun(t))

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/control/iosys.py:1024, in InterconnectedSystem._rhs(self, t, x, u)
   1021 u = np.array(u, ndmin=1)
   1023 # Compute the input and output vectors
-> 1024 ulist, ylist = self._compute_static_io(t, x, u)
   1026 # Go through each system and update the right hand side for that system
   1027 xdot = np.zeros((self.nstates,))        # Array to hold results

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/control/iosys.py:1074, in InterconnectedSystem._compute_static_io(self, t, x, u)
   1071 state_index, input_index, output_index = 0, 0, 0
   1072 for sys in self.syslist:
   1073     # Compute outputs for each system from current state
-> 1074     ysys = sys._out(
   1075         t, x[state_index:state_index + sys.nstates],
   1076         ulist[input_index:input_index + sys.ninputs])
   1078     # Store the outputs at the start of ylist
   1079     ylist[output_index:output_index + sys.noutputs] = \
   1080         ysys.reshape((-1,))

File ~/Repositories/aocs-utils/.venv_aocs_utils/lib/python3.8/site-packages/control/iosys.py:849, in NonlinearIOSystem._out(self, t, x, u)
    848 def _out(self, t, x, u):
--> 849     y = self.outfcn(t, x, u, self._current_params) \
    850         if self.outfcn is not None else x
    851     return np.array(y).reshape((-1,))

Input In [16], in markleyPD_outfcn(t, x, u, params)
     44 print('\nCalled markleyPD_outfcn with u: ')
     45 print(u)
---> 46 rot = Rotation.from_quat(u[3:7])
     48 ## Markley p.291
     49 # τ = -k_p*sign(dq_4)*dq_0:3 - k_d*ω
     50 tau = -k_p*np.sign(u[6])*u[3:6] - k_d*u[0:3]

File _rotation.pyx:627, in scipy.spatial.transform._rotation.Rotation.from_quat()

File _rotation.pyx:531, in scipy.spatial.transform._rotation.Rotation.__init__()

ValueError: Found zero norm quaternions in `quat`.

Ниже приведен минималистичный код, который может воспроизвести проблему:

      import numpy as np
import control
from scipy.spatial.transform import Rotation

# Rotational dynamics state update function
def rotDyn_updfcn(t, x, u, params):
    I = params.get('sat_inertia')
    
    ## Markley p.84
    # I*dω = τ_e - ω x (Iω)
    dx = np.matmul(np.linalg.inv(I), (u - np.cross(x, np.matmul(I,x))))
    
    return dx


# Rotational kinematics state update function
def rotKin_updfcn(t, x, u, params):
    
    print('\nCalled rotKin_updfcn with u: ')
    print(u)

    ## Markley p.71
    # dq = 1/2 * Ξ(q)*ω   
    dq = np.array([[ x[3]*u[0] - x[2]*u[1] + x[1]*u[2]],
                   [ x[2]*u[0] + x[3]*u[1] - x[0]*u[2]],
                   [-x[1]*u[0] + x[0]*u[1] + x[3]*u[2]],
                   [-x[0]*u[0] - x[1]*u[1] - x[2]*u[2]]]) / 2
    
    return dq

# Rotational kinematics output function
def rotKin_outfcn(t, x, u, params):
    
    print('\nCalled rotKin_outfcn with u: ')
    print(u)
    
    return x

# Basic quaternion attitude feedback controller
def markleyPD_outfcn(t, x, u, params):
    k_p = params.get('k_p')
    k_d = params.get('k_d')
    
    print('\nCalled markleyPD_outfcn with u: ')
    print(u)
    rot = Rotation.from_quat(u[3:7])
    
    ## Markley p.291
    # τ = -k_p*sign(dq_4)*dq_0:3 - k_d*ω
    tau = -k_p*np.sign(u[6])*u[3:6] - k_d*u[0:3]
    
    return tau


params = {
    "sat_inertia": [[10000, 0, 0],
                    [0, 9000, 0],
                    [0, 0, 12000]],
    "k_p": 50,
    "k_d": 500
}

## Plant construction
rotDyn = control.NonlinearIOSystem(rotDyn_updfcn,
                                   None,
                                   name='rotDyn',
                                   inputs=['L','M','N'],
                                   outputs=['P','Q','R'],
                                   states=['P','Q','R'],
                                   params=params,
                                   dt=0
                                  )

rotKin = control.NonlinearIOSystem(rotKin_updfcn,
                                   rotKin_outfcn,
                                   name='rotKin',
                                   inputs=['P','Q','R'],
                                   outputs=['q0','q1','q2','q3'],
                                   states=['q0','q1','q2','q3'],
                                   params=params,
                                   dt=0
                                  )

plant = control.interconnect((rotDyn, rotKin),
                             name='plant',
                             inputs=rotDyn.input_labels,
                             outputs=rotDyn.output_labels + rotKin.output_labels,
                             states=rotDyn.state_labels + rotKin.state_labels
                            )
plant.name='plant' #Required due to bug in interconnect?

## Controller construction
ctrl = control.NonlinearIOSystem(None,
                                 markleyPD_outfcn,
                                 inputs = ['P', 'Q', 'R', 'q0', 'q1', 'q2', 'q3'],
                                 outputs = ['L', 'M', 'N'],
                                 params=params,
                                 dt=0
                                )

## Closed loop system
system = control.interconnect([plant, ctrl],
                              name='system',
                              outputs=ctrl.output_labels + plant.output_labels,
                              states=plant.state_labels
                             )
system.name = 'system'

## Run simulation
time = np.linspace(0,300,3000)

# initial condition
q_0 = [0.6853, 0.6953, 0.1531, 0.1531]
omega_0 = [0.53, 0.53, 0.053]
x_0 = np.concatenate([omega_0, q_0])

y = control.input_output_response(system, time, X0=x_0)

Я знаю, что это довольно сложная проблема с математической точки зрения, но надеюсь, что кто-то может хотя бы пролить свет на мое использованиепри решении дифференциальных уравнений и использовании питонабиблиотека.

0 ответов

Другие вопросы по тегам