独自のモデリング要素の作成
MotionSolve Pythonインターフェースでは、高度なモデリング要素を作成し、使用できます。以下の例で詳しく説明します。以下の例は、MotionSolve Pythonインターフェースを使用しなくても行えますが、その場合、作成も使用も難しくなります。
MotionSolveでは、汎用のモデリング要素と現象固有のモデリング要素の豊富なライブラリが提供されています。これらを組み合わせて、新しい挙動を示す新しいモデリング要素を定義できます。このプロセスは、集約と呼ばれます。MotionSolveでは、このようなオブジェクトを表現するための特別なCompositeクラスが提供されています。
Compositeクラス
Compositeは、パート、マーカー、フォース、微分方程式、代数方程式、その他のMotionSolveプリミティブなどのエンティティを集約することを可能にする特別なクラスです。Compositeクラスオブジェクトは他のソルバーオブジェクトと同じように動作しますが、扱いやすいようにまとめてパッケージ化されたエンティティの集合という点が異なります。これらのオブジェクトはアトミックエンティティとして動作するため、複数回インスタンス化できます。作成する複合クラスは、組み込みのCompositeクラスから継承されます。次の例では、独自の複合クラスを作成する方法を示します。
例えば、ローパスフィルタButterworthFilterという複合クラスを作成するとします。リンク先ではButterworthフィルタについて詳しく説明していますが、ここでは簡単に説明します。ローパスフィルタを使用すると、低周波信号を通過させることができます。このフィルタは高周波信号を減衰させます。カットオフ周波数によって、減衰が始まる周波数が定義されます。周波数が高くなるほど、減衰の度合が高まります。Butterworthフィルタには次数があります。次数が大きいほど、カットオフが急激になります。
from msolve import *
class ButterworthFilter (Composite):
from msolve import *
class ButterworthFilter (Composite):
inputSignal = Function()
outputSignal = Function ()
order = Int (3)
2つの特別なメソッドcreateChildrenおよびupdateChildrenを記述して、このクラスの挙動を定義する必要があります。オプションとして検証メソッドvalidateSelfを記述することもできます。
- createChildren: その複合クラスで必要なすべてのMotionSolveエンティティを作成するために使用されます。複合クラスオブジェクトが作成されると、createChildrenメソッドが呼び出されます。この呼び出しは、複合オブジェクトの作成時に一度だけ行われます。createChildren内で、さまざまなMotionSolveオブジェクトをインスタンス化し、これらのオブジェクトの不変のプロパティを定義します。
- updateChildren: このクラスのすべての変更可能なプロパティを定義するために使用されます。例えば、Butterworthフィルタの係数はそのフィルタの次数に依存します。同様に、処理対象の入力信号を変更できます。updateChildrenメソッドは、フィルタの次数と入力信号を定義します。これらをcreateChildrenメソッドで定義することもできますが、これはオプションです。
- validateSelf: 入力をチェックして、ButterworthFilterオブジェクトに渡されたデータが物理的に意味があることを確認します。
from msolve import *
class ButterworthFilter (Composite):
"""The Butterworth filter is a type of signal processing filter designed to have
as flat a frequency response as possible in the passband. It is also referred
to as a maximally flat magnitude filter and it is generally used to eliminate
high frequency content from an input signal.
"""
inputSignal = Function ()
outputSignal = Function ()
order = Int (3)
#- topology ----------------------------------------------------------------
def createChildren (self):
"""This method is called once when the object is created. It is used to
define all the 'immutable' properties of the class. The Butterworth
filter consists of the following:
* A string containing the function expression defining the input signal
* A VARIABLE that defines the input signal
* A TFSISO that captures the input to output relationship
* An ARRAY of type 'X' to hold the internal states of the TFSISO
* An ARRAY of type 'Y' to hold the output from TFSISO
* An ARRAY of type 'U' to hold the input signal to the TFSISO
* A string that defines the function expression to access the output
"""
self.var = Variable (function=self.inputSignal)
x = Array (type="X")
y = Array (type="Y")
u = Array (type="U", size=1, variables=[self.var])
num = [1]
den = [1, 2, 2, 1]
self.tf = Tfsiso (label="Butterworth Filter", x=x, y=y, u=u, numerator=num,
denominator=den)
self.outputSignal = "ARYVAL({id},1)".format(id=y.id)
#- Update Children -----------------------------------------------------------
def updateChildren (self):
"""This is called when the property value changes to propagate the change
to the child objects
"""
self.var.function = self.inputSignal
if self.order == 1:
self.tf.denominator = [1, 1]
elif self.order == 2:
self.tf.denominator = [1, math.sqrt(2), 1]
elif self.order == 3:
self.tf.denominator = [1, 2, 2, 1]
elif self.order == 4:
self.tf.denominator = [1, 2.6131, 3.4142, 2.6131, 1]
#- validate ----------------------------------------------------------------
def validateSelf (self, validator):
validator.checkGe ("order", 1)
validator.checkLe ("order", 4)
ButterWorth複合クラスを作成したら、これを任意のモデルで“アトミック”オブジェクトとして使用できます。以下の例は、全体座標系のy方向に振動する非線形マススプリングダンパです。重力は負のy方向に作用しています。T=1秒においてy方向に100Nの衝撃力が加えられます。この衝撃は11秒に終了します。
###############################################################################
## The Model ################################################################
###############################################################################
def butterworthFilter ():
m = Model ()
Units (mass="KILOGRAM",length="MILLIMETER", time="SECOND", force="NEWTON")
Accgrav (jgrav=-9800)
Integrator (error=1e-7)
ground = Part (ground=True)
# Mass
block = Part (mass=1.4702,ip=[44144.717,44144.717,73.5132,0,0,0])
block.cm = Marker (part=block, qp=[0,10,0], zp=[0,11,0])
# Translational joint
jjm = Marker (part=ground, qp=[0,0,0], zp=[0,1,0])
jnt = Joint (type="TRANSLATIONAL", i=block.cm, j=jjm)
# Nonlinear Spring-damper
fexp = "-10*(DY({i},{j})-10)**3 -1e-3*VR({i},{j})".format(i=block.cm.id, j=jjm.id)
nlspdp = Sforce (type="TRANSLATION", i=block.cm, j=jjm, function=fexp)
# Impulse force
iforce = "STEP(TIME, 1, 0, 1.04, 10) *STEP(TIME, 1.05, 10, 1.1, 0)"
sfim = Marker (part=block, qp=[0,10,0], zp=[0,11,0])
impulse = Sforce (type="TRANSLATION", actiononly=True, i=sfim, j=jjm, function=iforce)
# Filter
inputSignal = "VY({i}, {j})".format(i=block.cm.id, j=jjm.id)
m.filt = ButterworthFilter (inputSignal=inputSignal, order=1)
# Requests
m.r1 = Request (type="DISPLACEMENT", i=block.cm, j=jjm)
m.r2 = Request (type="VELOCITY", i=block.cm, j=jjm)
m.r3 = Request (type="ACCELERATION", i=block.cm, j=jjm)
m.r4 = Request (type="FORCE", i=block.cm, j=jjm)
m.r5 = Request (f2=m.filt.inputSignal, f3=m.filt.outputSignal)
m.r6 = Request (type="FORCE", i=sfim, j=jjm)
return m
###############################################################################
## Entry Point ################################################################
###############################################################################
if __name__ == "__main__":
m = butterworthFilter ("butterworth-1")
m.simulate (type="DYNAMICS", end=5, dtout=0.005)
m.reset()
m = butterworthFilter ("butterworth-2")
m.filt.order = 2
m.simulate (type="DYNAMICS", end=5, dtout=.005)
m.reset()
図 1.
図 2.
DocString
Pythonに組み込まれている強力なヘルプ機能では、モジュール、クラス、および関数にドキュメントを関連付けることができます。オブジェクト定義の先頭ステートメントとして文字列定数を追加することにより、このようなオブジェクトをドキュメントできます。docstringでは、その関数やクラスの役割を記述します。そうすることで、ユーザーやプログラマーは、クラス、関数、またはオブジェクトのインスタンスに対するヘルプ機能を呼び出すことによって、実行時にオブジェクトのクラスやメソッドについて調べることができます。
help (ButterworthFilter)
"""
Assuming this is file mymodule.py, then this string, being the
first statement in the file, will become the "mymodule" module's
docstring when the file is imported.
"""
class MyClass(object):
"""The class's docstring"""
def my_method(self):
"""The method's docstring"""
def my_function():
"""The function's docstring"""
摩擦のモデリング例
- 動摩擦
- 静摩擦またはクーロン摩擦
- 速度依存の摩擦現象(変化する最大静止摩擦力や摩擦遅延など)
- VS= 静摩擦から動摩擦への遷移速度
- MUS = 静摩擦係数
- MUD = 動摩擦係数
- K0 = ブリッスル剛性
- K1 = ブリッスル減衰
- K2 = 粘性係数
|
## ジョイント内の速度 |
|
## ストライベック係数 |
|
## 垂直抗力 |
|
## クーロン摩擦 |
|
## 静摩擦 |
|
|
|
## ブリッスルたわみzを定義するODE |
|
## 摩擦力 |
- A DIFF defining the bristle deflection
- A FORCE defining the friction element
from msolve import *
################################################################################
## LuGre Friction Element######################################################
################################################################################
class LuGre (Composite):
"""
Create a friction force on a translational joint using the LuGre friction model.
The LuGre friction force consists of 4 atomic elements:
1. A DIFF defining the bristle deflection
2. A MARKER defining the point of action of the friction force
3. A FORCE defining the friction element
4. A REQUEST capturing the friction force on the block
"""
joint = Reference (Joint)
vs = Double (1.E-3)
mus = Double (0.3)
mud = Double (0.2)
k0 = Double (1e5)
k1 = Double (math.sqrt(1e5))
k2 = Double (0.4)
#- topology----------------------------------------------------------------
def createChildren (self):
"""This is called when the object is created so the children objects
"""
# The DIFF defining bristle deflection
self.diff = Diff (routine=self.lugre_diff, ic=[0,0])
# The MARKER on which the friction force acts
self.im = Marker ()
# The FORCE defining the friction force
self.friction = Sforce (type="TRANSLATION", actiononly=True,
routine=self.lugre_force)
# The REQUEST capturing the friction force
self.request = Request (type="FORCE", comment="Friction force")
#- Update Children -----------------------------------------------------------
def updateChildren (self):
"""This is called when the property value changes to propagate the change
to the child objects
"""
self.friction.i = self.joint.i
self.friction.j = self.joint.j
self.im.setValues (body=self.joint.i.body, qp=self.joint.i.qp, zp=self.joint.i.zp)
self.request.setValues (i=self.im, j=self.joint.j, rm=self.joint.j)
#- validate ----------------------------------------------------------------
def validateSelf (self, validator):
validator.checkGe0 ("VS")
validator.checkGe0 ("MUS")
validator.checkGe0 ("MUD")
validator.checkGe0 ("K0")
validator.checkGe0 ("K1")
validator.checkGe0 ("K2")
if self.mud > self.mus:
msg = tr("Mu dynamic({0}) must be <= Mu static ({1})",
self.mud, self.mus)
validator.sendError(msg)
#- Lugre Diff ------------------------------------------------------------
def lugre_diff (self, id, time, par, npar, dflag, iflag):
"Diff user function"
i = self.joint.i
j = self.joint.j
vs = self.vs
mus = self.mus
mud = self.mud
k0 = self.k0
z = DIF(self)
v = VZ(i,j,j,j)
N = math.sqrt (FX(i,j,j)**2 + FY(i,j,j)**2)
fs = mus*N
fc = mud*N
p = -(v/vs)**2
g = (fc + (fs - fc) * math .exp(p))/k0
if iflag or math.fabs(g) <1e-8:
return v
else:
return v - math.fabs(v) * z / g
#- friction_force -----------------------------------------------------------
def lugre_force (self, id, time, par, npar, dflag, iflag):
"Friction Force user function"
i = self.joint.i
j = self.joint.j
diff = self.diff
k0 = self.k0
k1 = self.k1
k2 = self.k2
v = VZ (i,j,j,j)
z = DIF (diff)
zdot = DIF1 (diff)
F = k0*z + k1*zdot + k2*v
return -F