6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【検討】NervesでQuadCopterの姿勢制御できるか

Last updated at Posted at 2024-09-01

課題

姿勢制御では、yaw,roll,pitchの3次元での計算が必要です。
行列での記述ができると何かと都合がよいです。
姿勢制御をPid制御で行うと想定して、ターゲットのRaspi Zero Wでこの演算にどれくらいの時間がかかる調査してみる。

姿勢制御を安定して行うには、最低でも毎秒100回、できれば、毎秒500回程度実行する必要があります。
余裕もみて、一回の演算処理を1m秒程度におさえたいところです。

姿勢制御プログラム

pid.ex
alias Nx.Tensor

defmodule Pid do
  import Nx.Defn

  defstruct kp: nil, ki: nil, kd: nil, guard: nil, integration: nil, error_last: nil, u: nil

  def new([kp, ki, kd, guard]) do
    kp = Nx.tensor(kp)
    ki = Nx.tensor(ki)
    kd = Nx.tensor(kd)
    guard = Nx.tensor(guard)
    guard = if !Nx.equal(ki, Nx.tensor(0.0)), do: Nx.multiply(guard, Nx.divide(kp, ki)), else: guard
    %Pid{
      kp: kp,
      ki: ki,
      kd: kd,
      guard: guard,
      integration: Nx.tensor([0,0,0]),
      error_last: Nx.tensor([0,0,0]),
      u: Nx.tensor([0,0,0])
    }
  end

  def out(%__MODULE__{kp: kp, ki: ki, kd: kd, guard: guard, integration: integration, error_last: error_last} = pid, setpoint, y_value, dt) do
    {integration, error, u} = out_nx(kp,ki,kd,guard, integration,error_last, setpoint, y_value, dt)
    %Pid{pid| integration: integration, error_last: error, u: u}
  end

  defn out_nx(kp,ki,kd,guard, integration,error_last, setpoint, y_value, dt) do
    error = setpoint-y_value
    integration = min(integration+error*dt, guard)
    d_error = error - error_last
    u = kp * error + ki * integration + kd * d_error / dt
    {integration, error, u}
  end

end
controller.ex
defmodule Controller do
  defstruct pid_stable: %{}, pid_angle: %{}, dt: 0.0

  def new(dt, pid_stable_const, pid_angle_const) do
    pid_stable = Pid.new(pid_stable_const)
    pid_angle = Pid.new(pid_angle_const)
    %Controller{pid_stable: pid_stable, pid_angle: pid_angle, dt: dt}
  end

  def next(%Controller{pid_stable: pid_stable, pid_angle: pid_angle, dt: dt}, angle, angular_velocity, setpoint) do
    # PID for angles
    pid_angle = Pid.out(pid_angle, setpoint, angle, dt)

    # PID for stabilization
    pid_stable = Pid.out(pid_stable, pid_angle.u, angular_velocity, dt)
    %Controller{pid_stable: pid_stable, pid_angle: pid_angle, dt: dt}
  end

  def get_u(%Controller{pid_stable: pid_stable}) do
    pid_stable.u
  end
end

測定プログラム

適当な値を用意して、Pidの演算処理を10回実行して実行時間を表示する。
単位はマイクロ秒。

test.ex
defmodule Main do

  def main do
    pid_stable_const = [[0,0,0], [0,0,0], [0,0,0], [0,0,0]]
    pid_anble_const = [[0,0,0], [0,0,0], [0,0,0], [0,0,0]]

    controller = Controller.new(10, pid_stable_const, pid_anble_const)
    angle = Nx.tensor([0, 0, 0])
    angular_velocity = Nx.tensor([0, 0, 0])
    set_point = Nx.tensor([0, 0, 0])

    for x <- 1..10 do
      {time, controller} = :timer.tc(fn -> Controller.next(controller, angle, angular_velocity, set_point)end)
      u = Controller.get_u(controller)
      time
    end
  end

end

結果

No CPU Nx defn EXLA 実行時間概算(μs)
1 RaspiZero 使用 使用 未使用 8000
2 RaspiZero 使用 未使用 未使用 3600
3 RaspiZero 未使用 未使用 未使用 100
4 IntelCPU 使用 使用 未使用 300
5 Raspi4 使用 使用 未使用 3300
6 Raspi4 使用 使用 使用 5800

2024/9/5
Raspi4の結果追加しました

まとめ

  • 思っていたより、Nxを使うと遅い
  • defnを使わない記述の3m秒だったら、なんとか使えるかも。でももう一桁速くなってほしい
  • Nxを使わない(1次元の処理を3回実行)記述なら余裕
  • Intel CPUは速い

EXLAが使えると状況はかわるかもしれません。Raspi ZeroではEXLAのコンパイルができてないので、一旦 Raspi 4で比較してみたい。

結果めも

結果1

iex(1)> Main.main()
[10881, 9542, 9326, 9430, 8546, 8083, 7798, 8698, 7986, 8534]
iex(2)>

結果2

defnを使わないで、Nxの関数を呼び出した場合

pid.ex
  def out(%__MODULE__{kp: kp, ki: ki, kd: kd, guard: guard, integration: integration, error_last: error_last} = pid, setpoint, y_value, dt) do
    # {integration, error, u} = out_nx(kp,ki,kd,guard, integration,error_last, setpoint, y_value, dt)
    error = Nx.subtract(setpoint, y_value)
    integration = Nx.min(Nx.add(integration, Nx.multiply(error, dt)), guard)
    d_error = Nx.subtract(error, error_last)
    u = Nx.add(Nx.add(Nx.multiply(kp, error), Nx.multiply(ki, integration)), Nx.divide(Nx.multiply(kd, d_error), dt))

    %Pid{pid| integration: integration, error_last: error, u: u}
  end
iex(1)> Main.main()
[4320, 4158, 3477, 4261, 3670, 3232, 4136, 3068, 3771, 3911]
iex(2)>

結果3

Nxを使わないで、1次元の処理を3回行った場合

iex(1)> Main.main()
[171, 106, 100, 99, 100, 97, 205, 227, 105, 99]
iex(2)>

結果4

Nx版をIntel CPU環境で実行した場合

iex(1)> Main.main()
[6792, 282, 344, 249, 181, 304, 194, 254, 356, 212]
iex(2)> 
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?