キネマティックモデリングは難しくありません

ロボットの作成を始めたいとずっと思っていましたが、常に十分な無料のお金、時間、スペースがありません。 だから私は彼らの仮想モデルを書くつもりでした!



これを可能にする強力なツールは、サードパーティのプログラム( Modelica )またはプロプライエタリ(Wolfram Mathematica、さまざまなCADシステム)とのインターフェースが難しいため、 Juliaで自転車を作ることにしました。 その後、すべての稼働時間をROSサービスにドッキングできます。



ロボットの動きは非常に遅く、メカニクスはエネルギーが最も少ない状態にあり、設計とサーボによって設定された制約を受けると想定しています。 したがって、 JuMPパッケージが必要な制約の最適化問題を解決するだけで十分です(非線形最適化の場合、依存関係で指定されていないIpoptパッケージが必要になります(代わりに独自のライブラリを使用できますが、無料のものに制限したい)。個別にインストール)。 Modelicaのように微分方程式を解くことはしませんが、このための十分に開発されたパッケージ、たとえば「 DASSL 」があります。



リアクティブプログラミング(ライブラリ「 リアクティブ 」)を使用してシステムを制御します。 「 IJulia 」、「 Interact 」、および「 Compose 」を必要とするメモ帳(Jupyter)で描画します。 便宜上、「 MacroTools 」が引き続き必要です。



パッケージをインストールするには、Julia REPLでコマンドを実行する必要があります



foreach(Pkg.add, ["IJulia", "Ipopt", "Interact", "Reactive", "JuMP", "Compose", "MacroTools"])
      
      





図に概略的に示されている単純な2次元システムを考えてみましょう。

そこにある赤い線はバネを示し、黒い線は伸びないロープ、小さなレース-無重量ブロック、大きな-負荷を示しています。 ロープには長さがあり、バネには長さと剛性があります。 変数座標を呼び出します:



(x、y)-貨物の座標(大きな円)

(xctl、yctl)-負荷に関係する長さ1.7のロープの「制御端」の座標。

(xp、yp)-ロープに沿ってスライドできる無重量ポイントブロックの座標。


長さ0.1および剛性1の1つのバネが(0.3)に固定され、荷重に取り付けられています。 長さ0.15、剛性5の2番目のバネは、荷重と、ポイント(5.1)および(4.5.5)で固定された長さ6のロープに沿ってスライドするブロックを接続します。



(パラメーターは進化的に選択されたので、新しい機能を追加するときのパラメーターの動作を明確に理解できました)



  @wire(x,y, xctl,yctl, 1.7) @wire(xp, yp, 5.0,1.0, 4.5,5.0, 6.0) @energy([w(x,y, 0,3, 1, 0.1), w(x,y, xp,yp, 5, 0.15), 0.4*y])
      
      





完全な方程式解法関数コード
 function myModel(xctl, yctl) m = Model() @variable(m, y >= 0) @variable(m, x) @variable(m, yp) @variable(m, xp) @wire(x,y, xctl,yctl, 1.7) @wire(xp, yp, 5.0,1.0, 4.5,5.0, 6.0) @energy([w(x,y, 0,3, 1, 0.1), w(x,y, xp,yp, 5, 0.15), 0.4*y]) status = solve(m) xval = getvalue(x) yval = getvalue(y) xpval = getvalue(xp) ypval = getvalue(yp) # print("calculate $status $xval $yval for $xctl, $yctl\n") (status, xval, yval, xpval, ypval) end
      
      







ワイヤマクロは、指定された長さの「拡張不可能なロープバインド」の制限を2つのオブジェクトまたは2つのオブジェクトと1つのブロックに設定します。



エネルギーマクロは最小化されたエネルギー関数を表します-特別な用語wはバネ(与えられた剛性と長さ)を表し、0.4 * yは重力場の負荷のエネルギーであり、特別な構文を考えるには単純すぎます。



これらのマクロは、ライブラリNLconstraintおよびNLobjectiveで表されます(線形制約と目的はより効果的ですが、関数に対処できません)。



 @NLconstraint(m, (x-xctl)^2+(y-yctl)^2 <= 1.7) @NLconstraint(m, sqrt((5.0-xp)^2+(1.0-yp)^2) + sqrt((4.5-xp)^2+(5.0-yp)^2) <= 6.0) @NLobjective(m, Min, 1*(sqrt(((x-0)^2 + (y - 3)^2)) - 0.1)^2 + 5*(sqrt((x - xp)^2 + (y - yp)^2) - 0.15)^2 + 0.4*y)
      
      





等しいのではなく、以下の制約を使用すると、ロープはたるみますが、伸びません。 2つのポイントについて、それらを「リジッドロッド」で接続し、対応するマクロを置き換える必要がある場合は、厳密な等価性を使用できます。



マクロコード
 macro wire(x,y,x0,y0, l) v = l^2 :(@NLconstraint(m, ($x0-$x)^2+($y0-$y)^2 <= $v)) end macro wire(x,y, x0,y0, x1,y1, l) :(@NLconstraint(m, sqrt(($x0-$x)^2+($y0-$y)^2) + sqrt(($x1-$x)^2+($y1-$y)^2) <= $l)) end calcenergy(d) = MacroTools.@match d begin [t__] => :(+$(map(energy1,t)...)) v_ => energy1(v) end energy1(v) = MacroTools.@match v begin w(x1_,y1_, x2_,y2_, k_, l_) => :($k*(sqrt(($x1 - $x2)^2 + ($y1 - $y2)^2) - $l)^2) x_ => x end macro energy(v) e = calcenergy(v) :(@NLobjective(m, Min, $e)) end
      
      







次に、コントロールについて説明します。



 xctl = slider(-2:0.01:5, label="X Control") xctlsig = signal(xctl) yctl = slider(-1:0.01:0.5, label="Y Control") yctlsig = signal(yctl)
      
      





それらをシステムに接続しましょう:



 ops = map(myModel, xctlsig, yctlsig)
      
      





これをノートブックに表示します。



ヘルパー関数のペア
 xscale = 3 yscale = 3 shift = 1.5 pscale(x,y) = ((x*xscale+shift)cm, (y*yscale)cm) function l(x1,y1, x2,y2; w=0.3mm, color="black") compose(context(), linewidth(w), stroke(color), line([pscale(x1, y1), pscale(x2, y2)])) end
      
      







 @manipulate for xc in xctl, yc in yctl, op in ops compose(context(), # text(150px, 220px, "m = $(op[2]) $(op[3])"), # text(150px, 200px, "p = $(op[4]) $(op[5])"), circle(pscale(op[2], op[3])..., 0.03), circle(pscale(op[4], op[5])..., 0.01), l(op[2], op[3], op[4], op[5], color="red"), l(op[2], op[3], 0, 3, color="red"), l(op[2], op[3], xc, yc), l(op[4], op[5], 5, 1), l(op[4], op[5], 4.5,5) ) end
      
      





コードのフルバージョン
 using Interact, Reactive, JuMP, Compose, MacroTools macro wire(x,y,x0,y0, l) v = l^2 :(@NLconstraint(m, ($x0-$x)^2+($y0-$y)^2 <= $v)) end macro wire(x,y, x0,y0, x1,y1, l) :(@NLconstraint(m, sqrt(($x0-$x)^2+($y0-$y)^2) + sqrt(($x1-$x)^2+($y1-$y)^2) <= $l)) end calcenergy(d) = MacroTools.@match d begin [t__] => :(+$(map(energy1,t)...)) v_ => energy1(v) end energy1(v) = MacroTools.@match v begin w(x1_,y1_, x2_,y2_, k_, l_) => :($k*(sqrt(($x1 - $x2)^2 + ($y1 - $y2)^2) - $l)^2) x_ => x end macro energy(v) e = calcenergy(v) :(@NLobjective(m, Min, $e)) end function myModel(xctl, yctl) m = Model() @variable(m, y >= 0) @variable(m, x) @variable(m, yp) @variable(m, xp) @wire(x,y, xctl,yctl, 1.7) @wire(xp, yp, 5.0,1.0, 4.5,5.0, 6.0) @energy([w(x,y, 0,3, 1, 0.1), w(x,y, xp,yp, 5, 0.15), 0.4*y]) status = solve(m) xval = getvalue(x) yval = getvalue(y) xpval = getvalue(xp) ypval = getvalue(yp) # print("calculate $status $xval $yval for $xctl, $yctl\n") (status, xval, yval, xpval, ypval) end xctl = slider(-2:0.01:5, label="X Control") xctlsig = signal(xctl) yctl = slider(-1:0.01:0.5, label="Y Control") yctlsig = signal(yctl) ops = map(myModel, xctlsig, yctlsig) xscale = 3 yscale = 3 shift = 1.5 pscale(x,y) = ((x*xscale+shift)cm, (y*yscale)cm) function l(x1,y1, x2,y2; w=0.3mm, color="black") compose(context(), linewidth(w), stroke(color), line([pscale(x1, y1), pscale(x2, y2)])) end @manipulate for xc in xctl, yc in yctl, op in ops compose(context(), # text(150px, 220px, "m = $(op[2]) $(op[3])"), # text(150px, 200px, "p = $(op[4]) $(op[5])"), circle(pscale(op[2], op[3])..., 0.03), circle(pscale(op[4], op[5])..., 0.01), l(op[2], op[3], op[4], op[5], color="red"), l(op[2], op[3], 0, 3, color="red"), l(op[2], op[3], xc, yc), l(op[4], op[5], 5, 1), l(op[4], op[5], 4.5,5) ) end
      
      







ノートブックを起動する簡単な方法:Julia REPLで、 using IJulia notebook()



ます。 これはブラウザ「 http:// localhost:8888 / tree 」で開く必要があります。 そこで、「new」/「Julia」を選択し、入力フィールドにコードをコピーして、Shift-Enterを押す必要があります。



All Articles