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

背景

我们在一些工业产品中使用树莓派替代了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)

posted @ 2021-12-02 07:51  郑立赛  阅读(1085)  评论(0编辑  收藏  举报