# 2. 構造モデル 本プログラムでは、データ駆動を意識した。走行体はシナリオ通りに動作し、ユーザは機能実現の詳細を把握する必要が無いものとする。 ## 2-1. シナリオデータの設計 ナリオを走行コマンドの列で表現する。走行コマンドは、 1. オプション 2. 走行方法 3. 終了判定 によって構成される。 1. オプション 走行コマンドの実行前に、距離計や角度計のリセットを行う場合がある。これらは走行コマンドのオプション設定としてデータで表す。 2. 走行方法 走行方法は、走行種別とそのパラメータによって記述する。機能モデルに要求される走行方法を以下に示す。 | 走行種別 | パラメータ | | ------------------ | ---------------------------------------------------- | | PWM 固定値 | 左右 PWM | | PID 制御 | 基本 PWM, ライン沿い (右 or 左)、PID パラメータ、目標輝度 | | 消す?: 固定角走行 | 基本 PWM, ライン沿い (右 or 左)、 PID パラメータ、目標機体角度 | 3. 終了判定 走行コマンドの終了判定は、関数として記述する。経過時間・走行距離・センサ値などの外界の情報を受け取って、継続または終了を返すものとする。 以降、このような外界の全情報を、単に『ロボット情報』と呼ぶ。 ## 2-2. モジュール構成・クラス図 ```plantuml @startuml allow_mixing skinparam packageStyle Node P1 -d-> P2 P1 -d--> P4 P2 -d-> P3 共通 -l[hidden]-> P1 'note right of "シナリオ制御": シナリオ制御は外界のロボット情報を走行コマンドに与え、\n走行コマンドの指令 (モーターの制御値) を外界に反映する。\n\nまた走行コマンドの実行前に、センサリセット等のオプションを反映する。 'note right of "走行コマンド": 走行コマンドは、外界のロボット情報を受け取ってモータの制御値を返す。\n走行コマンドの終了判定は、コマンド作成時に受け取ったクロージャによって行う。 package "シナリオ制御" as P1 #F5F5F5 { +class CommandRunner { - commands: Command[] - currentCommand: int - clock: Clock - device: Device } } package "走行コマンド" as P2 #D5E8D4 { +class Command { - isEnd: Fn~<&Snapshot> -> bool - runMethod: IRunMethod + options: Options + run(snapshot: &Snapshot) -> Pwm } Command -d-o IRunMethod Command -d-o Options interface IRunMethod <<Interface>> { + init(snapshot: &Snapshot) + update(snapshot: &Snapshot) -> Pwm } IRunMethod <|.. BrightnessPidRun +class BrightnessPidRun { - pid: Pid + init(snapshot: &Snapshot) + update(snapshot: &Snapshot) -> Pwm } IRunMethod <|.. AnglePidRun +class AnglePidRun { - pid: Pid + init(snapshot: &Snapshot) + update(snapshot: &Snapshot) -> Pwm } IRunMethod <|.. FixedPwmRun +class FixedPwmRun { - pwm: Pwm + init(snapshot: &Snapshot) + update(snapshot: &Snapshot) -> Pwm } +class Options { + resetDistance: bool + resetDirection: bool } } package "PID制御" as P3 #F8CECC { +class Pid { - basePwm: Pwm - pidParam: PidParam - edge: FollowEdge - targetBrightness: float - state: PidState + update(dt: float, brightness: float) -> Pwm + reset() } Pid -d-o PidState class PidState { - acc: float - lastDiff: float + update(dt: float, brightness: float, param: &PidParam) + reset() } } package "計測・デバイス制御" as P4 #DAE8FC { +class Device { + robo: Robo - dir: DiretionCalculator - dist: DistanceCalculator + tick(dt: float) -> Snapshot + setPwm(pwm: Pwm) + resetDirection() + resetDistance() } Device -d-o Robo Device -d-o DiretionCalculator Device -d-o DistanceCalculator class Robo { + wheels: Monor[2] + color: ColorSensor + gyro: GyroSensor + sonar: SonarSensor + touch: TouchSensor } class DiretionCalculator { - current: float + tick(dt: float) -> float + reset() } class DistanceCalculator { - current: float + tick(dt: float) -> float + reset() } } package "共通" #F5F5F5 { +class Snapshot { + dt: float + pwm: Pwm + distance: float + deltaDistance: float + direction: float + deltaDirection: float + color: rgb_raw_t + brightness: float + gyro: float + sonarDistance: float } +class PidParam { + float p + float i + float d } +enum FollowEdge { LeftEdge RightEdge } +class Pwm { + l: int + r: int } } @enduml ``` ## パッケージ図 ET ロボコンのパッケージ図とはちょっと違う ```plantuml @startuml allow_mixing 'skinparam packageStyle Node package "" as P1 #F5F5F5 { class "シナリオ制御" {} } package "走行コマンド" as P2 #D5E8D4 { } package "計測・デバイス制御" as P4 #DAE8FC { } package "共通" #F5F5F5 { } @enduml ``` # パーツ ```plantuml 'skinparam style strictuml skinparam backgroundColor transparent +class 走行コマンド { '+ 開始時オプション: 開始時オプション '- 走行方法: 走行方法 - 終了判定: Fn~<&ロボット情報> -> bool + 更新(&ロボット情報) -> Pwm + 終了判定の実施(&ロボット情報) -> bool } 走行コマンド "1 " *-d- "-走行方法 1" 走行方法 走行コマンド "1" *-d- "1 +オプション" 開始時オプション interface 走行方法 <<Interface>> { + 初期化(&ロボット情報) + 更新(&ロボット情報) -> Pwm } 走行方法 <|.. "輝度に対するPID制御" +class "輝度に対するPID制御" { '- pid: Pid + 初期化(&ロボット情報) + 更新(&ロボット情報) -> Pwm } 走行方法 <|.. "モータ回転比に対するPID制御" +class "モータ回転比に対するPID制御" { '- pid: Pid + 初期化(&ロボット情報) + 更新(&ロボット情報) -> Pwm } 走行方法 <|.. PWM固定走法 +class PWM固定走法 { - pwm: Pwm + 初期化(&ロボット情報) + 更新(&ロボット情報) -> Pwm } +class 開始時オプション { + 距離計リセット: bool + 角度計リセット: bool } ``` ```plantuml 'skinparam style strictuml skinparam backgroundColor transparent +class ロボット情報 { + dt: float + pwm: Pwm + 距離: float + 距離偏差: float + 角度: float + 角度偏差: float + 色: rgb_raw_t + 輝度: float } +class Pwm { + l: int + r: int } ``` ```plantuml 'skinparam style strictuml skinparam backgroundColor transparent +class PID制御 { - 基本PWM: Pwm - 目標値: float ''- 状態: PidState + 更新(dt: float, 現在値: float) -> Pwm + リセット() } PID制御 "1 " *-d- "1 -状態" PID状態 PID制御 "1 " *-d- "1 -設定" PID設定 PID制御 "1 " *-d- "1 -方向" 制御方向 class PID状態 { - 蓄積値: float - 前回差: float + 更新(dt: float, 現在値: float, 設定: &PID設定) + リセット() } +class PID設定 { + p: float + i: float + d: float } +enum 制御方向 <<Enuemration>> { +正方向 +負方向 } ``` ```plantuml 'skinparam style strictuml skinparam backgroundColor transparent +class デバイス { + 更新(dt: float) -> ロボット情報 + PWMセット(pwm: Pwm) + 距離計リセット() + 方向計リセット() } "デバイス" "1" *-d- "+ロボット 1" ロボット "デバイス" "1" *-d- "+角度計 1" 角度計 "デバイス" "1" *-d- "+距離計 1" 距離計 class ロボット { + 左右モータ: Monor[2] + 色センサー: ColorSensor + ジャイロセンサ―: GyroSensor + 超音波センサー: SonarSensor + タッチセンサー: TouchSensor + 各種センサ値取得(&mut ロボット情報) + モータ角取得(l: &mut int, r: &mut int) } class 角度計 { - 現在値: float + 更新(dt: float) -> float + リセット() } class 距離計 { - 現在値: float + 更新(dt: float) -> float + リセット() } ``` ```plantuml 'skinparam style strictuml skinparam backgroundColor transparent +class コマンド実行 { '- コマンド列: 走行コマンド[] - 現コマンド: int - 時計: Clock - 現在時間: uint '- デバイス: デバイス + 更新() } ``` 凡例 ```plantuml +class PublicClass { +publicField: Type -privateField: Type } ``` ```plantuml -class PrivateClass { +publicMethod() -> Type -privateMethod() -> Type } ``` ```plantuml +enum Enum <<Enuemration>> { +Variant } ``` ```plantuml= +interface Interface <<Interface>> { } ```