第7章 複合運動 7.1 多重振り子 【リスト7-1】二重振り子 from tkinter import * from tkinter import messagebox import random, math, time def array(N1, N2=0, N3=0): if N2 == 0 and N3==0: return [0 for i in range(N1)] elif N3 == 0: return [[0 for i in range(N2)] for j in range(N1)] else: return [[[0 for i in range(N3)] for j in range(N2)] for k in range(N1)] def Sim(root):#■シミュレーション global t, dt, ID1, ID2, aTH, bTH, aVel, bVel,adTH, bdTH global PaG, PbG, LBL,aAcc, bAcc, aX, aY, bX,bY t += dt; root.title("time = %9.6f" % t) CosDTh=math.cos(aTH[ID2] - bTH[ID2]) SinDTh=math.sin(aTH[ID2] - bTH[ID2]) SinATh=math.sin(aTH[ID2]) ; SinBTh=math.sin(bTH[ID2]) aAlf2 =aVel[ID2]*aVel[ID2]; bAlf2 =bVel[ID2]*bVel[ID2] #おもり a の計算 aAcc[ID1]=(dt*(myuC*SinDTh*(LbL*bAlf2+aAlf2*CosDTh) + PaG *SinATh - myuC*PbG*SinBTh*CosDTh) / (myuC*(CosDTh*CosDTh) - 1)) aVel[ID1]=(aVel[ID2]+aAcc[ID1])*(1-Myu) # 速度計算 adTH[ID1]= aVel[ID1]*dt; aTH[ID1] = aTH[ID2]+adTH[ID1] aX[ID1] = PaL*math.sin(aTH[ID1]) # 位置計算 aY[ID1] = PaL*math.cos(aTH[ID1]) #おもり b の計算 bAcc[ID1]=(dt*(aAlf2*SinDTh-aAcc[ID2]/dt*CosDTh - PbG*SinBTh)/LbL) bVel[ID1]=(bVel[ID2]+bAcc[ID1])*(1-Myu) #速度計算 bdTH[ID1]= bVel[ID1]*dt; bTH[ID1]= bTH[ID2]+ bdTH[ID1] bX[ID1]= aX[ID1] + PbL*math.sin(bTH[ID1]) # 位置計算 bY[ID1]= aY[ID1] + PbL*math.cos(bTH[ID1]) def draw(canvas, ID1):#■おもり位置の変更 global XStart, YStart, SC, aX, bX, aY, bY global L1ID, L2ID, B2ID, B3ID X1 = XStart; Y1 = YStart X2 = aX[ID1]*SC + XStart; Y2 = aY[ID1]*SC + YStart X3 = bX[ID1]*SC + XStart; Y3 = bY[ID1]*SC + YStart canvas.coords(L1ID, X1, Y1, X2, Y2) canvas.coords(L2ID, X2, Y2, X3, Y3) canvas.coords(B2ID, X2-4, Y2-4, X2+4, Y2+4) canvas.coords(B3ID, X3-4, Y3-4, X3+4, Y3+4) # 軌跡を表示するときは,上1行をコメントにし以下の5行のコメントを外す # BXY=canvas.coords(B3ID) # XX=(BXY[0]+BXY[2])/2;YY=(BXY[1]+BXY[3])/2; # canvas.coords(B3ID, XX, YY, XX, YY) # canvas.itemconfig(B3ID, fill="green", outline="") # B3ID=canvas.create_oval(X3-4, Y3-4, X3+4, Y3+4, fill="red") #メイン処理 root=Tk(); root.title("二重振り子") canvas=Canvas(root, width=240,height=250, bg="white");canvas.pack() #パラメータ設定 PI = math.pi; SC = 2000; XStart = 120; YStart = 10 PaL =0.05; PbL = 0.05 # 棒の長さ PaM =0.02; PbM = 0.02 # おもりの重さ g =9.8 # 重力加速度 dt =0.005 # 時間刻み Myu =0.001 # 粘性抵抗(人工拡散項を考慮する) LOOP=20000 # シミュレーション回数 # 事前計算 myuC=PbM/(PaM+PbM) PaG=g/PaL; PbG=g/PbL; LbL=PbL/PaL #初期設定(接頭文字a, bがおもりを示す) aTH=array(2); bTH=array(2) # θ aTH[1]=30*PI/180; bTH[1]=10*PI/180 adTH =array(2); bdTH =array(2) # Δθ aAcc=array(2); bAcc=array(2) # 加速度 aVel =array(2); bVel =array(2) # 速度 aX=array(2); aY=array(2) # a の XY 座標 bX=array(2); bY=array(2) # b の XY 座標 aX[1] = PaL * math.sin(aTH[1]) aY[1] = PaL * math.cos(aTH[1]) bX[1] = aX[1] + PbL * math.sin(bTH[1]) bY[1] = aY[1] + PbL * math.cos(bTH[1]) t = 0; ID1 = 0; ID2 = 1 #初期描画 X1 = XStart; Y1 = YStart X2 = aX[1]*SC + XStart; Y2 = aY[1]*SC + YStart X3 = bX[1]*SC + XStart; Y3 = bY[1]*SC + YStart L1ID=canvas.create_line(X1, Y1, X2, Y2, fill="blue", width=2) L2ID=canvas.create_line(X2, Y2, X3, Y3, fill="blue", width=2) B1ID=canvas.create_oval(X1-2, Y1-2, X1+2, Y1+2, fill="#FF7700") B2ID=canvas.create_oval(X2-4, Y2-4, X2+4, Y2+4, fill="red") B3ID=canvas.create_oval(X3-4, Y3-4, X3+4, Y3+4, fill="red") root.update() #シミュレーション開始 for k in range(LOOP): time.sleep(0.01) Sim(root); draw(canvas, ID1); root.update() ID = ID1; ID1 = ID2; ID2 = ID 【リスト7-2】多重振り子 from tkinter import * from tkinter import messagebox import random, math, time def array(N1, N2=0, N3=0): if N2 == 0 and N3==0: return [0 for i in range(N1)] elif N3 == 0: return [[0 for i in range(N2)] for j in range(N1)] else: return [[[0 for i in range(N3)] for j in range(N2)] for k in range(N1)] def Sim(root):#■シミュレーション global t, dt, g, KFact,PaM, F, ID1, ID2, KFact, aTH, adTH t += dt; root.title("time = %9.6f" % t) mG0 = 0; DMg = PaM * g for i in range(Num): mG0 += DMg; F[i]= -mG0 * math.sin(aTH[i][ID1]) adTH[i][ID2]= adTH[i][ID1]+ F[i]*dt; mG0 *=KFact TH = 0 for i in range(Num-1, -1, -1): TH += adTH[i][ID1] adTH[i][ID2]= adTH[i][ID2]* (1 - Myu) aTH[i][ID2]= aTH[i][ID1]+ adTH[i][ID2]*dt def draw(canvas, ID1):#■おもり位置の変更 global XStart, YStart, SC, aX, bX, aY, bY,PaL global LID,BID X1 = XStart; Y1 = YStart for i in range(Num-1,-1, -1): cosTH = math.cos(aTH[i][ID1]); sinTH = math.sin(aTH[i][ID1]) X2 = PaL * sinTH + X1; Y2 = PaL * cosTH + Y1 canvas.coords(LID[i], X1, Y1, X2, Y2) canvas.coords(BID[i], X2-2, Y2-2, X2+2, Y2+2) X1 = X2; Y1 = Y2 #メイン処理 root=Tk(); root.title("二重振り子") canvas=Canvas(root, width=400,height=500, bg="white");canvas.pack() #パラメータ設定 PI = math.pi; SC = 2000; XStart = 200; YStart = 10 PaL =5 # 棒の長さ PaM =0.1 # おもりの重さ Num=50 # 分割数 TH=30*PI/180 # 初期角度 g =9.8 # 重力加速度 dt =0.05 # 時間刻み Myu =0.001 # 粘性抵抗(人工拡散項を考慮する) KFact=1 #伝達係数 LOOP=2000 # シミュレーション回数 #初期設定(接頭文字a, bがおもりを示す) a=array(Num,2);F=array(Num);LID=array(Num); BID=array(Num) aTH=array(Num,2); adTH=array(Num,2) # θ, Δθ for i in range(Num): aTH[i][0]=TH; adTH[i][0]=0 t=0; ID1=0;ID2=1 #初期描画 X1 = XStart; Y1 = YStart canvas.create_oval(X1-2, Y1-2, X1+2, Y1+2, fill="yellow") for i in range(Num-1,-1, -1): cosTH = math.cos(aTH[i][ID1]); sinTH = math.sin(aTH[i][ID1]) X2 = PaL * sinTH + X1; Y2 = PaL * cosTH + Y1 LID[i]=canvas.create_line(X1, Y1, X2, Y2, fill="blue", width=1) BID[i]=canvas.create_oval(X2-2, Y2-2, X2+2, Y2+2, fill="red") X1 = X2; Y1 = Y2 root.update() #シミュレーション開始 for k in range(LOOP): time.sleep(0.1); Sim(root); draw(canvas, ID1); root.update() ID = ID1; ID1 = ID2; ID2 = ID 7.2 おもりとバネ 【リスト7-3】おもりをゴム紐で引っ張る動き from tkinter import * import time root=Tk();root.title("おもりをゴム紐で引っ張る動き") canvas=Canvas(root,width=500,height=500, bg="white");canvas.pack() canvas.focus_set() Kfact=0.05; Rfact=0.5; mouseX=100; mouseY=100 PX = 100; PY = 100; VX = 0; VY = 0 LID=canvas.create_line(mouseX, mouseY, PX, PY, fill="blue",width=2) BID=canvas.create_oval(PX-5,PY-5,PX+5,PY+5,fill="red") #■計算 def calc(canvas): global Kfact, Rfact, PX, PY, VX, VY, mouseX, mouseY, LID, BID VX = (VX - Kfact*(PX - mouseX))*Rfact VY = (VY - Kfact*(PY - mouseY))*Rfact PX += VX; PY += VY canvas.coords(LID,mouseX, mouseY, PX, PY) canvas.coords(BID,PX-5,PY-5,PX+5,PY+5) #■マウス移動 def MouseMotion(event): global mouseX, mouseY mouseX=event.x; mouseY= event.y #図形上でのクリックイベント def deleteLine(event): canvas.delete('current') print("Line select Button No.",event.num) #■バインドの指定 canvas.bind("", MouseMotion) #■メインループ while True: root.update();time.sleep(0.01); calc(canvas) 【リスト7-4】 棒でつながった球の運動 from tkinter import * import time,math,winsound root=Tk();root.title("棒でつながった球") canvas=Canvas(root,width=500,height=270, bg="white");canvas.pack() canvas.focus_set() P1M = 1500; P2M = 200; SLen=100; Myu = 0.02; Kfact = 10; G=0.98; TimerInt = 0.01 Y1 =10; X1 =200; X2 = X1 + SLen; Y2 = 30 V1X= 0; V1Y= 0; V2X= 0; V2Y=0; canvas.create_rectangle(0,250,510,280, fill="#775522",outline="") canvas.create_line(0,250,510,250, fill="black",width=2) LID=canvas.create_line(X1,Y1, X2, Y2, fill="blue",width=2) B1ID=canvas.create_oval(X1-6,Y1-6,X1+6,Y1+6,fill="red") B2ID=canvas.create_oval(X2-3,Y2-3,X2+3,Y2+3,fill="yellow") #■計算 def calc(canvas): global Kfact, P1M, P2M, SLen, X1, Y1, X2, Y2 global V1X, V1Y,V2X, V2Y, mouseX, mouseY, LID, B1ID,B2ID Mu1=1-Myu; DX = X1 - X2; DY = Y1 - Y2 DL = math.sqrt(DX*DX + DY*DY); HX = DX/DL; HY = DY/DL FL = -Kfact*(DL - SLen); FX = HX*FL; FY = HY*FL A1X = FX*0.5/P1M; A1Y = FY*0.5/P1M A2X = -FX*0.5/P2M; A2Y = -FY*0.5/P2M V1X = (A1X + V1X)*Mu1; V1Y = (A1Y + V1Y + G)*Mu1 V2X = (A2X + V2X)*Mu1; V2Y = (A2Y + V2Y + G)*Mu1 X1 = X1 + V1X; Y1 = Y1 + V1Y X2 = X2 + V2X; Y2 = Y2 + V2Y if Y1>(250-6): if V1Y>1: winsound.Beep(523,100) Y1 = (250-6); V1Y = -V1Y if Y2>(250-3): if V2Y>1: winsound.Beep(1026,50) Y2 = (250-3); V2Y = -V2Y canvas.coords(LID,X1,Y1,X2,Y2) canvas.coords(B1ID,X1-6,Y1-6,X1+6,Y1+6) canvas.coords(B2ID,X2-3,Y2-3,X2+3,Y2+3) #■左ボタンダウン def leftMouseDown(event):#ダウン global X1, Y1; X1 = event.x; Y1 = event.y #■バインドの指定 canvas.bind("",leftMouseDown) #■メインループ while True: root.update();time.sleep(TimerInt); calc(canvas) 7.3 連続バネ/ゴム紐の運動 【リスト7-5】連続バネ from tkinter import * import time,math def array(N1, N2=0, N3=0): if N2 == 0 and N3==0: return [0 for i in range(N1)] elif N3 == 0: return [[0 for i in range(N2)] for j in range(N1)] else: return [[[0 for i in range(N3)] for j in range(N2)] for k in range(N1)] #パラメータ設定 NumNode=30 #要素数(節点数-1) Mass=5 #節点質量 LenNode=5 #節点間の長さ Myu=0.002 #粘性抵抗 Kfact=5 #節点間のバネ係数 Gravity=0.98 #重力 DT=0.001 #時間刻み TimerInt=0.001 #スリープ時間 MX=NumNode+1 #節点数(要素数+1: 配列サイズ) sCapture=False #画面キャプチャモード X=array(MX);Y=array(MX);XB=array(MX);YB=array(MX); VX=array(MX);VY=array(MX);LID=array(NumNode) def initP():#■座標値等の初期設定 global MX,X,Y,XB,YB, VX, VY for i in range(MX): X[i]=20+i*LenNode*1.5; Y[i]=40 XB[i]=X[i];YB[i]=Y[i]; VX[i]=-200; VY[i]=100 def force(X1, Y1, X2, Y2):#■節点間の力の計算 global LenNode, Kfact dx = X2-X1; dy = Y2-Y1; R=[0,0] DR = math.sqrt(dx * dx + dy * dy) if DR < LenNode : DR = LenNode LL = ((DR-LenNode)*Kfact) / DR R[0]= LL * dx; R[1]= LL * dy return R def calc(canvas):#■計算 global NumNode,Kfact, Rfact, X, XB, Y,YB, VX, VY, DT, Mass,Myu, Gravity for i in range(NumNode+1): # 壁面・床の処理 if Y[i] > 260: Y[i]= 260; VY[i]= -abs(VY[i]) if Y[i] < 10: Y[i]= 10; VY[i]= abs(VY[i]) if X[i] > 260: X[i]= 260; VX[i]= -abs(VX[i]) if X[i] < 10: X[i]= 10; VX[i]= abs(VX[i]) for i in range(NumNode+1): # 力の計算・加速度 X[i] = XB[i] + VX[i]*DT Y[i] = YB[i] + VY[i]*DT if i == 0: F =force(X[i],Y[i], X[i+1], Y[i+1]) elif i == NumNode: F =force(X[i],Y[i], X[i-1], Y[i-1]) else: F1 = force(X[i],Y[i], X[i+1], Y[i+1]) F2 = force(X[i],Y[i], X[i-1], Y[i-1]) F[0]=F1[0]+F2[0]; F[1]=F1[1]+F2[1] VX[i]= (VX[i] + F[0]/Mass)*(1 - Myu) VY[i]= (VY[i] + F[1]/Mass + Gravity)*(1 - Myu) for i in range(NumNode+1): XB[i]=X[i]; YB[i]=Y[i] for i in range(NumNode): canvas.coords(LID[i], X[i], Y[i],X[i+1],Y[i+1]) def leftMouseDown(event):#■左ボタンクリック global mouseX, mouseY, XB, YB, LenNode, Kfact, sCapture if sCapture:#画面キャプチャ用処理 for i in range(20): calc(canvas);root.update() return mouseX=event.x; mouseY= event.y for k in range(30):#引っ張りあげたときの形状確定のための繰返し XB[0]=mouseX; YB[0]=mouseY for i in range(NumNode): t = i + 1 dx = XB[t]-XB[i]; dy = YB[t] - YB[i] DR = math.sqrt(dx * dx + dy * dy) if DR < LenNode : DR = LenNode FF = ((DR-LenNode)+Kfact)*0.5 / DR XB[i] = XB[i] + dx * FF XB[t] = XB[t] - dx * FF YB[i] = YB[i] + dy * FF YB[t] = YB[t] - dy * FF calc(canvas) sCapture=True def rightMouseDown(event):#■右ボタンクリック global sCapture; if sCapture: sCapture=False else: initP() #■メイン処理 root=Tk();root.title("連続バネ/ゴム紐の運動") canvas=Canvas(root,width=270,height=270, bg="white");canvas.pack() canvas.focus_set(); initP(); mouseX=0; mouseY=0 canvas.create_rectangle(8,8,262,262, fill="", outline="blue",width=2) for i in range(NumNode):#初期形状表示 LID[i]=canvas.create_line(X[i], Y[i],X[i+1],Y[i+1],fill="red",width=2) canvas.bind("",leftMouseDown)#■バインドの指定 canvas.bind("",rightMouseDown) #■メインループ while True: time.sleep(0.01);root.update(); if not sCapture: calc(canvas) 7.4 ふたつの振り子 【リスト7-6】 2つの振り子のおもりの衝突 from tkinter import * import time,math,winsound def array(N1, N2=0, N3=0): if N2 == 0 and N3==0: return [0 for i in range(N1)] elif N3 == 0: return [[0 for i in range(N2)] for j in range(N1)] else: return [[[0 for i in range(N3)] for j in range(N2)] for k in range(N1)] PI=math.pi; PI3=PI/3 Myu = 0.0 # 粘性抵抗 e = 1 # 衝突係数 DT=0.01 # 時間刻み M1=10; M2=10 # 振り子のおもりの重さ R1=10; R2=10 # 振り子のおもり半径 L=100 # 振り子の棒の長さ X0=120; Y0=10 # 振り子を吊るす位置の中心位置 X1=X0-R1; X2=X0+R2 # 振り子を吊るす位置のX座標 RID=array(2,2);TH=array(2);V=array(2)# 配列宣言 TH[1]=PI3 # 振り子 2 のおもりの初期位置 #■メイン処理 root=Tk();root.title("ふたつの振り子") canvas=Canvas(root,width=240,height=150, bg="white");canvas.pack() canvas.focus_set() def initPend(canvas, X0, Y0, L, R, TH, C):#■振り子初期位置の描画 RT=[0,0]; X2=X0+L*math.sin(TH); Y2=Y0+L*math.cos(TH) RT[0]=canvas.create_line(X0,Y0, X2, Y2, fill="black",width=2) canvas.create_oval(X0-2,Y0-2,X0+2,Y0+2,fill="yellow") RT[1]=canvas.create_oval(X2-R,Y2-R,X2+R,Y2+R,fill=C) return RT RID[0]=initPend(canvas, X1,Y0, L,R1,TH[0],"red") RID[1]=initPend(canvas, X2,Y0, L,R2,TH[1],"blue") def movePend(canvas, RT, X0, Y0, L, R, TH):#■振り子位置移動 X2=X0+L*math.sin(TH); Y2=Y0+L*math.cos(TH) canvas.coords(RT[0], X0,Y0, X2, Y2) canvas.coords(RT[1],X2-R,Y2-R,X2+R,Y2+R) def checkCollision():#■おもり衝突のチェック global X1,X2, Y0, R1, R2; RR=R1+R2;RR=RR*RR XX1=X1+L*math.sin(TH[0]); YY1=Y0+L*math.cos(TH[0]) XX2=X2+L*math.sin(TH[1]); YY2=Y0+L*math.cos(TH[1]) DX=XX1-XX2; DY=YY2-YY1 if DX*DX+DY*DY<=RR: return True return False def Collision():#■おもり衝突時の速度変更 global e, M1, M2; MM=M1+M2; ee=1+e ; VV=V[0] # if abs(V[0]-V[1])>0.05: winsound.Beep(1026,50) V[0]+= M2*ee*(V[1]-VV)/MM V[1]-= M1*ee*(V[1]-VV)/MM def calc():#■衝突以外のおもりの移動の計算 global V,TH,DT,Myu for i in range(2): V[i]-=DT*(math.sin(TH[i])+V[i]*Myu) TH[i]+=DT*V[i] def dsp(canvas):#■振り子の描画 global RID,X1,X2, Y0,L,R1,R2,TH movePend(canvas,RID[0], X1,Y0, L,R1,TH[0]) movePend(canvas,RID[1], X2,Y0, L,R2,TH[1]) #■メインループ while True: calc(); dsp(canvas);root.update() if checkCollision():Collision() time.sleep(0.0001) 7.5 強制振動と振動の伝達 【リスト7-7】強制振動と振動の伝達 from tkinter import * import time,math,winsound def array(N1, N2=0, N3=0): if N2 == 0 and N3==0: return [0 for i in range(N1)] elif N3 == 0: return [[0 for i in range(N2)] for j in range(N1)] else: return [[[0 for i in range(N3)] for j in range(N2)] for k in range(N1)] PI=math.pi V1=0; X1=100; L1= 50; H1=50; M1=1.0; Myu1=0.02 # 物体 1 データ V2=0; X2=300; L2=100; H2=50; M2=1.5; Myu2=0.02 # 物体 2 データ DT=0.01; DTH=PI*DT; Kfact=1 # 実行用パラメータ Hist=array(400,2); IHist=0; iDsp=10 # 履歴表示用 def history(canvas):#■履歴表示 global Hist,IHist, X1, X2 Hist[IHist][0]=X1-100; Hist[IHist][1]=X2-300;IHist+=1 if IHist>=400: IHist=0 canvas.delete("Graph"); GH=200; GH1=GH-30; GH2=GH+30 ip=IHist;GX1=Hist[ip][0]+GH; GX2=Hist[ip][1]+GH canvas.create_line(50,GH, 450, GH, fill="green",tag="Graph") for i in range(399): ip+=1 if ip>=400: ip=0 GN1=Hist[ip][0]+GH; GN2=Hist[ip][1]+GH if ip % 100==0: canvas.create_line(i+50,GH1, i+50, GH2, fill="green",tag="Graph") canvas.create_line(i+50,GX1, i+51, GN1, fill="red",tag="Graph") canvas.create_line(i+50,GX2, i+51, GN2, fill="blue",tag="Graph") GX1=GN1; GX2=GN2 def calc(F):#■計算 global M1,M2, Myu1,Myu2,X1,V1,X2,V2,Kfact FS=(X2-X1-200)*Kfact Acc=(F+FS)/M1; V1=(V1+Acc*DT)*(1-Myu1); X1+=V1*DT Acc=-FS/M1; V2=(V2+Acc*DT)*(1-Myu2); X2+=V2*DT def initDraw(canvas,X,L,H,C):#■物体初期表示 LL=L/2; return canvas.create_rectangle(X-LL,100, X+LL, 100-H, fill=C,width=2) def initLine(canvas,X1, X2,H,C):#■バネを直線で描く HH=100-H/2; return canvas.create_line(X1,HH, X2, HH, fill=C,width=2) def Draw(canvas,ID, X,L,H):#■物体の表示(座標位置変更) LL=L/2; canvas.coords(ID, X-LL,100, X+LL, 100-H) def DrawLine(canvas,ID, X1, X2, H): #■バネの表示(座標位置変更) HH=100-H/2; canvas.coords(ID, X1,HH, X2, HH) root=Tk();root.title("強制振動") canvas=Canvas(root,width=480,height=400, bg="white");canvas.pack() LID= initLine(canvas,X1,X2,H1,"black") ID1 = initDraw(canvas, X1, L1, H1,"red") ID2 = initDraw(canvas, X2, L2, H2,"blue") TH=0;N=0 while True: root.update(); time.sleep(0.001) calc(200*math.sin(TH)); TH+=DTH DrawLine(canvas,LID, X1, X2, H1) Draw(canvas, ID1, X1, L1, H1) Draw(canvas, ID2, X2, L2, H2) N+=1 if N % iDsp==0: history(canvas)