锐客网

【树莓派】Python开发工控机急停设计

背景 我们在一些工业产品中使用树莓派替代了PLC和上位机,并借助树莓派的算力将AI和机器视觉引入工业领域。
以前的产品都不存在动作机构,仅仅将结果输出到指示灯、蜂鸣器或者显示器上,没有安全隐患,
现在引入了动作机构,需要根据结果驱动设备执行一定的动作,动作机构的引入,增加了产品的安全隐患,比如可能会夹手,撞机等。为此我们需要设计额外的保护程序,其中最重要的是急停功能的实现。
要求

  • 急停信号优先级最高,任何情况下按下急停都应该马上停止
问题分析
  • 动作机构由24V供电,急停开关串联在电源上,可以做到开关按下后,动作机构断电。(急停开关都带有锁定机构,按下后不会弹起,会保持按下状态)
  • 树莓派独立于动作机构供电,急停开关按下后,树莓派收到信号,开始终止程序,之后一直监听急停按钮信号。
  • Python一般情况下是单线程运行,为了及时响应急停,需要将急停功能做成主进程,业务动作逻辑作为子进程,当监听到急停信号后,马上终止子进程
设计思路
  • 择子进程而不是子线程的原因为:Python中子线程无法发送kill信号,没有很好的办法干预子线程的行为(除非每一步都判断一下,会造成代码复杂度升高),而子进程可以直接发送terminate信号杀死。
  • 急停使用低电平触发原因为:我们认为低电平是一个稳定的状态,高电平不是一个稳定的状态,比如由于某种原因导致断电,那么也应该触发急停,发生任何非正常的情况,停下来总是没错的。
接线示意图 【【树莓派】Python开发工控机急停设计】【树莓派】Python开发工控机急停设计
文章图片

Python程序流程图 【树莓派】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 Truedef step_2(): time.sleep(3) return Truedef 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)