JuliaControl / JuliaControl/RobustAndOptimalControl.jl

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

Open
#90 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Julia
Stars
67
Forks
10
Avg merge
1h 21m
Merged PRs (30d)
3

Description

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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce mk2_block_diagram_() and trace the failure through ControlSystemsBase/src/connections.jl:332 and RobustAndOptimalControl/src/named_systems2.jl:367,463. Compare the reported U2 and Y1 connections with the inputs and outputs assembled in the example. Done means the failure message identifies the mismatched signals clearly enough to locate the faulty connection.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
developer-experience
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.