I built an inverted pendulum on an Arduino using C (ie. everything was done procedurally). I'm trying to self study application design and would like to refactor my code into a more OO approach with SOLID, loose coupling, and testability in mind.
How can I improve this design to achieve a more loosely coupled system and better testability?
Brief summary of classes
MotorController - An interface for motor controllers.
DrokL928 - Is the motor controller I use, implements
Cart - Is just a convenient wrapper for
DrokL928. Allows for more intuitive control of the cart.
Encoder - An external library for reading values from rotary encoders.
EncoderWrapper - A wrapper for
Encoder. That way I encapsulate the external API into one place.
StateVector - Holds the current state data.
StateUpdater - Processes encoder values from
EncoderWrapper and assigns them to
LQRController - Computes the PWM signal (based on the current state) to send to the Cart in order to stabilize the pendulum. see: Wikipedia's linear-quadratic-regulator.
Specific design questions
SYSTEM_LOOP_RATE. These smell like they don't belong inside
StateUpdater, but they are relevant to the calculation of the state. I suppose all of those private methods inside
StateUpdatercould be put into a separate class
LQRControlleruses the constant
pendulumBound. That is, the LQR controller should only calculate the input if the pendulum angle is within a certain bound. For some reason I feel like this doesn't belong here, but maybe I'm wrong on that. For the sake of completeness, maybe I should add a bound for each variable in
As of right now I can't instantiate
EncoderWrapperinto a test harness because it requires a reference to an
Encoder. And by extension, I can't instantiate a
StateUpdaterinto a test harness. How can I fix this?
In the most ideal case, I would like for
StateVectorto not be so hardcoded and the variables should be open to modification. Therefore,
LQRControllershould have a
gainVectorof any length (right now it is hardcoded to 4). Is this too much abstraction? If not, how can I go about achieving this? Now that I think about it, I believe it would be too much abstraction because then I'm not sure how the
StateUpdaterwould calculate the state on arbitrary state variables, because the algorithm is very specific.