Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/udonrobo/UdonLibrary
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyNelson314 committed Oct 23, 2024
2 parents fb44aff + ee0cd74 commit 25c26c9
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 41 deletions.
4 changes: 2 additions & 2 deletions docs/Algorithm/LoopCycleController.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ループ周期制御

`LoopCycleController` クラスを使用し、ループ内の処理時間によらず、ループ周期を一定に保ちます
Arduino の `loop()` 関数は、処理時間によって周期が変動します。そのため、PID 制御の様な一定周期でのフィードバックが重要なアルゴリズムを使用する場合、ループ周期を一定に保つ必要があります

コンストラクタ引数にループ周期をマイクロ秒単位で指定し`update()` を呼び出すことで周期が一定に保たれます。
下記に `LoopCycleController` クラスを使用した例を示します。コンストラクタ引数にループ周期(マイクロ秒)を指定し`update()` を呼び出すことで周期が一定に保たれます。

```cpp
#include <Udon.hpp>
Expand Down
6 changes: 4 additions & 2 deletions docs/Algorithm/MovingAverage.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# 移動平均

モーターの出力値を急激に変化させると、大電流が流れ、モーターやモータードライバーに負荷がかかります。この出力値の急激な変化を抑えるのに移動平均を利用します
モーターの出力値を急激に変化させると、大電流が流れ、モーターやモータードライバーに負荷がかかります。移動平均を使うことで、出力値の急激な変化を抑えることができます

移動平均とは時系列データを平滑化する手法です。サンプル数が多いほど滑らかになりますが、応答性が悪くなります。

`MovingAverage` クラスは内部に過去の入力値を保持する配列を持っており、配列の平均値を出力値としています。
> [!NOTE]
>
> ループ毎に平均値を更新するため、ループ周期が一定でないと正しく動作しません。`LoopCycleController` を使ってループ周期を一定にすることをお勧めします。
```cpp
#include <Udon.hpp>
Expand Down
89 changes: 55 additions & 34 deletions docs/Algorithm/SteerOptimizer.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,67 @@
# 独立ステア最適化

数式から得られた独立ステアの出力値をそのまま使用すると、かなり操縦の難しい足回りとなってしまいます。`SteerOptimizer` クラスはを使用することで、操縦性向上が見込めます。以下の最適化処理が含まれています。

- 車輪が動作中でない場合に旋回角度を維持する

<img width="400px" src="https://github.com/user-attachments/assets/298c4f20-9b63-423d-ba46-f87974e8de11"/>

- 車輪の旋回範囲を無限にする

式から得られた旋回角度は、±π の範囲で得られます。この範囲を無限に拡張します。

ゆっくりモジュールを旋回しようとした場合、次のような挙動をします。最適化しない場合 π と -π の境界を超える時に、逆方向に旋回してしまいます。

<img width="400px" src="https://github.com/user-attachments/assets/d100e091-2295-4800-9e2e-7fc83b3039c8"/>

- 車輪の回転方向の最適化

90 度以上の旋回が必要な場合、目標旋回角度を 180 度回転させ、車輪の回転方向を逆転させます。目標角度へ最短で到達できるようになります。

<img width="400px" src="https://github.com/user-attachments/assets/9570c557-7f15-4bed-a39d-6f3a844e50ba"/>

## 個別インクルード

```cpp
#include <Udon/Algorithm/SteerOptimizer.hpp>
```

独立ステアモジュールの旋回角度と回転方向を最適化するためのクラス。
## 全車輪最適化

独立ステアの計算結果をそのまま使用すると、操縦性能が低下してしまう場合があります。このクラスを使用することで、操縦性向上が見込めます
`SteerOptimizer` クラスを用います。このクラスには車輪数を指定するためのテンプレート引数があります。下記の例では 4 つの車輪を使用しています。`operator()` を通すことで最適値を得られます

> このクラスには以下の最適化処理が含まれています:
>
> - 車輪が動作中でない場合に旋回角度を維持する
>
> - 車輪の旋回範囲を無限にする
>
> - 車輪の回転方向の最適化
>
> 90 度以上の旋回が必要な場合、目標旋回角度を 180 度回転させ、車輪の回転方向を逆転させます。これにより、旋回と逆旋回の切り替え時に、車輪を旋回させずに正確な動作が可能となります。
```cpp
static Udon::SteerOptimizer<4> optimizer;

## 全車輪最適化
void setup()
{
...
}

`SteerOptimizer` クラスには車輪の個数を指定するためのテンプレート引数があります。車輪が 4 輪の場合は次のようにインスタンス化します。
void loop()
{
// 移動量
Udon::Pos pos = { { 0.0, 100.0 }, 0.0 }; // y 軸方向に 100 進行

```cpp
// グローバル領域内など
Udon::SteerOptimizer<4> optimizer;
```
// 最適化前
std::array<Udon::Polar, 4> raw = pos.toSteer();

`operator()` を通すことで最適値を得られます。
// 最適化後
std::array<Udon::Polar, 4> optimized = optimizer(raw); // optimizer.operator()

> 各モジュールの値を極座標で扱います。ホイールの出力値は r、モジュールの旋回角度は theta として表現されます。そのため、引数と戻り値は極座標(Udon::Polar)の配列を使用して、全てのモジュールの値を扱います。
for (auto&& polar : optimized)
{
Serial.print(polar.r); // ホイールの出力値 [-255 ~ 255]
Serial.print(" ");
Serial.print(polar.theta); // モジュールの旋回角度 [rad]
Serial.print(" ");
}
Serial.println();

}
```

> 注意点として、本クラスは最適化のみを行います。事前に最適化前の値を求めておく必要があります。
> 本クラスは最適化のみを行うため、事前に最適化前の値を求めておく必要があります。上記の例では `Udon::Pos::toSteer()` で求めています
>
> 最適化前の値要件:
>
Expand All @@ -45,18 +75,9 @@ Udon::SteerOptimizer<4> optimizer;
>
> - `theta` [ ± 最適化前 `theta` ]
```cpp
// ループ内
// 最適化前の値を格納する配列
const std::array<Udon::Polar, 4> raw = {...};

// 最適化後
const std::array<Udon::Polar, 4> optimized = optimizer(raw); // 最適化後の値を取得
```
## 車輪ごとに最適化

`SteerModuleOptimizer` クラスを使用することで、各車輪ごとに最適化を行えます。車輪ごとをクラスで管理している場合、こちらを使用すると便利です。
実は、車輪の最適化は独立して計算されます(車輪間で影響し合わない)。そのため、`SteerModuleOptimizer` クラスを使用することで、各車輪ごとに最適化を行えます。車輪ごとをクラスで管理している場合、こちらを使用すると便利です。

```cpp
Udon::SteerModuleOptimizer optimizer;
Expand Down Expand Up @@ -91,16 +112,16 @@ void loop()
{
bus.update();
// 1. コントローラから Udon::Pos {{x, y}, turn} を得る `getMoveInfo()`
// 2. Udon::Pos からステアの最適化前の値を得る `toSteer()`
// 3. 最適化後の値を得る optimizer()
// 1. `getMoveInfo()`: コントローラから Udon::Pos {{x, y}, turn} を得る
// 2. `toSteer()` : Udon::Pos からステアの最適化前の値を得る
// 3. `optimizer()` : 最適化後の値を得る
const auto optimized = optimizer(pad.getMoveInfo().toSteer());
//... // 車輪 Node に送信するなど
}
```

OpenSiv3D でシミュレートする
## OpenSiv3D でシミュレートする

> Visual Studio への UdonLibrary の追加方法は [ダウンロード: Visual Studio](./../../README.md) を参照してください。
Expand Down
10 changes: 7 additions & 3 deletions docs/Other/Utility.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## `Show 関数`

```cpp
Show(Args&&... args);
```
様々なオブジェクトを出力でき、マルチプラットフォームで動作します。PC の場合標準出力、マイコンの場合 USB シリアルへ送信します。また `Udon::Showln` 関数を用いると最後に改行を出力します。
```cpp
Expand Down Expand Up @@ -37,7 +41,7 @@ void setup()
## `Assert 関数`
```cpp
Assert(bool expression, const char* const message = "", AssertAction action = AssertAction::Abort)
Assert(bool expression, const char* const message = "", AssertAction action = AssertAction::Abort);
```

`expression` が false となるとき、任意のメッセージを出力しプログラムを中断する関数です。`action``Udon::AssertAction::Skip` を指定することで中断しないように設定できます。
Expand All @@ -57,10 +61,10 @@ void setup()
## `Normalized 関数`

```cpp
double Normalized(double value, double min, double max)
double Normalized(double value, double min, double max);
```
-∞~+∞ の範囲を min~max の範囲に変換するします。最小値が設定できるようになった剰余算のイメージです。
-∞~+∞ の範囲を min~max の範囲に変換します。最小値が設定できるようになった剰余算のイメージです。
```cpp
Normalized(190, -180, 180) == 10
Expand Down

0 comments on commit 25c26c9

Please sign in to comment.