三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

python的工业过程控制场景模拟第一百二十一篇:机械臂任务优先级管理,紧急取样任务打断常规巡检动作优先执行。

python的工业过程控制场景模拟第一百二十一篇:机械臂任务优先级管理,紧急取样任务打断常规巡检动作优先执行。

机械臂任务优先级管理:紧急取样任务打断常规巡检的实现

“在化工产线中,一个紧急取样指令晚执行30秒,可能就意味着整批产品报废。但机械臂正在执行2小时的巡检任务,难道要等它跑完吗?”

—— 基于哈尔滨工程大学《工业过程控制》第十一章“分级控制与中断管理”思想

一、实际应用场景描述

在化工生产、制药车间、核设施巡检等场景中,机械臂通常承担两类任务:

┌──────────────────────────────────────────────┐

│ 机械臂任务调度系统架构 │

│ │

│ [上位机调度系统] │

│ • 接收MES/SCADA任务指令 │

│ • 维护任务队列 │

│ • 优先级仲裁 │

│ │ │

│ ▼ │

│ ┌────────────────────────────┐ │

│ │ 任务优先级管理器 (核心) │ ←── 本工具 │

│ │ • 优先级定义 │ │

│ │ • 中断检测 │ │

│ │ • 上下文保存/恢复 │ │

│ │ • 资源锁管理 │ │

│ └────────────┬───────────────┘ │

│ │ 调度决策 │

│ ▼ │

│ ┌────────────────────────────┐ │

│ │ 机械臂控制层 │ │

│ │ • 运动规划 (直线/圆弧) │ │

│ │ • 关节空间插值 │ │

│ │ • 伺服使能/报警处理 │ │

│ └────────────┬───────────────┘ │

│ │ 执行指令 │

│ ▼ │

│ ┌────────────────────────────┐ │

│ │ 现场设备层 │ │

│ │ • 末端执行器 (夹爪/取样阀)│ │

│ │ • 位置传感器 │ │

│ │ • 视觉系统 │ │

│ │ • 安全光栅 │ │

│ └────────────────────────────┘ │

│ │

│ 典型任务流: │

│ 常规巡检(低优先级) │

│ ↓ │

│ 【紧急取样请求(高优先级)】← 外部中断 │

│ ↓ │

│ 保存巡检上下文 → 执行取样 → 恢复巡检 │

└──────────────────────────────────────────────┘

典型应用场景

场景 常规任务 紧急任务 痛点

化工反应釜 每30分钟取样分析 反应异常需立即取样 等待巡检完成可能错过最佳取样时机

制药发酵罐 定时pH/溶氧检测 染菌预警需紧急取样 批次报废风险高

核设施巡检 辐射剂量日常监测 泄漏报警需立即取样 安全风险极高

食品灌装线 包装前金属检测 异物报警需紧急复检 食品安全合规要求

半导体晶圆 工艺腔室巡检 颗粒度超标需立即取样 良率损失严重

二、引入痛点

2.1 现场的真实困境

场景 现场反馈 根因

“等不起” “巡检任务还有20分钟结束,但反应釜已经超温了,取样指令还在排队” 无优先级抢占机制

“回不来” “紧急取样后,机械臂不知道回到哪里继续巡检” 上下文未保存

“撞上了” “紧急任务执行完,机械臂直接回到原位置,撞到了新放置的设备” 环境状态未更新

“锁死了” “紧急任务执行时,夹爪还握着巡检样品,导致取样失败” 资源未释放

“停不下来” “按下急停后,机械臂还在执行原有轨迹” 中断响应延迟

2.2 核心矛盾

机械臂任务调度的核心矛盾是“任务连续性”与“事件紧急性”之间的冲突。

传统顺序执行模式下,高优先级任务必须等待低优先级任务完成,这在安全关键场景下是不可接受的。

2.3 三种解决思路

思路 核心思想 优势 劣势

轮询检测 每执行一段轨迹检查一次中断 实现简单 响应延迟大(最坏情况=轨迹段时长)

硬件中断 外部信号触发控制器中断 响应最快(μs级) 需硬件支持,上下文保存复杂

优先级调度 软件层任务优先级管理 灵活,易实现 依赖调度算法设计

三、核心逻辑讲解

3.1 理论基础:分级控制与中断管理

本工具基于哈工程《工业过程控制》第十一章“分级控制与计算机控制系统”:

① 优先级分层模型

┌─────────────────────────────────────────────┐

│ 优先级分层 (Priority Levels) │

│ │

│ Level 0: 急停/安全中断 (最高优先级) │

│ • 硬件触发,立即响应 │

│ • 切断伺服使能,机械抱闸 │

│ │

│ Level 1: 紧急取样/安全联锁 │

│ • 软件中断,抢占式调度 │

│ • 保存上下文,执行紧急任务 │

│ │

│ Level 2: 工艺调整/人工干预 │

│ • 可抢占,但需确认资源 │

│ │

│ Level 3: 常规巡检/自动任务 (最低优先级) │

│ • 可被抢占,需支持断点续传 │

└─────────────────────────────────────────────┘

② 中断响应流程(哈工程教材P312)

中断响应时序:

T0: 外部紧急信号触发

↓ (硬件中断延迟 ~μs)

T1: CPU响应中断,保存当前PC和寄存器

T2: 查询中断向量表,跳转到ISR

T3: 保存当前任务上下文(位置、速度、目标点)

T4: 执行紧急任务预处理(回零/避障)

T5: 执行紧急取样动作

T6: 紧急任务完成,恢复现场

T7: 恢复被中断任务的上下文

T8: 返回原任务继续执行

③ 上下文保存内容

# 必须保存的状态(否则无法恢复)

context = {

'current_position': [x, y, z, rx, ry, rz], # 当前关节/笛卡尔位置

'target_position': [xt, yt, zt, rxt, ryt, rzt], # 原目标位置

'motion_params': {'speed': v, 'acc': a, 'blend': r}, # 运动参数

'gripper_state': 'open'/'closed', # 末端执行器状态

'io_state': {di1: True, do2: False}, # IO状态

'task_progress': 0.65, # 任务完成度(用于断点续传)

'timestamp': time.time() # 中断时刻

}

3.2 抢占式调度算法

抢占式调度核心逻辑:

while 系统运行:

# 1. 检查高优先级中断

if 紧急取样请求队列非空:

# 2. 保存当前任务上下文

context = 保存当前任务状态()

压入上下文栈(context)

# 3. 抢占当前任务

暂停当前任务()

释放非关键资源()

# 4. 执行紧急任务

while 紧急任务未完成:

执行紧急取样步骤()

# 5. 恢复现场

context = 弹出上下文栈()

恢复资源()

恢复任务(context)

# 6. 继续原任务

从断点继续执行()

# 7. 执行常规任务

执行常规巡检步骤()

3.3 资源锁管理

资源锁机制(防止死锁):

┌─────────────────────────────────────────────┐

│ 资源类型: │

│ • 机械臂本体 (独占资源) │

│ • 末端执行器 (夹爪/取样阀) │

│ • 视觉系统 (共享资源) │

│ • 外部轴 (传送带/旋转台) │

│ │

│ 锁申请规则: │

│ 1. 高优先级任务可抢占低优先级任务的资源 │

│ 2. 同级任务按FIFO顺序获取资源 │

│ 3. 死锁检测: 超时回退机制 │

│ │

│ 紧急任务资源策略: │

│ • 必须资源: 机械臂本体、取样阀 → 强制获取 │

│ • 可选资源: 视觉系统 → 降级执行 │

│ • 释放资源: 执行完毕后立即释放 │

└─────────────────────────────────────────────┘

四、代码讲解(面向对象设计)

4.1 类结构总览

类名 职责 设计模式

"TaskPriority" 任务优先级枚举 枚举类型

"TaskType" 任务类型枚举 枚举类型

"TaskContext" 任务上下文(dataclass) 值对象

"Task" 任务基类 模板方法

"RoutineInspectionTask" 常规巡检任务 具体实现

"EmergencySamplingTask" 紧急取样任务 具体实现

"RobotArmController" 机械臂控制器(模拟) 领域模型

"PriorityScheduler" 优先级调度器(核心) 策略模式

"ResourceManager" 资源锁管理器 单例模式

"InterruptHandler" 中断处理器 观察者模式

4.2 核心代码(精简版,CSDN友好)

完整源码约 480 行,单文件,包含 8 个核心类 + 完整调度逻辑。

以下为关键类摘录,完整代码见附件。

"""

机械臂任务优先级管理与紧急中断处理

参考哈尔滨工程大学《工业过程控制》第十一章"分级控制与计算机控制系统"

"""

from dataclasses import dataclass, field

from typing import List, Optional, Deque, Callable

from enum import Enum, auto

import time

import threading

from collections import deque

import logging

from abc import ABC, abstractmethod

# ============================================================

# 1. 基础数据结构(枚举与值对象)

# ============================================================

class TaskPriority(Enum):

"""任务优先级(数值越小优先级越高)"""

EMERGENCY = 0 # 紧急取样/安全中断

SAFETY = 1 # 安全联锁

MANUAL = 2 # 人工干预

PROCESS = 3 # 工艺调整

ROUTINE = 4 # 常规巡检(最低优先级)

def __lt__(self, other):

return self.value < other.value

class TaskType(Enum):

"""任务类型"""

ROUTINE_INSPECTION = "常规巡检"

EMERGENCY_SAMPLING = "紧急取样"

SAFETY_INTERRUPT = "安全中断"

MANUAL_OPERATION = "手动操作"

@dataclass

class TaskContext:

"""任务上下文(用于中断时保存状态)"""

task_id: str

task_type: TaskType

priority: TaskPriority

current_position: List[float] = field(default_factory=list)

target_position: List[float] = field(default_factory=list)

motion_params: dict = field(default_factory=dict)

gripper_state: str = "unknown"

io_state: dict = field(default_factory=dict)

progress: float = 0.0 # 任务进度 0-1

start_time: float = 0.0

interrupt_time: float = 0.0

resume_point: str = "start" # 恢复执行的位置标识

def to_dict(self) -> dict:

"""转换为字典(便于序列化)"""

return {

'task_id': self.task_id,

'task_type': self.task_type.value,

'priority': self.priority.name,

'current_position': self.current_position,

'target_position': self.target_position,

'motion_params': self.motion_params,

'gripper_state': self.gripper_state,

'progress': self.progress,

'resume_point': self.resume_point

}

# ============================================================

# 2. 任务基类(模板方法模式)

# ============================================================

class Task(ABC):

"""任务基类"""

def __init__(self, task_id: str, priority: TaskPriority,

task_type: TaskType):

self.task_id = task_id

self.priority = priority

self.task_type = task_type

self.context: Optional[TaskContext] = None

self.is_running = False

self.is_interrupted = False

self.is_completed = False

self.interrupt_lock = threading.Lock()

@abstractmethod

def execute_step(self) -> bool:

"""执行任务单步(子类实现)"""

pass

def run(self):

"""任务执行主循环(模板方法)"""

self.is_running = True

self.context.start_time = time.time()

logging.info(f"[Task] 启动任务: {self.task_id} ({self.task_type.value})")

try:

while not self.is_completed and self.is_running:

# 检查是否被中断

if self.is_interrupted:

logging.info(f"[Task] 任务 {self.task_id} 被中断")

break

# 执行单步

step_completed = self.execute_step()

if step_completed:

self.context.progress = min(1.0, self.context.progress + 0.1)

time.sleep(0.1) # 模拟执行时间

except Exception as e:

logging.error(f"[Task] 任务 {self.task_id} 执行异常: {e}")

finally:

self.is_running = False

if self.is_completed:

logging.info(f"[Task] 任务 {self.task_id} 完成")

def interrupt(self, interrupt_time: float) -> TaskContext:

"""任务被中断,保存上下文"""

with self.interrupt_lock:

self.is_interrupted = True

self.context.interrupt_time = interrupt_time

self.context.current_position = self._get_current_position()

self.context.gripper_state = self._get_gripper_state()

logging.info(f"[Task] 任务 {self.task_id} 在进度 {self.context.progress*100:.1f}% 处被中断")

return self.context

def resume(self, context: TaskContext):

"""从上下文恢复任务"""

with self.interrupt_lock:

self.context = context

self.is_interrupted = False

self.is_running = True

logging.info(f"[Task] 任务 {self.task_id} 从断点恢复 (进度: {context.progress*100:.1f}%)")

def _get_current_position(self) -> List[float]:

"""获取当前位置(子类实现)"""

return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

def _get_gripper_state(self) -> str:

"""获取夹爪状态(子类实现)"""

return "unknown"

# ============================================================

# 3. 具体任务实现

# ============================================================

class RoutineInspectionTask(Task):

"""常规巡检任务(低优先级)"""

def __init__(self, task_id: str, waypoints: List[List[float]]):

super().__init__(task_id, TaskPriority.ROUTINE, TaskType.ROUTINE_INSPECTION)

self.waypoints = waypoints

self.current_waypoint_index = 0

self.context = TaskContext(

task_id=task_id,

task_type=TaskType.ROUTINE_INSPECTION,

priority=TaskPriority.ROUTINE,

target_position=waypoints[0] if waypoints else []

)

def execute_step(self) -> bool:

"""执行巡检单步"""

if self.current_waypoint_index >= len(self.waypoints):

self.is_completed = True

return True

target = self.waypoints[self.current_waypoint_index]

# 模拟移动到目标点

logging.debug(f"[Routine] 移动到点位 {self.current_waypoint_index}: {target}")

# 模拟到达目标(实际应检查位置误差)

time.sleep(0.5) # 模拟运动时间

self.current_waypoint_index += 1

self.context.progress = self.current_waypoint_index / len(self.waypoints)

# 到达点位后执行检测动作

if self.current_waypoint_index < len(self.waypoints):

logging.info(f"[Routine] 到达巡检点 {self.current_waypoint_index}/{len(self.waypoints)}")

return True

def _get_current_position(self) -> List[float]:

"""获取当前位置"""

if self.current_waypoint_index > 0:

return self.waypoints[self.current_waypoint_index - 1]

return self.waypoints[0] if self.waypoints else [0,0,0,0,0,0]

class EmergencySamplingTask(Task):

"""紧急取样任务(高优先级)"""

def __init__(self, task_id: str, sampling_point: List[float],

sample_id: str):

super().__init__(task_id, TaskPriority.EMERGENCY, TaskType.EMERGENCY_SAMPLING)

self.sampling_point = sampling_point

self.sample_id = sample_id

self.steps = [

"move_to_safe_height",

"move_above_sample",

"descend_to_sample",

"activate_gripper",

"retreat_to_safe_height",

"move_to_analysis_station",

"release_sample"

]

self.current_step_index = 0

self.context = TaskContext(

task_id=task_id,

task_type=TaskType.EMERGENCY_SAMPLING,

priority=TaskPriority.EMERGENCY,

target_position=sampling_point

)

def execute_step(self) -> bool:

"""执行紧急取样单步"""

if self.current_step_index >= len(self.steps):

self.is_completed = True

return True

step = self.steps[self.current_step_index]

logging.info(f"[Emergency] 执行取样步骤: {step} (样品ID: {self.sample_id})")

# 模拟执行步骤

time.sleep(0.3)

self.current_step_index += 1

self.context.progress = self.current_step_index / len(self.steps)

self.context.resume_point = step # 记录恢复点

return True

def _get_current_position(self) -> List[float]:

"""获取当前位置(简化)"""

return self.sampling_point.copy()

def _get_gripper_state(self) -> str:

"""获取夹爪状态"""

if self.current_step_index > 3: # 激活夹爪后

return "closed"

return "open"

# ============================================================

# 4. 机械臂控制器(模拟)

# ============================================================

class RobotArmController:

"""机械臂控制器(模拟层)"""

def __init__(self):

self.current_position = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

self.gripper_state = "open"

self.is_servo_enabled = True

self.is_emergency_stop = False

self.lock = threading.Lock()

logging.info("[Robot] 机械臂控制器初始化完成")

def move_linear(self, target: List[float], speed: float = 0.5) -> bool:

"""直线运动(模拟)"""

with self.lock:

if not self.is_servo_enabled or self.is_emergency_stop:

return False

logging.debug(f"[Robot] 直线移动到: {target}, 速度: {speed}")

self.current_position = target.copy()

time.sleep(0.2) # 模拟运动时间

return True

def move_joint(self, target: List[float], speed: float = 0.5) -> bool:

"""关节运动(模拟)"""

with self.lock:

if not self.is_servo_enabled or self.is_emergency_stop:

return False

logging.debug(f"[Robot] 关节移动到: {target}, 速度: {speed}")

self.current_position = target.copy()

time.sleep(0.15)

return True

def set_gripper(self, state: str) -> bool:

"""设置夹爪状态"""

with self.lock:

if self.is_emergency_stop:

return False

self.gripper_state = state

logging.info(f"[Robot] 夹爪状态: {state}")

time.sleep(0.1)

return True

def get_position(self) -> List[float]:

"""获取当前位置"""

with self.lock:

return self.current_position.copy()

def emergency_stop(self):

"""紧急停止"""

with self.lock:

self.is_emergency_stop = True

self.is_servo_enabled = False

logging.critical("[Robot] 紧急停止触发!")

def reset_emergency(self):

"""复位紧急停止"""

with self.lock:

self.is_emergency_stop = False

self.is_servo_enabled = True

logging.info("[Robot] 紧急停止复位")

# ============================================================

# 5. 资源锁管理器(单例模式)

# ============================================================

class ResourceManager:

"""资源锁管理器(单例)"""

_instance = None

_lock = threading.Lock()

def __new__(cls):

with cls._lock:

if cls._instance is None:

cls._instance = super().__new__(cls)

cls._instance._init()

return cls._instance

def _init(self):

self.resource_locks: dict = {}

self.lock_holders: dict = {}

self.lock_timeout = 5.0 # 锁超时时间(秒)

# 初始化关键资源

self.register_resource("robot_arm", "机械臂本体")

self.register_resource("gripper", "夹爪")

self.register_resource("vision_system", "视觉系统")

self.register_resource("sampling_valve", "取样阀")

def register_resource(self, resource_id: str, description: str):

"""注册资源"""

self.resource_locks[resource_id] = threading.Lock()

self.lock_holders[resource_id] = None

logging.debug(f"[Resource] 注册资源: {resource_id} ({description})")

def acquire(self, resource_id: str, task_id: str,

priority: TaskPriority, timeout: float = None) -> bool:

"""获取资源锁(支持优先级抢占)"""

if resource_id not in self.resource_locks:

logging.error(f"[Resource] 资源不存在: {resource_id}")

return False

timeout = timeout or self.lock_timeout

start_time = time.time()

while time.time() - start_time < timeout:

with self.resource_locks[resource_id]:

current_holder = self.lock_holders[resource_id]

# 资源未被占用

if current_holder is None:

self.lock_holders[resource_id] = (task_id, priority, time.time())

logging.info(f"[Resource] {task_id} 获取资源: {resource_id}")

return True

# 资源被占用,检查优先级

holder_task_id, holder_priority, acquire_time = current_holder

# 高优先级任务可抢占低优先级任务

if priority < holder_priority: # 数值小=优先级高

logging.warning(f"[Resource] {task_id}(P{priority.value}) 抢占 "

f"{holder_task_id}(P{holder_priority.value}) 的资源: {resource_id}")

self.lock_holders[resource_id] = (task_id, priority, time.time())

return True

# 同级任务等待(FIFO)

if priority == holder_priority:

pass # 继续等待

time.sleep(0.05) # 短暂休眠,避免忙等

logging.error(f"[Resource] {task_id} 获取资源 {resource_id} 超时")

return False

def release(self, resource_id: str, task_id: str) -> bool:

"""释放资源锁"""

if resource_id not in self.resource_locks:

return False

with self.resource_locks[resource_id]:

current_holder = self.lock_holders[resource_id]

if current_holder is None:

return True

holder_task_id, _, _ = current_holder

if holder_task_id == task_id:

self.lock_holders[resource_id] = None

logging.info(f"[Resource] {task_id} 释放资源: {resource_id}")

return True

else:

logging.warning(f"[Resource] {task_id} 尝试释放非自己持有的资源: {resource_id}")

return False

def get_holder(self, resource_id: str) -> Optional[tuple]:

"""获取资源当前持有者"""

return self.lock_holders.get(resource_id)

# ============================================================

# 6. 优先级调度器(核心)

# ============================================================

class PriorityScheduler:

"""

优先级调度器 —— 核心控制逻辑

基于哈工程《工业过程控制》第十一章"分级控制"思想

实现抢占式任务调度与中断管理

"""

def __in

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

← 返回列表