工业工程

工业工程

首页
专业认知
课程体系
方法与工具
软件技能
就业发展
考研深造
院校学科
行业洞察
关于
考研倒计时
注册
工业工程

工业工程

首页 专业认知 课程体系 方法与工具 软件技能 就业发展 考研深造 院校学科 行业洞察 关于 考研倒计时 注册
  1. 首页
  2. 软件技能
  3. 用 Python 做 IE 数据分析:从工时数据到线平衡计算

用 Python 做 IE 数据分析:从工时数据到线平衡计算

0
  • 软件技能
  • 发布于 2025-07-22
  • 37 次阅读
伴读书童
伴读书童
目录
当前文章没有目录

Python 对工业工程的价值,不在于"会编程"这个标签,而在于它能把 IE 每天重复做的分析工作变成可复用、可批量、可追溯的模型。

这篇文章不讲语法基础,直接给三个 IE 高频场景的完整代码:工时数据处理、线平衡计算、仿真建模。

一、环境准备

# 核心库
import pandas as pd        # 数据处理
import numpy as np         # 数值计算
from scipy import stats    # 统计检验
import matplotlib.pyplot as plt   # 可视化

# 优化与仿真(按需安装)
# pip install pulp         # 线性规划
# pip install simpy        # 离散事件仿真
# pip install ortools      # 运筹优化

安装方式建议用 Anaconda 或 pip install pandas numpy scipy matplotlib。

二、场景一:工时数据处理与标准工时计算

2.1 问题

某工位秒表测时,观测了 15 个周期,需要:剔除异常值 → 计算正常时间 → 加宽放 → 得到标准工时。

2.2 代码

import numpy as np
import pandas as pd

def calc_standard_time(observations, rating=1.0, allowance=0.15, verbose=True):
    """标准工时计算:剔除异常值 + 评比 + 宽放"""
    obs = np.array(observations, dtype=float)

    # 1. 三倍标准差法剔除异常值
    while True:
        mean = obs.mean()
        std = obs.std(ddof=1)
        lower, upper = mean - 3 * std, mean + 3 * std
        mask = (obs >= lower) & (obs <= upper)
        if mask.all():
            break
        removed = obs[~mask]
        if verbose:
            print(f"剔除异常值: {removed.round(2).tolist()}")
        obs = obs[mask]

    observed_time = obs.mean()
    normal_time = observed_time * rating
    standard_time = normal_time * (1 + allowance)

    if verbose:
        print(f"有效观测次数: {len(obs)}")
        print(f"观测平均时间: {observed_time:.2f} 秒")
        print(f"评比系数:     {rating}")
        print(f"正常时间:     {normal_time:.2f} 秒")
        print(f"宽放率:       {allowance:.1%}")
        print(f"标准时间:     {standard_time:.2f} 秒")
        print(f"标准差:       {obs.std(ddof=1):.3f}  变异系数: {obs.std(ddof=1)/observed_time:.2%}")

    return standard_time

# 示例:15 次观测,含一个异常值
data = [42.1, 43.5, 41.8, 42.6, 42.9, 43.1, 41.9, 42.4,
        42.7, 68.2, 43.0, 42.3, 41.7, 42.8, 42.5]
st = calc_standard_time(data, rating=1.10, allowance=0.18)

输出解读:

  • 68.2 秒会被自动识别并剔除
  • 变异系数(CV)反映作业稳定性,一般手工装配作业 CV < 10% 说明较稳定

2.3 批量处理多个工位

# 从 Excel 读取多工位测时数据
df = pd.read_excel("工时观测.xlsx")
# 假设格式:工位 | 观测序列(多列)

results = []
for _, row in df.iterrows():
    obs = row.drop("工位").dropna().astype(float).tolist()
    st = calc_standard_time(obs, rating=1.05, allowance=0.15, verbose=False)
    results.append({"工位": row["工位"], "标准工时": round(st, 2)})

result_df = pd.DataFrame(results)
print(result_df)

三、场景二:生产线平衡计算

3.1 问题

给定各工位的标准工时与客户需求节拍,计算:

  • 平衡率与失衡率
  • 理论最少工位数
  • 每日产能
  • 瓶颈工位

3.2 代码

import pandas as pd
import numpy as np

def line_balance(task_times, takt_time=None, available_time=27600, demand=None):
    """产线平衡分析
    task_times: 各工位作业时间列表(秒)
    takt_time: 节拍(秒),与 demand 二选一
    available_time: 每日有效工作时间(秒),默认 460 分钟
    demand: 日需求数量
    """
    tt = np.array(task_times, dtype=float)

    if takt_time is None:
        if demand is None:
            raise ValueError("需提供 takt_time 或 demand")
        takt_time = available_time / demand

    total_time = tt.sum()
    bottleneck = tt.max()
    bottleneck_idx = int(tt.argmax()) + 1
    n_stations = len(tt)

    balance_rate = total_time / (bottleneck * n_stations)
    idle_rate = 1 - balance_rate

    # 理论最少工位数(向上取整)
    min_stations = int(np.ceil(total_time / takt_time))

    # 日产能(受瓶颈限制)
    capacity = int(available_time / bottleneck)

    # 各工位与节拍的差
    diff = tt - takt_time

    return {
        "节拍(秒)": round(takt_time, 2),
        "工位数": n_stations,
        "总作业时间(秒)": round(total_time, 2),
        "瓶颈工位": bottleneck_idx,
        "瓶颈时间(秒)": round(bottleneck, 2),
        "平衡率": f"{balance_rate:.1%}",
        "失衡率": f"{idle_rate:.1%}",
        "理论最少工位数": min_stations,
        "日产能(件)": capacity,
        "超节拍工位": [i + 1 for i, d in enumerate(diff) if d > 0],
        "各工位与节拍差": [round(d, 2) for d in diff],
    }

# 示例
task_times = [32, 41, 28, 25, 39]
res = line_balance(task_times, demand=800, available_time=27600)
for k, v in res.items():
    print(f"{k}: {v}")

3.3 改善方案模拟

def simulate_improvement(task_times, improvements, demand=800, available_time=27600):
    """模拟改善后的平衡效果
    improvements: {工位索引(0起): 改善后的时间}
    """
    tt = np.array(task_times, dtype=float).copy()
    for idx, new_time in improvements.items():
        tt[idx] = new_time
    return line_balance(tt.tolist(), demand=demand, available_time=available_time)

# 假设:W2 通过布局改善降至 30.7,W5 通过电动工具降至 35,
# 并把 1.5 秒作业从 W5 转到 W4
before = line_balance([32, 41, 28, 25, 39], demand=800)
after = line_balance([32, 30.7, 34, 26.5, 33.5], demand=800)

print("\n改善前:", before["平衡率"], "日产能", before["日产能(件)"])
print("改善后:", after["平衡率"], "日产能", after["日产能(件)"])

3.4 用启发式算法做要素分配

当作业要素很多时,人工分配很费劲,可用**最长作业时间优先(LPT)**启发式:

def assign_stations(elements, takt_time):
    """LPT 启发式工位分配
    elements: [(要素名, 时间, 前置要素列表)]
    返回:各工位包含的要素
    """
    # 按时间降序排列
    sorted_elem = sorted(elements, key=lambda x: -x[1])
    stations = [[]]

    for name, time_val, preds in sorted_elem:
        placed = False
        for st in stations:
            station_time = sum(e[1] for e in st)
            assigned_names = [e[0] for e in st]
            # 检查前置约束 & 容量约束
            if all(p in assigned_names for p in preds) and station_time + time_val <= takt_time:
                st.append((name, time_val, preds))
                placed = True
                break
        if not placed:
            stations.append([(name, time_val, preds)])

    return stations

# 示例
elements = [
    ("A1 取外壳", 4.2, []),
    ("A2 贴标签", 5.8, ["A1 取外壳"]),
    ("A3 装主板", 8.5, ["A1 取外壳"]),
    ("A4 锁螺丝", 9.3, ["A3 装主板"]),
    ("A5 通电测试", 6.1, ["A4 锁螺丝"]),
    ("A6 外观检查", 1.5, ["A5 通电测试"]),
]
stations = assign_stations(elements, takt_time=20)
for i, st in enumerate(stations, 1):
    t = sum(e[1] for e in st)
    print(f"工位{i} (负荷 {t:.1f}s): {[e[0] for e in st]}")

四、场景三:离散事件仿真

用 SimPy 建立一个简单的单工序系统,模拟排队与利用率。

import simpy
import random
import statistics

class ProductionLine:
    def __init__(self, env, num_machines, process_time_mean, process_time_std):
        self.env = env
        self.machines = simpy.Resource(env, num_machines)
        self.pt_mean = process_time_mean
        self.pt_std = process_time_std
        self.wait_times = []
        self.throughput = 0

    def process(self, item):
        """单个工件的加工流程"""
        arrive = self.env.now
        with self.machines.request() as req:
            yield req
            self.wait_times.append(self.env.now - arrive)
            pt = max(0.1, random.gauss(self.pt_mean, self.pt_std))
            yield self.env.timeout(pt)
            self.throughput += 1

def source(env, line, arrival_interval, num_items):
    """工件发生器"""
    for i in range(num_items):
        env.process(line.process(f"Item-{i}"))
        yield env.timeout(random.expovariate(1.0 / arrival_interval))

def run_simulation(num_machines=3, pt_mean=10, pt_std=2,
                   arrival_interval=4, num_items=500, sim_time=2000):
    random.seed(42)
    env = simpy.Environment()
    line = ProductionLine(env, num_machines, pt_mean, pt_std)
    env.process(source(env, line, arrival_interval, num_items))
    env.run(until=sim_time)

    utilization = (pt_mean * line.throughput) / (num_machines * sim_time)
    return {
        "吞吐量": line.throughput,
        "平均等待时间": round(statistics.mean(line.wait_times), 2),
        "最大等待时间": round(max(line.wait_times), 2),
        "设备利用率": f"{utilization:.1%}",
    }

# 对比不同设备数量的效果
for n in [2, 3, 4, 5]:
    r = run_simulation(num_machines=n)
    print(f"设备数={n}: 吞吐={r['吞吐量']}, 平均等待={r['平均等待时间']}s, "
          f"利用率={r['设备利用率']}")

这段代码的 IE 价值:在设备投资决策前,用仿真回答"再加一台设备能提升多少产出、等待时间能降多少",比拍脑袋决策可靠得多。

五、场景四:运筹优化(排产)

用 PuLP 求解一个简单的生产计划问题。

import pulp

def production_planning(products, resources, profit, usage, capacity):
    """产品组合优化
    products: 产品列表
    resources: 资源(工序)列表
    profit: {产品: 单位利润}
    usage: {产品: {资源: 单位消耗}}
    capacity: {资源: 可用能力}
    """
    prob = pulp.LpProblem("ProductMix", pulp.LpMaximize)
    x = {p: pulp.LpVariable(f"x_{p}", lowBound=0, cat="Integer") for p in products}

    # 目标函数:最大化利润
    prob += pulp.lpSum(profit[p] * x[p] for p in products)

    # 约束:资源能力限制
    for r in resources:
        prob += pulp.lpSum(usage[p][r] * x[p] for p in products) <= capacity[r]

    prob.solve(pulp.PULP_CBC_CMD(msg=False))

    return {p: int(x[p].value()) for p in products}, pulp.value(prob.objective)

# 示例
products = ["A", "B", "C"]
resources = ["机加", "装配", "检验"]
profit = {"A": 120, "B": 180, "C": 150}
usage = {
    "A": {"机加": 2, "装配": 3, "检验": 1},
    "B": {"机加": 4, "装配": 3, "检验": 2},
    "C": {"机加": 3, "装配": 4, "检验": 1},
}
capacity = {"机加": 800, "装配": 900, "检验": 400}

plan, total = production_planning(products, resources, profit, usage, capacity)
print("最优生产计划:", plan)
print("最大利润:", round(total, 2))

六、可视化:让数据说话

import matplotlib.pyplot as plt
import numpy as np

def plot_yamazumi(task_times, takt_time, labels=None):
    """山积图:工位负荷可视化"""
    n = len(task_times)
    if labels is None:
        labels = [f"W{i+1}" for i in range(n)]

    fig, ax = plt.subplots(figsize=(10, 5))
    colors = ["#d62728" if t > takt_time else "#2ca02c" for t in task_times]
    bars = ax.bar(labels, task_times, color=colors, alpha=0.85)

    ax.axhline(takt_time, color="black", linestyle="--", linewidth=1.5,
               label=f"节拍 {takt_time}s")

    balance = sum(task_times) / (max(task_times) * n)
    ax.set_title(f"产线平衡山积图(平衡率 {balance:.1%})", fontsize=13)
    ax.set_ylabel("作业时间 (秒)")
    ax.legend()

    for bar, t in zip(bars, task_times):
        ax.text(bar.get_x() + bar.get_width()/2, t + 0.5,
                f"{t}", ha="center", fontsize=10)

    plt.tight_layout()
    plt.show()

plot_yamazumi([32, 41, 28, 25, 39], takt_time=34.5)

中文显示问题:Matplotlib 默认不支持中文,需设置字体:

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False

七、学习路径建议

阶段 内容 产出
第 1 个月 Python 基础 + pandas 能用 pandas 替代 Excel 做数据处理
第 2 个月 numpy + scipy.stats 能独立做假设检验与分布拟合
第 3 个月 matplotlib 可视化 能做出专业级图表
第 4–5 个月 仿真(simpy)与优化(PuLP) 能建模求解实际问题
第 6 个月 综合项目 一个完整的 IE 数据分析项目

八、务实提醒

  1. 不要为了学 Python 而学:先用它解决一个真实问题(课程作业、竞赛、实习项目)
  2. 能跑通比写得优雅重要:IE 场景下,脚本是工具不是产品
  3. 结果要能解释:给管理层汇报时,代码的复杂度不重要,结论的可解释性才重要
  4. Excel 仍是主战场:Python 处理完的数据,很多时候还是要输出到 Excel 给同事看

延伸阅读

  • 《工业工程必备软件地图:从 Excel 到 FlexSim,每个阶段该学什么》
  • 《生产线平衡:从节拍、瓶颈到平衡率改善的完整方法》

相关阅读

  • 工业工程必备软件地图:从 Excel 到 FlexSim,每个阶段该学什么:按学习阶段给出 IE 的软件全景图:Excel、统计分析、仿真建模、CAD、企业系统,并给出…
  • Python 与 VBA:工业工程自动化的两条路径怎么选:每周花 4 小时做同一份报表,一年就是 200 小时。本文系统对比两条自动化路径:VBA(E…
  • SQL 工业工程数据分析实战:从 MES 取数到指标看板:IE 日常有 60% 的时间花在等数据上。本文从真实取数场景出发,讲透 SQL 核心语法(S…
  • 工业工程师的 Excel 实战手册:从工时分析、过程能力到线平衡的 30 个技法:面向 IE 场景的 Excel 实操手册:连续测时数据差分还原、IQR 与 3σ 异常值剔除…
  • 工业工程数据与指标看板:从指标定义、采集口径到可视化落地的完整手册:从指标定义卡 12 要素讲到看板落地:OEE 三种分母口径对照(负荷 75.45% / 计划…
相关文章

APS排到分钟级:有限产能可以按工作中心切

Acumatica 2026 R2 已正式发布,制造业侧三项增强:APS 支持按工作中心分别设置有限或无限产能、排程精度细化到分钟、重叠班次合并为单一日历;

TrakSYS 14.1:用对话配置MES

Parsec Automation 发布 TrakSYS 14.1,核心是 TrakSYS IQ Build:一个基于模型上下文协议(MCP)的连接器。

MES 12.0:AI智能体直接改配置,还要人点头

10 月 20 日,Critical Manufacturing 发布 MES 驱动的工业运营平台 12.0 版。

浪潮海岳MOM9.0:九大模块全面AI化

10 月 16 日,浪潮数字企业在山东省具身智能产业发展大会上发布浪潮海岳 AI 原生工业软件——新一代制造运营 MOM9.0。

Poka发布无代码AI Studio

IFS 旗下工业 AI 平台 Poka 在 IFS Unleashed 大会上发布 Poka AI Studio——一个面向车间“人的流程”管理的无代码智能体构。

工业智能中枢 + 数字孪生验证层

10 月 12 日第二十六届中国国际工业博览会期间,库卡在上海发布“开天 KAI”智能平台并同步推出 Visual Components CN。

目录
当前文章没有目录
Copyright © 2002 CUMT All Rights Reserved. Powered by 中国矿业大学工业工程系.
粤ICP备2024349181号-3