RobustAndOptimalControl.jl icon indicating copy to clipboard operation
RobustAndOptimalControl.jl copied to clipboard

Hard to understand error message: Lengths of U2 (1:27) and Y1

Open tallakt opened this issue 1 year ago • 3 comments

So I was working on my first larger block diagram, and trying to get everything in place. First I was meeting error messages giving me the name of an input/output that I was able to figure out. But after sorting out all that, I was presented with the following error message, after which I am quite stuck.

I am asking in this issue if maybe the error message should be improved on?

julia> bd = mk2_block_diagram_()
┌ Warning: Connecting single output to multiple inputs Y1=Union{Nothing, Int64}[1, 9, 8, 5, 20, 21, 22, 23, 24, 25, 11, 13, 12, 14, 3, 4, 6, 7, 16, 18, 17, 19, 15, 4, 11, 12, 21, 2, 10, 1]
└ @ ControlSystemsBase ~/.julia/packages/ControlSystemsBase/eWEYr/src/connections.jl:323
ERROR: Lengths of U2 (1:27) and Y1 (Union{Nothing, Int64}[1, 9, 8, 5, 20, 21, 22, 23, 24, 25, 11, 13, 12, 14, 3, 4, 6, 7, 16, 18, 17, 19, 15, 4, 11, 12, 21, 2, 10, 1]) must be equal
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] feedback(sys1::StateSpace{Continuous, Float64}, sys2::StateSpace{Continuous, Bool}; U1::Vector{Union{Nothing, Int64}}, Y1::Vector{Union{Nothing, Int64}}, U2::UnitRange{Int64}, Y2::UnitRange{Int64}, W1::Vector{Union{Nothing, Int64}}, Z1::Vector{Union{Nothing, Int64}}, W2::Vector{Union{Nothing, Int64}}, Z2::Vector{Union{Nothing, Int64}}, Wperm::Colon, Zperm::Colon, pos_feedback::Bool)
   @ ControlSystemsBase ~/.julia/packages/ControlSystemsBase/eWEYr/src/connections.jl:332
 [3] feedback(s1::NamedStateSpace{Continuous, StateSpace{Continuous, Float64}}, s2::NamedStateSpace{Continuous, StateSpace{Continuous, Bool}}; u1::Vector{Symbol}, w1::Vector{Symbol}, z1::Vector{Symbol}, y1::Vector{Symbol}, u2::Function, y2::Function, w2::Vector{Any}, z2::Vector{Any}, kwargs::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:pos_feedback,), Tuple{Bool}}})
   @ RobustAndOptimalControl ~/.julia/packages/RobustAndOptimalControl/nd5G8/src/named_systems2.jl:367
 [4] connect(systems::Vector{NamedStateSpace{Continuous, StateSpace{Continuous, Float64}}}; u1::Vector{Symbol}, y1::Vector{Symbol}, w1::Vector{Symbol}, z1::Vector{Symbol}, verbose::Bool, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ RobustAndOptimalControl ~/.julia/packages/RobustAndOptimalControl/nd5G8/src/named_systems2.jl:463
 [5] connect(systems::Vector{NamedStateSpace{Continuous, StateSpace{Continuous, Float64}}}, pairs::Vector{Pair{Symbol, Symbol}}; kwargs::Base.Pairs{Symbol, Vector{Symbol}, Tuple{Symbol, Symbol}, NamedTuple{(:w1, :z1), Tuple{Vector{Symbol}, Vector{Symbol}}}})
   @ RobustAndOptimalControl ~/.julia/packages/RobustAndOptimalControl/nd5G8/src/named_systems2.jl:467
 [6] mk2_block_diagram_(; inner_feedback::Bool, outer_feedback::Bool)
   @ Main ~/Documents/forsøk/spark_10_feedback/spark_10_feedback.jl:386
 [7] mk2_block_diagram_()
   @ Main ~/Documents/forsøk/spark_10_feedback/spark_10_feedback.jl:327
 [8] top-level scope
   @ REPL[256]:1

The code for the failing function:

function mk2_block_diagram(airspeed; params = init_pitch_params(), inner_feedback = true, outer_feedback = true)
  integrator = tf([1], [1, 0])
  dynamic_pressure = 0.5 * params.rho * airspeed^2
  k0 = params.aoa_to_moment_gain_normalized
  k1 = params.aoa_rate_to_moment_gain_normalized
  back = dynamic_pressure * parallel(k0 * integrator, tf(k1 / max(airspeed, eps())))
  # we assume the airflow is coming from pitch angle zero
  feedback(inertia_tf(params = params), back)
  
  my_named_tf = (tf, name) -> named_ss(ss(tf), u = Symbol("$(name)_u"), x = Symbol("$(name)_x"), y = Symbol("$(name)_y"))
  only_first = arr -> [x[1] => y[1] for (x, y) = arr]

  inertia = my_named_tf(inertia_tf(), :inertia) # output is pitch rate
  pitch_int = my_named_tf(integrator, :pitch_int) # output is pitch angle
  aoa_aero = my_named_tf(tf(dynamic_pressure * k0), :aoa_aero)
  aoa_rate_aero = my_named_tf(tf(dynamic_pressure * k1 / max(eps(), airspeed)), :aoa_rate_aero)
  aoa_int = my_named_tf(integrator, :aoa_int) # output is aoa angle, signal is aoa_rate otherwise system is not proper
  hacker = my_named_tf(hacker_tf(), :hacker)
  elev_aero = my_named_tf(elevator_to_aero_moments_tf(airspeed, params = params), :elev_aero)
  props_ctrl = my_named_tf(mk2_inner_props_controller(), :props_ctrl)
  elev_ctrl = my_named_tf(mk2_inner_elev_controller(), :elev_ctrl)
  outer_ctrl = my_named_tf(mk2_outer_controller_tf(), :outer_ctrl)

  inputs = [:pitch_ref
            , :aoa_rate
            , :disturbance
           ]

  blocks = [
            inertia
            , pitch_int
            , aoa_aero
            , aoa_rate_aero
            , aoa_int
            , hacker
            , elev_aero
            , props_ctrl
            , elev_ctrl
            , outer_ctrl
           ]

  splitters = [
               splitter(:aoa_rate, 2)
               , splitter(:pitch_int_y, 3)
               , splitter(:outer_ctrl_y, 2)
               , splitter(:inertia_y, 2)
              ]

  sum_blocks = [
                sumblock("aoa_int_u = aoa_rate1 - pitch_int_y1")
                , sumblock("aoa_rate_aero_u = aoa_rate2 - pitch_int_y2")
                , sumblock("inertia_u = aoa_aero_y + aoa_rate_aero_y + hacker_y + elev_aero_y + disturbance")
                , sumblock(inner_feedback ? "props_ctrl_u = outer_ctrl_y1 - inertia_y1" : "props_ctrl_u = outer_ctrl_y1")
                , sumblock(inner_feedback ? "elev_ctrl_u = outer_ctrl_y2 - inertia_y2" : "elev_ctrl_u = outer_ctrl_y2" )
                , sumblock(outer_feedback ? "outer_ctrl_u = pitch_ref - pitch_int_y3" :  "outer_ctrl_u = pitch_ref")
               ]

  connections = vcat(
                     only_first([elev_ctrl.y => elev_aero.u
                                 , props_ctrl.y => hacker.u
                                 , aoa_int.y => aoa_aero.u
                                ])
                     , [sys.y[1] => sys.y[1] for sys = sum_blocks] # need to connect sum blocks to real blocks
                     , vcat([[n => n for n = setdiff(sys.u, inputs)] for sys = sum_blocks]...)
                     , vcat([sys.u[1] => sys.u[1] for sys = splitters])
                     , :inertia_y3 => :pitch_int_u
                    )
  # dump([
  # :blocks => blocks
  # , :splitters => splitters
  # , :sum_blocks => sum_blocks
  # , :connections => connections
  # ])
  connect(vcat(blocks, splitters, sum_blocks), connections, w1 = inputs, z1 = [sys.y[1] for sys = blocks])
end

tallakt avatar Apr 21 '23 13:04 tallakt