Cを使用してArduinoに 倒立振り子 を構築しました(つまり、すべてが手続き的に行われました)。私はアプリケーション設計を自己学習しようとしています。SOLID、疎結合、およびテスト容易性を念頭に置いて、コードをより多くのOOアプローチにリファクタリングしたいと考えています。
この設計を改善して、より疎結合なシステムとより優れたテスト容易性を実現するにはどうすればよいですか?
MotorController
-モーターコントローラーのインターフェイス。
DrokL928
-使用するモーターコントローラーはMotorController
を実装しています。
Cart
-DrokL928
の便利なラッパーです。カートをより直感的に制御できます。
Encoder
-ロータリーエンコーダーから値を読み取るための外部ライブラリ。
EncoderWrapper
-Encoder
のラッパー。このようにして、外部APIを1つの場所にカプセル化します。
StateVector
-現在の状態データを保持します。
StateUpdater
-EncoderWrapper
からのエンコーダー値を処理し、StateVector
に割り当てます。
LQRController
-振り子を安定させるために、カートに送信する(現在の状態に基づいて)PWM信号を計算します。参照してください: Wikipediaの線形二次レギュレーター 。
StateUpdater
は定数IDLER_PULLEY_RADIUS
およびSYSTEM_LOOP_RATE
を使用します。これらはStateUpdater
に属さないようなにおいですが、状態の計算に関連しています。 StateUpdater
内のこれらのプライベートメソッドはすべて、別のクラスStateCalculator
に入れることができると思います。
LQRController
は定数pendulumBound
を使用します。つまり、LQRコントローラーは、振り子の角度が特定の範囲内にある場合にのみ入力を計算する必要があります。どういうわけかこれはここに属していないように感じますが、多分私はそれについて間違っています。完全を期すために、StateVector
の各変数に境界を追加する必要があるかもしれません。
現時点では、EncoderWrapper
への参照が必要なため、Encoder
をテストハーネスにインスタンス化できません。そして、拡張機能として、StateUpdater
をテストハーネスにインスタンス化することはできません。どうすれば修正できますか?
最も理想的なケースでは、StateVector
をそれほどハードコードせず、変数を変更できるようにする必要があります。したがって、LQRController
には任意の長さのgainVector
が必要です(現時点では4にハードコードされています)。これは抽象化しすぎですか?そうでない場合、どうすればこれを達成できますか?考えてみると、アルゴリズムが非常に具体的であるため、StateUpdater
が任意の状態変数の状態を計算する方法がわからないため、抽象化が多すぎると思います。
優れたデザインの特徴の1つは、ドメインについての知識があまりない人にとっても、ほとんどの場合自明です。たまたま、質問を読む前に倒立振子が何であるかわからなかったため、図からいくつかの洞察を収集しようとしましたが、残念ながら、Cart
が問題に関連している可能性がある唯一のオブジェクトのようでしたドメイン。
ウィキペディアで inverted pendulums についてすぐに読んだ後、デザインの詳細はより理解しやすくなりましたが、Pendulum
などの抽象化が欠落していて、代わりに状態とStateVector
とStateUpdater
の間に分散した動作。
私の提案は、システムを外側から考え、実装方法ではなく、モデル化しようとしている概念に焦点を当てることですたとえば、あなたは言及しました:
Cart
-DrokL928
の便利なラッパーです。カートをより直感的に制御できます。
Cart
は単なる「便利なラッパー」ではなく、モデル化しようとしている主要なものの1つです。カートには位置と速度があり、その動きを制御できます。 Encoder
とMotorController
は、それぞれ現在の状態の取得と動きの制御に使用される実装の詳細です。
同様に、角度と速度のプロパティを持つPendulum
クラスがあり、これらの値を取得するために内部でEncoder
を使用します。
最後に、InvertedPendulum
とCart
で構成されるシステム全体を表し、Pendulum
を使用してシステムを安定させるController
クラス。
これにより、すべての部分がより意味のあるオブジェクトに移動されたため、StateVector
およびStateUpdater
がデザインから削除されます。
現時点では、
EncoderWrapper
への参照が必要なため、Encoder
をテストハーネスにインスタンス化できません。 ...どうすれば修正できますか?
MotorController
の場合と同じようにインターフェイスを作成します。実際の実装では外部ライブラリが呼び出され、テストの返信定型文を単に返す1つ以上の偽の実装を作成できます。
完全な答えではありません。しかし、あなたは組み込みシステムを設計しているので(そして偶然にも、私はArduinoがサポートする振り子に取り組んだだけです):これらは通常、大規模なステートマシンです。コントローラーはいずれにしても同様のインターフェースを備えているため、クラスの設計はかなり簡単です。だからそれはマイナーな部分です。大きなものは、状態マシンの設計です。そしてもちろん、リアルタイムアプリケーションを扱っているので、必要なパフォーマンスを優先して、常に優れたデザインの神々に従うことができないことがわかります:-/
Regaring test:これは特にRTアプリケーションに対してトリッキーです。テストを追加することで、システムの動作をゆがめます。例えば、最終的には死後に読むことができる高速バッファロガーを追加しました。空想的なデバッグを行うことは物理学では動きを見たいときに動きを止めることができないため、単に不可能です。