Python : 用matplotlib做游標卡尺模擬
這是我碩士論文要用到的模擬
廢話不多說就直接來了吧
下面是我寫的程式碼
程式碼解釋我哪天有空再來補
存起來的mp4檔在這裡
我這邊是用主尺39 副尺20(即是一般游標卡尺的規格)來做測試影片
紅色的線是對齊的時候
對齊到主尺的副尺刻度線
好了,今天的筆記到此結束
希望有幫助未來遺忘這些的自己,以及需要的人
廢話不多說就直接來了吧
下面是我寫的程式碼
程式碼解釋我哪天有空再來補
'''
matplotlib_animation_sample.py
This is a 1-D vernier scale simmulation.
Before reading codes below, there're some concepts you need to know:
1.accuracy:
the accuracy of the vernier is calculate by the formula:
abs(round(main_steps/vernier_steps)-main_steps/vernier_steps)
means the difference between main_steps/vernier_steps and its closest integer
<ex>
if main_staps = 39, vernier_steps = 20
the accuracy of the vernier is 4-39/29 = 1/20 = 0.05 mm
2.steps and gap:
in vernier scale mechanism
"vernier_steps * vernier_gap = main_steps * main_gap"
vernier_steps: how many steps between all vernier scale lines
main_steps: how many steps between all main scale lines
vernier_gap: how many pixels between two vernier scale lines
main_gap: how many pixels between two main scale lines
in general cases
main_gap = 1mm = 1*mm_unit
main_steps = the length of the vernier scale (in mm)
'''
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import math
import time
#define key parameters
if len(sys.argv)==1: # default
main_steps = 39 # total 39 steps between all lines in main scale
vernier_steps = 20 # total 20 steps between all lines in vernier scale
elif len(sys.argv)==2:
main_steps = int(sys.argv[1])
vernier_steps = 20
else:
main_steps = int(sys.argv[1])
vernier_steps = int(sys.argv[2])
mm_unit = 1000 # 1 mm contains 1000 pixels
main_gap = mm_unit # main scale's accuracy is 1mm
#parameters follow by key parameters and other global variables
vernier_gap = int(main_steps*main_gap/vernier_steps) # design to be int
line_height = 5
frame_size = main_steps*main_gap #use vernier length as the frame_size
x0 = 0
vernier_pos = 0
vernier_accuracy = 0
vernier_accuracy_dig = 0
#prepare plotting objects
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)
def drawMainScale(plt_pos,frame_size):
global x0
ydata = [0,line_height]
line_list = []
x0 = int(math.floor(plt_pos))
for x in range(x0,x0+frame_size+1):
xdata = [x, x] #start and end pt have same x
ln, = ax1.plot(xdata,ydata)
ln.set_color('black')
line_list.append(ln)
return line_list
def drawVernierScale(frame_size):
ydata = [0,line_height]
line_list = []
for x in range(0,frame_size+1):
xdata = [x, x] #both start and end pt are in same x
ln, = ax2.plot(xdata,ydata)
ln.set_color('black')
line_list.append(ln)
return line_list
def drawMatch(pos):
vernier_match = pos
#print('vernier_match:', vernier_match)
dots = 100
xdata = [vernier_match]*dots
ydata = np.linspace(0,line_height,dots,endpoint=True)
ln, = ax2.plot(xdata,ydata,'rs')
return ln
def init():
global frame_size,vernier_steps,vernier_accuracy,vernier_accuracy_dig
if main_steps%vernier_steps==0:
print('your vernier scale is useless')
quit()
vernier_accuracy = abs(round(main_steps/vernier_steps)-main_steps/vernier_steps)
vernier_accuracy_copy = vernier_accuracy
while True:
vernier_accuracy_copy = vernier_accuracy_copy*10
vernier_accuracy_dig += 1
if vernier_accuracy_copy >=1:
break
print('vernier_accuracy_dig:',vernier_accuracy_dig)
ax1.set_xlim(0, main_steps)
ax1.set_ylim(0, 6)
ax2.set_xlim(0, vernier_steps)
ax2.set_ylim(0, 6)
line_list = drawVernierScale(vernier_steps)
return line_list
def update(i): #i is an int from 0 to frames-1, and keep looping
ax1.clear() #avoid memory-leak
ax2.clear()
ax1.set_xlim(0, main_steps)
ax1.set_ylim(0, 6)
ax2.set_xlim(0, vernier_steps)
ax2.set_ylim(0, 6)
global x0, vernier_pos
plt_pos = round(i/main_gap,2)
ax1.set_xlim(plt_pos, plt_pos + main_steps)
line_list = drawVernierScale(vernier_steps)
line_list.extend(drawMainScale(plt_pos, main_steps))
match = 0
for x in range(x0,x0+main_steps):
if (main_gap*x-i)%vernier_gap==0 :
n = (main_gap*x-i)/vernier_gap #frame is moving, so -i is needed
if n==0:
#x0 is the range at main scale that align by vernier zero
vernier_pos = x0
else:
#align pos - distance between align pos and vernier zero
vernier_pos = x -n*vernier_gap/mm_unit
line_list.append(drawMatch(n))
print('i:',i,'x0:',x0,'x:',x,'n:',n,'main_gap*x-i:',main_gap*x-i,'n*vernier_gap/mm_unit',n*vernier_gap/mm_unit,'vernier_pos',vernier_pos)
match += 1
analog_pos = i/mm_unit
vernier_pos_str = str(round(vernier_pos,vernier_accuracy_dig))
vernier_accuracy_str = str(round(vernier_accuracy,vernier_accuracy_dig))
if match>1:
ax1.set_title('analog pos: %.3f (mm)\nvernier pos: N/A (match=%d)'
%(analog_pos,match))
else:
ax1.set_title('analog pos: %.3f (mm)\nvernier pos:%s (mm)\naccuracy:%s (mm)'\
%(analog_pos,vernier_pos_str,vernier_accuracy_str))
return line_list
def main():
ani = FuncAnimation(fig, update, frames = 200000, interval = 20,
init_func=init, blit=False)
#ani.save('animation.mp4', writer='ffmpeg', fps=30)
plt.show()
if __name__ == '__main__':
main()
存起來的mp4檔在這裡
我這邊是用主尺39 副尺20(即是一般游標卡尺的規格)來做測試影片
紅色的線是對齊的時候
對齊到主尺的副尺刻度線
希望有幫助未來遺忘這些的自己,以及需要的人

留言
張貼留言