背景
我們在一些工業產品中使用樹莓派替代了PLC和上位機,並藉助樹莓派的算力將AI和機器視覺引入工業領域。
以前的產品都不存在動作機構,僅僅將結果輸出到指示燈、蜂鳴器或者顯示器上,沒有安全隱患,
現在引入了動作機構,需要根據結果驅動裝置執行一定的動作,動作機構的引入,增加了產品的安全隱患,比如可能會夾手,撞機等。為此我們需要設計額外的保護程式,其中最重要的是急停功能的實現。
要求
- 急停訊號優先順序最高,任何情況下按下急停都應該馬上停止
問題分析
- 動作機構由24V供電,急停開關串聯在電源上,可以做到開關按下後,動作機構斷電。(急停開關都帶有鎖定機構,按下後不會彈起,會保持按下狀態)
- 樹莓派獨立於動作機構供電,急停開關按下後,樹莓派收到訊號,開始終止程式,之後一直監聽急停按鈕訊號。
- Python一般情況下是單執行緒執行,為了及時響應急停,需要將急停功能做成主程式,業務動作邏輯作為子程式,當監聽到急停訊號後,馬上終止子程式
設計思路
- 擇子程式而不是子執行緒的原因為:Python中子執行緒無法傳送kill訊號,沒有很好的辦法干預子執行緒的行為(除非每一步都判斷一下,會造成程式碼複雜度升高),而子程式可以直接傳送terminate訊號殺死。
- 急停使用低電平觸發原因為:我們認為低電平是一個穩定的狀態,高電平不是一個穩定的狀態,比如由於某種原因導致斷電,那麼也應該觸發急停,發生任何非正常的情況,停下來總是沒錯的。
接線示意圖
Python程式流程圖
程式碼實現
import RPi.GPIO as GPIO
import time
from multiprocessing import Process
# 定義訊號引腳
button_stop = 20
button_reset = 21
button_start = 22
# 初始化GPIO
def init_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# 初始化按鈕,按鈕均為低電平觸發
GPIO.setup(button_reset, GPIO.IN)
GPIO.setup(button_start, GPIO.IN)
GPIO.setup(button_stop, GPIO.IN)
# 業務動作
def step_1():
time.sleep(3)
return True
def step_2():
time.sleep(3)
return True
def step_3():
time.sleep(3)
return True
# 復位動作組合
def run_reset():
move_reset_list = [
step_3,
step_2,
step_1
]
result = True
try:
for func in move_reset_list:
func_name = func.__name__
print("正在執行: %s" % func_name)
func_result = func()
if not func_result:
result = False
break
except:
result = False
finally:
if not result:
exit(1)
else:
exit(0)
# 業務動作組合
def run_step():
result = True
try:
auto_cover_list = [
step_1,
step_2,
step_3
]
for func in auto_cover_list:
func_name = func.__name__
print("正在執行: %s" % func_name)
func_result = func()
if not func_result:
result = False
break
except:
result = False
finally:
if not result:
exit(1)
else:
exit(0)
if __name__ == '__main__':
# 開始工作
init_gpio()
while True:
if GPIO.input(button_start) == 0:
try:
p_run = Process(target=run_step, daemon=True)
p_run.start()
# 監聽急停訊號
while p_run.is_alive():
if GPIO.input(button_stop) == 0:
p_run.terminate()
break
else:
time.sleep(0.1)
if p_run.exitcode == 0 or p_run.exitcode is None:
print("執行成功")
else:
print("執行失敗")
except:
print("執行失敗")
elif GPIO.input(button_reset) == 0:
p_reset = Process(target=run_reset, daemon=True)
p_reset.start()
# 監聽急停訊號
while p_reset.is_alive():
if GPIO.input(button_stop) == 0:
p_reset.terminate()
break
else:
time.sleep(0.1)
elif GPIO.input(button_stop) == 0:
# 急停按鈕釋放後,再釋放程式
while True:
if GPIO.input(button_stop) == 0:
time.sleep(0.1)
else:
break
else:
time.sleep(0.1)