Systems Thinking

I’ve recently been developing a deeper, more passionate skillset for the ever-so important topic of “system” engineering.  Our team just completed the creation of a model to simulate a simple tank, where our embedded software would monitor and dispense product from the tank, and our “plant” model would take the output of our embedded software, “act” as the actual product the software is controlling, and provide our software with inputs so it knows how to control the outputs, thus creating a closed-loop system.  This helps us simulate and test our control strategy in a simulated environment.  From the start, our requirements were well defined, some of which included:

  • Parameters
    • Tank Size
  • Inputs
    • Initial fill amount
    • Filling/Emptying mode
    • Fill Rate
    • Empty Rate
  • Output
    • Percentage filled
    • Weight

Although the requirements were (for the most part) clear and the model was implemented to a T, the actual usage of the model didn’t quite work the way we wanted.

First, after filling or emptying the tank, the amount of product remaining reverted back to the value provided by the “initial fill amount”.  What if we wanted to verify the fill amount after filling or emptying?  Did we really have to “inject” whatever value we wanted the tank to remain at after finished with the empty/fill process?

Second, this model restricts the tank from being filled and emptied at the same time.  Who said this isn’t possible in real life?  It may be unusual to fill and empty a tank simultaneously, but should we really prohibit the actual capability to do so?

Last and most importantly, our control algorithm is what actually controls the rate at which the tank is emptied.  However, the requirements called for creating a separate input to control how fast the tank was emptied.  This means during testing, the tank fill level had to be manually controlled by a “dummy” input (empty rate), and our control algorithm outputs were controlling absolutely nothing.  It not only makes more sense, but is much simpler to connect our control algorithm outputs to the “empty rate” of the simulation model in order for a smoother, more organic system model.

We eventually had to redesign and refactor the way our simulation model behaved.  Could this have been avoided?  If so, where did we go wrong?

We ultimately lost our vision of the actual, real-life SYSTEM we were trying to create.  We were too busy creating a model to simulate the way a tank works, instead of incorporating the way this specific tank is integrated in our overall system.

So, what have we learned?  Here are a few tips we can use to do our best at creating clear, concise, cohesive SYSTEMS:

  • KISS (Keep It Simple, Smarty!): Intricate, complex solutions almost always create problems in the long run. Simple, intuitive ideas will promote readability, portability, reusability, robustness, and testability. If the average person can easily wrap their mind around the concept the first time it’s explained, it’s likely to be a sound design.
  • Think about the actual system/product: If the current task is to create a model for how bread becomes toast in a toaster, don’t require inputs such as “toaster plugged in [true/false]”, “lever down [true/false]”, “toaster heat setting [0-10]”, etc. and outputs such as and “toasted [true/false]. Instead, create an input for “temperature [integer]”, and an output for “toastiness [0-100%]”. Too many inputs/outputs can cause confusion, unnecessary computations, corner cases to go untested, and even states that don’t really exist. Also, connect closed-loop data signals together in appropriate places. Use final control algorithm outputs for plant model inputs, and plant model sensor outputs as control algorithm inputs. Keep manual “injection” signals to a minimum, primarily for fault insertions.
  • Think 4th-dimensionally: As encouraged by a former coworker, think about how this component will be used within the grand scheme of things. Does the system’s ultimate scope require this particular component to support features x, y, and z in the future? Keep in mind all possible circumstances each component may eventually need to sustain so major refactors are kept to a minimum.

The next time you find yourself in a tunnel vision on a particular task/component, keep in mind the end-goal, the ultimate vision of what the final outcome will look like, feel like, act like, etc., and how this small piece of the puzzle fits in the grand scheme of things.  You may just find yourself overthinking a complex, unrealistic solution when a simple, intuitive, well-designed plan leads to a smarter, leaner, more cohesive system.