ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

python的先进制造技术工业场景模拟第十篇:读取机器人工作站生产记录,统计每日工件抓取成功数与抓取失败数量。

python的先进制造技术工业场景模拟第十篇:读取机器人工作站生产记录,统计每日工件抓取成功数与抓取失败数量。 周三夜班机器人焊接工作站。今晚又废了三个件夜班班长老赵指着废料筐机器人抓件的时候吸盘没吸住——工件在夹具上歪了焊枪一过去直接焊偏。三个壳体每个两百多六百多块钱没了。我调出机器人工作站的 HMI 日志。屏幕上滚动着一行行记录时间戳、机器人编号、工件编号、抓取结果SUCCESS / FAIL、失败原因代码01吸盘真空不足、02工件位置偏移、03夹具干涉、04视觉定位超时、循环时间秒。系统里不是有抓取记录吗我问。有老赵点开一页但它是按次记录的——每次抓取一行一晚上几千行。我想知道的是今天总共抓了多少次、成功了几次、失败了几次、失败率多少、主要是哪种原因导致的。系统没有每日抓取统计这个功能——它只会实时显示当前这一次抓没抓上。所以你现在每次开班都不知道昨晚的抓取情况对老赵苦笑第二天早上我来接班看到废料筐里多了几个废件才知道昨晚抓取有问题。但那时候夜班已经结束了没法实时干预。厂长说必须搞一个每日抓取统计报表每天早上贴在看板上——成功多少、失败多少、失败原因排名一目了然。明白了我打开 VS Code你需要一个程序读取机器人工作站的生产记录 CSV按日期分组统计每天的抓取成功数和失败数计算失败率再按失败原因分类汇总。输出一张每日统计表和一张趋势图。这样每天早上你直接把报表打印出来贴在看板上昨天抓了 480 次成功 465 次失败 15 次失败率 3.1%主要原因是吸盘真空不足9 次。我敲了几行代码import pandas as pdimport matplotlib.pyplot as plt# 读取抓取记录df pd.read_csv(robot_grasp_log.csv, parse_dates[timestamp])# 提取日期df[date] df[timestamp].dt.date# 按日期分组统计daily df.groupby(date).agg(total(grasp_result, count),success(grasp_result, lambda x: (x SUCCESS).sum()),fail(grasp_result, lambda x: (x FAIL).sum()),).reset_index()daily[fail_rate] daily[fail] / daily[total] * 100# 按失败原因统计fail_reasons df[df[grasp_result] FAIL][fail_reason].value_counts()print(daily)print(fail_reasons)这只是核心逻辑我说完整版会用 OOP 封装一个类管数据加载解析时间戳、统一结果编码一个类管数据清洗处理缺失值、异常时间戳一个类管每日统计成功/失败计数、失败率计算一个类管失败原因分类汇总一个类管可视化每日成功/失败堆叠柱状图、失败率趋势图、失败原因饼图。数据自包含——用 numpy 合成一份机器人抓取日志 CSV读者下载就能跑。老赵凑近屏幕所以你这东西就是把几千行每次抓了没抓上的记录按天汇总——成功多少、失败多少、失败原因是什么。一张表、一张图早上看一眼就知道昨晚的抓取情况。对。而且还能做趋势分析我补充比如连续三天失败率从 1% 升到 3% 又升到 5%——这说明吸盘系统在退化不是偶发问题。可以提前安排维护不等废件出来再处理。机器人工作站的管理不是抓上了就行是用数据看趋势在废件出现之前就发现问题。一、实际应用场景真实痛点场景设定机器人工作站焊接/搬运/装配每次执行工件抓取动作后在日志中记录时间戳、机器人编号、工件编号、抓取结果成功/失败和失败原因代码。一个班次产生数千条抓取记录。现场管理者需要每日统计抓取成功数、失败数、失败率及失败原因分布用于评估工作站稳定性和安排设备维护。常见痛点日志按次逐条记录行数庞大系统不提供按日聚合的每日抓取统计报表依赖次日人工查看废料筐来反推抓取质量。现场原话叙事化我不是不想管抓取质量老赵说每次抓没抓上系统里都有记录。但它是流水账——一行一行滚过去谁看得过来我需要知道的是今天总共抓了多少次、成功了几次、失败了几次、失败率多少、主要是哪种原因。这些系统给不了我。第二天早上我看到废料筐才知道昨晚有问题——但那时候已经废了三个件了。核心矛盾逐次记录的抓取流水账与管理者需要的每日聚合统计之间的汇总断层。需要一个机器人工作站抓取记录统计程序用pandas 按日期分组聚合用numpy 计算失败率和统计指标用matplotlib 绘制每日趋势图和失败原因分布图输出结构化的每日抓取统计报表。二、痛点分析映射到滨州职业学院《先进制造技术》课程模型《先进制造技术》模块 本篇痛点对应工业机器人技术基础机器人末端执行器与抓取策略 抓取可靠性评估基于日志数据的抓取成功率统计与失败模式分析。FMS与先进生产管理FMS 的监控与数据采集MDC 生产数据采集从机器人日志中提取结构化指标支撑生产管理决策。智能制造与数字孪生生产过程的实时监控 质量趋势可视化用趋势图反映抓取质量的时序变化实现早期预警。先进制造新模式数据驱动的持续改进 根因分析按失败原因分类汇总定位系统性问题而非偶发故障。一句话总结我们需要构建一个机器人工作站抓取记录统计程序用pandas 按日期分组聚合用numpy 计算失败率用matplotlib 绘制趋势图和分布图实现从逐次流水账到每日质量报表的转化。三、核心逻辑讲解大白话3.1 问题本质把抓取记录想象成快递配送记录把机器人抓取日志想象成快递配送的签收记录* 每次抓取 一次配送快递员每次送一个包裹记录送到了成功或没送到失败。* 失败原因 拒收原因收件人不在吸盘真空不足、地址不对工件位置偏移、电话不通视觉定位超时。* 每日统计 快递站长的日报表今天总共派了 480 单成功 465 单失败 15 单失败率 3.1%——其中 9 单是收件人不在吸盘问题4 单是地址不对定位偏移2 单是电话不通视觉超时。* 趋势图 配送质量走势连续三天失败率从 1% → 3% → 5%说明快递员可能车子出问题了——对应车间里吸盘系统退化需要维护。工业应用* pandas 日期分组df[date] df[timestamp].dt.date 提取日期groupby(date).agg(...) 按天聚合——一行代码完成几千条记录的每日汇总。* numpy 失败率计算fail_rate fails / total * 100向量化计算所有天的失败率比逐天循环快几十倍。* matplotlib 堆叠柱状图横轴日期柱子高度是总抓取次数绿色部分是成功数红色部分是失败数——一张图看清每天抓了多少、废了多少。3.2 业务逻辑 → 代码映射定义机器人抓取数据模型│▼ GraspDataLoader (pandas)读取抓取日志 CSVpd.read_csv(robot_grasp_log.csv, parse_dates[timestamp])解析时间戳、机器人编号、工件编号、抓取结果、失败原因│▼ DataCleaner (pandas)数据清洗处理缺失时间戳删除或标记统一结果编码SUCCESS/FAIL → 1/0解析失败原因代码01→吸盘真空不足, 02→工件偏移, ...│▼ DailyStatistics (pandas/numpy)每日统计groupby(date).agg(totalcount,successsum(result1),failsum(result0))fail_rate fail / total * 100│▼ ReasonAnalyzer (pandas)失败原因分类汇总fail_df.groupby(fail_reason).size()计算各原因占比│▼ GraspVisualizer (matplotlib)可视化1. 每日成功/失败堆叠柱状图2. 每日失败率趋势折线图3. 失败原因饼图│▼ SyntheticDataGenerator (numpy)合成数据生成用 numpy 按泊松分布生成每日抓取次数按概率分布模拟成功/失败和失败原因确保数据自包含、可复现3.3 为什么用 groupby 聚合而不是逐行循环* 问题30 天 × 每天 500 条 15000 行记录逐行判断日期再累加——代码长、速度慢。* 处理策略pandas.groupby() agg() 向量化聚合C 语言底层加速毫秒级完成。* 工程合理性机器人工作站 7×24 小时运行日志量可能达到百万级向量化是唯一合理的选择。3.4 分析前后对比维度 人工查看废料筐 Python 程序统计判断依据 废料筐里多了几个废件 日志中的 SUCCESS/FAIL 标记时间粒度 次日才发现 实时/按班次统计信息量 废了几个 成功数、失败数、失败率、原因分布趋势感知 无 失败率连续上升 → 提前预警根因定位 靠猜 失败原因排名 → 定位系统性问题四、OOP 代码实现4.1 项目结构robot_grasp_analyzer/├── robot_grasp_analyzer/│ ├── __init__.py│ ├── data_loader.py # 抓取日志数据加载│ ├── data_cleaner.py # 数据清洗│ ├── daily_statistics.py # 每日统计│ ├── reason_analyzer.py # 失败原因分类│ ├── visualizer.py # 可视化│ └── synthetic_data.py # 合成数据生成├── tests/│ ├── __init__.py│ └── test_grasp_analyzer.py # 单元测试├── results/│ ├── daily_report.csv # 每日统计报表│ ├── reason_summary.csv # 失败原因汇总│ ├── daily_stack.png # 每日堆叠柱状图│ ├── fail_rate_trend.png # 失败率趋势图│ ├── reason_pie.png # 失败原因饼图│ └── grasp_report.txt # 文本分析报告└── run_grasp.py # 主程序入口4.2 核心源码detailssummary/summary机器人抓取日志数据加载器import pandas as pdfrom pathlib import Pathfrom typing import Optionalclass GraspDataLoader:机器人抓取日志数据加载器支持 CSV 格式的抓取记录。def __init__(self, filepath: str, encoding: str utf-8):self.filepath Path(filepath)self.encoding encodingself._raw_df: Optional[pd.DataFrame] Nonedef load(self,timestamp_col: str timestamp,robot_col: str robot_id,workpiece_col: str workpiece_id,result_col: str grasp_result,reason_col: str fail_reason) - pd.DataFrame:加载抓取日志数据Parameters----------timestamp_col : str时间戳列名robot_col : str机器人编号列名workpiece_col : str工件编号列名result_col : str抓取结果列名reason_col : str失败原因列名Returns-------pd.DataFrameif not self.filepath.exists():raise FileNotFoundError(f文件不存在: {self.filepath})self._raw_df pd.read_csv(self.filepath,encodingself.encoding,parse_dates[timestamp_col] if timestamp_col else None,)# 列名标准化col_aliases {timestamp_col: [时间, timestamp, time, datetime],robot_col: [机器人, robot_id, robot, station],workpiece_col: [工件, workpiece_id, part_id, wp],result_col: [结果, grasp_result, result, status],reason_col: [失败原因, fail_reason, reason, error_code],}rename_map {}for target, aliases in col_aliases.items():if target not in self._raw_df.columns:for alias in aliases:if alias in self._raw_df.columns:rename_map[alias] targetbreakif rename_map:self._raw_df self._raw_df.rename(columnsrename_map)return self._raw_df.copy()def get_raw_data(self) - Optional[pd.DataFrame]:return self._raw_df.copy() if self._raw_df is not None else None/detailsdetailssummary/summary抓取数据清洗import numpy as npimport pandas as pdfrom typing import Dict, Optionalclass DataCleaner:机器人抓取数据清洗器处理缺失值、统一结果编码、解析失败原因。# 结果编码映射RESULT_MAP {SUCCESS: 1, 成功: 1, OK: 1, Pass: 1, 1: 1,FAIL: 0, 失败: 0, NG: 0, Fail: 0, 0: 0,}# 失败原因代码映射REASON_MAP {01: 吸盘真空不足,02: 工件位置偏移,03: 夹具干涉,04: 视觉定位超时,05: 气压不足,06: 工件变形,Unknown: 未知原因,: 未知原因,}def __init__(self):passdef clean(self,df: pd.DataFrame,timestamp_col: str timestamp,result_col: str grasp_result,reason_col: str fail_reason) - pd.DataFrame:清洗抓取数据Parameters----------df : pd.DataFrame原始数据timestamp_col : str时间戳列名result_col : str抓取结果列名reason_col : str失败原因列名Returns-------pd.DataFrameresult df.copy()# 时间戳处理if timestamp_col in result.columns:result[timestamp_col] pd.to_datetime(result[timestamp_col], errorscoerce)# 删除无效时间戳result result.dropna(subset[timestamp_col])# 提取日期result[date] result[timestamp_col].dt.date.astype(str)# 统一结果编码if result_col in result.columns:result[result_code] result[result_col].map(lambda x: self.RESULT_MAP.get(str(x).strip(), -1))else:result[result_code] -1# 解析失败原因if reason_col in result.columns:result[reason_text] result[reason_col].map(lambda x: self.REASON_MAP.get(str(x).strip(), str(x)))else:result[reason_text] 未知原因return result.reset_index(dropTrue)/detailsdetailssummary/summary每日抓取统计import numpy as npimport pandas as pdfrom typing import Dict, Optionalclass DailyStatistics:机器人每日抓取统计器按日期分组统计成功数、失败数、失败率。def __init__(self):passdef calculate(self,df: pd.DataFrame,date_col: str date,result_col: str result_code) - pd.DataFrame:计算每日统计Parameters----------df : pd.DataFrame清洗后的数据date_col : str日期列名result_col : str结果编码列名1成功, 0失败Returns-------pd.DataFrame每日统计表if df.empty:return pd.DataFrame()# 按日期分组聚合daily df.groupby(date_col).agg(total(result_col, count),success(result_col, lambda x: (x 1).sum()),fail(result_col, lambda x: (x 0).sum()),).reset_index()# 计算失败率daily[fail_rate] (daily[fail] / daily[total] * 100).round(2)daily[success_rate] (100 - daily[fail_rate]).round(2)# 排序daily daily.sort_values(date_col).reset_index(dropTrue)return dailydef get_overall_summary(self, daily_df: pd.DataFrame) - Dict:计算整体汇总if daily_df.empty:return {}return {total_days: len(daily_df),total_grasps: daily_df[total].sum(),total_success: daily_df[success].sum(),total_fail: daily_df[fail].sum(),overall_fail_rate: (daily_df[fail].sum() / daily_df[total].sum() * 100).round(2),avg_daily_grasps: daily_df[total].mean().round(1),max_daily_fail_rate: daily_df[fail_rate].max(),min_daily_fail_rate: daily_df[fail_rate].min(),}/detailsdetailssummary/summary失败原因分类分析import numpy as npimport pandas as pdfrom typing import Dict, Optionalclass ReasonAnalyzer:机器人抓取失败原因分类分析器按失败原因分组汇总计算各原因占比。def __init__(self):passdef analyze(self,df: pd.DataFrame,reason_col: str reason_text) - pd.DataFrame:分析失败原因分布Parameters----------df : pd.DataFrame清洗后的数据含失败原因文本reason_col : str失败原因列名Returns-------pd.DataFrame按数量降序排列的原因汇总表# 只取失败记录fail_df df[df[result_code] 0].copy()if fail_df.empty:return pd.DataFrame()# 按原因分组reason_counts fail_df[reason_col].value_counts().reset_index()reason_counts.columns [fail_reason, count]# 计算占比total_fails reason_counts[count].sum()reason_counts[percentage] (reason_counts[count] / total_fails * 100).round(2)# 排序reason_counts reason_counts.sort_values(count, ascendingFalse).reset_index(dropTrue)return reason_countsdef get_top_reason(self, reason_df: pd.DataFrame) - Optional[str]:获取最主要的失败原因if reason_df.empty:return Nonereturn reason_df.iloc[0][fail_reason]/detailsdetailssummary/summary抓取统计可视化import numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom pathlib import Pathfrom typing import Optionalplt.rcParams[font.sans-serif] [SimHei, DejaVu Sans]plt.rcParams[axes.unicode_minus] Falseclass GraspVisualizer:机器人抓取统计可视化COLORS {success: #2ECC71,fail: #E74C3C,warning: #F39C12,}PIE_COLORS [#E74C3C, #F39C12, #3498DB, #9B59B6,#1ABC9C, #34495E, #E67E22, #95A5A6,]def __init__(self, results_dir: str results):self.results_dir Path(results_dir)self.results_dir.mkdir(exist_okTrue)def plot_daily_stack(self,df: pd.DataFrame,date_col: str date,success_col: str success,fail_col: str fail,title: str 每日抓取成功/失败统计) - None:绘制每日成功/失败堆叠柱状图fig, ax plt.subplots(figsize(14, 6))x np.arange(len(df))width 0.6bars_success ax.bar(x, df[success_col], width,label成功, colorself.COLORS[success], alpha0.85,)bars_fail ax.bar(x, df[fail_col], width,bottomdf[success_col],label失败, colorself.COLORS[fail], alpha0.85,)# 标注失败数for i, (s, f) in enumerate(zip(df[success_col], df[fail_col])):if f 0:ax.text(i, s f / 2,str(f), hacenter, vacenter,fontsize8, fontweightbold, colorwhite,)ax.set_xlabel(日期, fontsize12)ax.set_ylabel(抓取次数, fontsize12)ax.set_title(title, fontsize14, fontweightbold)ax.set_xticks(x)ax.set_xticklabels(df[date_col], rotation45, haright)ax.legend()ax.grid(axisy, alpha0.3)plt.tight_layout()plt.savefig(self.results_dir / daily_stack.png,dpi150, bbox_inchestight)plt.close()def plot_fail_rate_trend(self,df: pd.DataFrame,date_col: str date,fail_rate_col: str fail_rate,title: str 每日失败率趋势) - None:绘制每日失败率趋势折线图fig, ax plt.subplots(figsize(14, 6))ax.plot(df[date_col], df[fail_rate_col],markero, linewidth2, color#E74C3C, markersize6,)# 参考线ax.axhline(y5.0, color#F39C12, linestyle--,linewidth1.5, alpha0.7, label警告线 5%)ax.axhline(y10.0, color#E74C3C, linestyle--,linewidth1.5, alpha0.7, label危险线 10%)# 标注数值for _, row in df.iterrows():ax.annotate(f{row[fail_rate_col]:.1f}%,(row[date_col], row[fail_rate_col]),textcoordsoffset points, xytext(0, 10),hacenter, fontsize8,)ax.set_xlabel(日期, fontsize12)ax.set_ylabel(失败率 (%), fontsize12)ax.set_title(title, fontsize14, fontweightbold)ax.legend()ax.grid(alpha0.3)plt.xticks(rotation45, haright)plt.tight_layout()plt.savefig(self.results_dir / fail_rate_trend.png,dpi150, bbox_inchestight)plt.close()def plot_reason_pie(self,df: pd.DataFrame,reason_col: str fail_reason,count_col: str count,title: str 失败原因分布) - None:绘制失败原因饼图if df.empty:returnfig, ax plt.subplots(figsize(10, 8))wedges, texts, autotexts ax.pie(df[count_col],labelsdf[reason_col],autopct%1.1f%%,colorsself.PIE_COLORS[:len(df)],startangle90,pctdistance0.85,)# 美化for autotext in autotexts:autotext.set_color(white)autotext.set_fontsize(10)autotext.set_fontweight(bold)ax.set_title(title, fontsize14, fontweightbold)plt.tight_layout()plt.savefig(self.results_dir / reason_pie.png,dpi150, bbox_inchestight)plt.close()/detailsdetailssummary/summary合成数据生成器import numpy as npimport pandas as pdfrom pathlib import Pathfrom typing import Optionalclass SyntheticDataGenerator:机器人抓取日志合成数据生成器模拟多天、多机器人的抓取记录。def __init__(self, rng: Optional[np.random.RandomState] None):self.rng rng or np.random.RandomState(42)self.robots [fRobot-{i} for i in range(1, 4)] # 3 台机器人# 失败原因及概率分布self.fail_reasons {01: 0.40, # 吸盘真空不足最常见02: 0.25, # 工件位置偏移03: 0.15, # 夹具干涉04: 0.10, # 视觉定位超时05: 0.07, # 气压不足06: 0.03, # 工件变形}def generate(self,n_days: int 30,output_path: str robot_grasp_log.csv) - pd.DataFrame:生成合成抓取日志Parameters----------n_days : int天数output_path : str输出路径Returns-------pd.DataFramerecords []record_id 0base_date pd.Timestamp(2026-01-01)for day in range(n_days):current_date base_date pd.Timedelta(daysday)for robot in self.robots:# 每天每台机器人约 400-600 次抓取泊松分布daily_grasps self.rng.poisson(lam500)# 模拟失败率趋势随时间略有上升模拟设备退化base_fail_rate 0.02 day * 0.001 # 从 2% 缓慢上升到 ~5%for _ in range(daily_grasps):record_id 1# 时间戳均匀分布在 8 小时班次内second_in_shift self.rng.randint(0, 8 * 3600)timestamp current_date pd.Timedelta(secondssecond_in_shift)# 抓取结果is_fail self.rng.random() base_fail_rategrasp_result FAIL if is_fail else SUCCESS# 失败原因fail_reason if is_fail:# 按概率分布选择失败原因rand self.rng.random()cumsum 0for reason, prob in self.fail_reasons.items():cumsum probif rand cumsum:fail_reason reasonbreakif not fail_reason:fail_reason Unknown# 工件编号利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表