飛行物体の実験室

ワイパーの動きをするギザギザ関数「三角波」をRISC-ⅴで出力するには?

アイキャッチ
2024-05-04

ワイパー関数が欲しいんです

訳あって0〜180°対応のサーボモータをワイパーのように動かしたいとなりました。

指定する角度は0, 18, 36...180, 162, ... 18, 0, 18 ...(以後ループ)となります。

一度に動く角度をd(ここでは18°)と置き、プログラムとして書くなら、こんなものがつくれるかと思います。

i ← 0 
angle ← 0
d ← 18 
T ← 180
while True:
  angle ← abs(i - T)   
  i ← mod(i+d, T*2)
endwhile

これなら目的の動きを実現できるでしょう。

しかし、ふと考えます。

これを1行分の式に落とし込むことはできるのか???

つまり、ループの部分の3行分を1本の式に、変数を1つで操作できるか?ということです。

数式に起こす

ここから話がこじれてきます。

まず、数学的にどうなるのかという疑問が浮かびます。

GPTにも聞かず、自ら数式を組み立ててできたのが以下のものです。

知っている関数を弄くり倒した結果、何とか完成しました。

しかしこの数式、自分でも「汚い」と感じます。

それもそのはずです。この式は面倒なゴリ押しを通して出来上がっているからです。

まず斜線を取り出します。

それを反転させます。

この後sin波から無理やりに0→1→0→1...を取り出し、先程の関数を別々に適用しているだけです。

いくらなんでも、もっと簡潔かつ美しく表現する方法があるという確信がありました。

そこでいくつかの知り合いに聞いてみることにしました。

すると、以下のツイート群を見つけたとの一報が入ります。

まさに私の求めていた情報です。

この時点で、私はこれを「三角波」と呼ぶ事を知りました。

ここから以下の式が判明します。

minとmodを使った簡潔な記法です。

この式は私のやっていた事の最終形であるとの確信がありました。疑似コードにするならこうでしょうか。

i ← 0 
angle ← 0
d ← 18
T ← 180
while True:
  if mod(i, T) < mod(-i, T):
    angle ← mod(i, T)
  else:
    angle ← mod(-i, T)

  i ← i+d
endwhile

先程の疑似コードと比較してもわかるように、コードとして少々広がっています。一方、コンピュータ的にはどちらのコードが良いのかという疑問が生じます。

コンピュータへの負担を考える前に、別の知り合いから得た異なる三角波表現を紹介します。

アークサインとサインを使用したこれまた簡潔で綺麗な記述です。

恐らく、数学的に記すならこれが美しいのかなと素人ながらに感じます。

他にフーリエ級数展開という方法もありますがこちらは恐らく長くなるので割愛します。

基準の選定

さて、コンピュータとしての負荷を考えていきます。

負荷の指標として2つ考えます。使用するメモリの大きさとプログラム自体の長さです。

これらを具体的に知るために、アセンブラを組んでいきます。

私はアセンブラを使って実機を動かしたことが無いので、まずはアセンブラを動かすアーキテクチャ(アセンブラが何の式を使えるのか=命令セット)を考えます。

少し習ったことのあるRISC-ⅴで調べます。

これらの疑似コードで改めて考える必要があるのは、mod演算・絶対値の計算の実現方法です。

基本命令セットは32bitのRV32Iとします。

「RISC-ⅴ原典(書籍)」などで調べると、RV32Iそのものにはmod・絶対値・積といった命令はありません。

しかし、「拡張命令セット」であるRV32Mの中に乗算除算、最大最小などが入っています。

今回は乗算、除算、剰余算のできるRV32Mを前提に、アセンブラでコードを組んでいきます。

RV32Mで仮のアセンブラを組む

まずは最小に考えた疑似コードをアセンブラにします。

変数名はそのまま書きますが、実際はレジスタです。

追加のレジスタはrで表しています。

addi     i, zero,   0
addi angle, zero,   0
addi     d, zero,  18
addi     T, zero, 180
add     r0,    T,   T
loop:
  sub angle,    i,      T
  blt angle, zero, endabs
  mul angle,   -1,  angle
endabs:
  add  i, i,  T
  remu i, i, r0
  jal  loop

コード長は11(44Byte)、使用したレジスタは合計5つでした。

次のminとmodを使用した方法を書き連ねます。

addi     i, zero, 0
addi angle, zero, 0
addi     d, zero, 18
addi     T, zero, 180
loop:
  remu    r0,    i, T
  sub     r1, zero, i
  remt    r1,    i, T
  blt     r1,   r0, ifthen
  sw   angle,   r0
  jal  endif
ifthen:
  sw   angle,   r1
endif:
  add      i,    i, d
  jal   loop

コード長は13(52Byte)、使用したレジスタは合計6つでした。

つまり、最初に考えたコードがアセンブラ上は優秀ということが推察できました。

実行したわけではないので、ミスが存在する可能性は否めませんがこれがコンピュータ的には一番いいのかなという結論に至りました。

それではまたお会いしましょう。

飛行物体