背景
我們在一些工業產品中使用樹莓派替代了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)