周五上午,机器人上下料工作站。

"这批毛坯又出问题了,"质检员小吴拿着卡尺,皱着眉,"来料尺寸标称 Φ50±0.05mm,实际我抽了 20 件,最大 Φ50.12,最小 Φ49.83,波动快到 0.3mm 了。机器人抓手按标称值算抓取中心,结果大件抓偏、小件夹不紧,定位误差直接飙到 0.4mm 以上,后面 CNC 加工余量不够,报废了 3 个。"

我接上他导出的来料尺寸测量表。

"这表里有什么?"小吴问。

"每批来料的实测外径、内径、高度,还有对应的机器人抓取后实测定位误差,"我指着屏幕,"但它就是两张表——一张来料尺寸,一张定位误差。你隐约觉得'来料大了就抓偏',但说不清到底是哪个尺寸维度影响最大、影响多少。现在调机器人抓取参数只能凭经验试,试一次撞一次。"

"我就想干一件事,"小吴说,"把来料尺寸波动和定位误差关联起来。算清楚:外径每大 0.1mm,定位误差增加多少?内径波动和高度的耦合效应呢?给我一个灵敏度排名,告诉我'以后来料检验重点卡哪个尺寸'。"

"比如外径波动贡献了 60% 的定位误差,内径只贡献 15%,"我接话,"用 pandas 做数据关联,numpy 算协方差矩阵,scipy 做多元线性回归+偏相关系数,scikit-learn 做随机森林特征重要性验证,matplotlib 画散点矩阵+残差图+灵敏度柱状图+误差传播蒙特卡洛模拟。networkx 把'尺寸维度→误差分量'当因果链路图。"

"对,"小吴点头,"别给我黑盒,要能说清楚是'外径超差 0.1mm 直接导致 X 方向偏 0.08mm'还是'高度波动通过夹具间隙放大了旋转误差'。我看得懂,能拿去跟采购说'外径公差必须收紧到 ±0.03mm,别的可以放宽'。"

"用 pandas 读来料+误差数据,scipy 做回归+相关性,matplotlib 出 6 图+报告,存 results/,"我开工程,"数据自包含,合成一批含外径/内径/高度三维波动、故意让外径主导定位误差的数据,下载就能跑。"

敲了行原型:

# 定位误差 = f(外径波动, 内径波动, 高度波动) + 随机噪声

# 灵敏度: ∂误差/∂外径, ∂误差/∂内径, ∂误差/∂高度

# 蒙特卡洛: 尺寸按分布采样 → 误差传播 → P95 误差

 

"完整版 OOP 封好,"我说,"数据加载器、尺寸分析器、回归建模器、灵敏度分析器、误差传播模拟器、可视化器,输出灵敏度排名+回归系数+蒙特卡洛P95+6图+报告。"

小吴凑近看:"那以后看报告:外径灵敏度 0.72 排第一,内径 0.18 排第二,高度 0.10 排第三;回归 R²=0.89;蒙特卡洛 P95 误差 0.38mm。结论:来料外径公差收紧到 ±0.03mm,内径和高度可以放宽到 ±0.08mm。"

"对,"我接话,"机器人不是'抓到就行',是'来料波动决定了你能抓多准'。数字孪生里挂来料质量节点,这套就是机器人工作站的'公差分配大脑'。"

 

一、实际应用场景(真实痛点)

 

场景设定:机器人上下料工作站,负责将毛坯件从料框抓取放置到 CNC 夹具上。来料由外协供应商提供,尺寸存在批次波动。机器人视觉/机械定位依赖来料标称尺寸计算抓取位姿。当来料尺寸偏离标称值时,定位误差增大,导致后续加工余量不足或夹具干涉。

 

现场原话(叙事化):

 

"不是我们机器人不准,"小吴说,"是来料尺寸飘了,机器人按标称值算抓取点,实际毛坯大一圈或者小一截,抓手中心就偏了。偏了之后放夹具里歪的,CNC 铣出来一面余量不够直接报废。上个月报废了 7 个,全是这个原因。"

"最坑的是供应商,"小吴补充,"他们每批都出检验报告,写着'符合 ±0.05mm 公差',但你仔细看,外径在上限 +0.04,内径在下限 -0.04,高度又在上限 +0.03——三个维度叠加起来,机器人端的综合定位误差远超 0.05mm。我们跟供应商扯皮,人家说'每个尺寸都在公差内啊'。我们需要的是:量化每个尺寸维度对定位误差的贡献权重,拿着数据去谈——哪个尺寸必须收紧,哪个可以放宽。"

核心矛盾:"来料抽检 + 经验调参" 与 "尺寸波动→定位误差映射 + 灵敏度量化 + 公差分配建议 + 蒙特卡洛误差传播" 之间的断层。

 

二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)

 

《先进制造技术》模块 本篇痛点对应

工业机器人技术基础:末端定位精度、误差来源 来料尺寸→定位误差映射

先进制造技术基础:公差配合、互换性 尺寸波动对装配/加工的影响

数控加工与CAD/CAM技术:加工余量保障 定位误差→余量不足报废

智能制造与数字孪生:质量数据驱动 来料质量→机器人精度数字孪生

先进制造新模式:供应链质量协同 数据驱动公差分配

 

一句话总结:我们需要一个"来料尺寸数据+定位误差记录→回归建模+灵敏度分析+蒙特卡洛误差传播+公差分配建议程序",用 

"pandas" 读尺寸/误差表,

"numpy" 算协方差/灵敏度,

"scipy" 做多元回归+偏相关,

"scikit-learn" 做随机森林特征重要性验证,

"matplotlib" 画散点矩阵/残差/灵敏度/蒙特卡洛,

"networkx" 建尺寸→误差因果链路,实现从"凭经验调机器人"到"量化尺寸影响+公差分配+来料检验标准优化"。

 

三、核心逻辑讲解(大白话)

 

3.1 问题本质:把来料波动想成"多米诺骨牌"

 

把机器人抓取定位想成多米诺骨牌链:

 

* 来料尺寸 = 第一张牌(外径、内径、高度)

* 抓取偏移 = 第二张牌(抓手按标称算,实际偏了)

* 定位误差 = 最后倒下的牌(放歪了,加工报废)

* 灵敏度 = 推第一张牌时,最后一张倒多远

* 回归 = 算"外径每变 0.1mm,定位误差变多少"

* 蒙特卡洛 = 把尺寸波动当骰子摇 10000 次,看误差分布

* 因果链路 = 外径→抓取中心偏移→X方向定位误差

 

3.2 业务逻辑 → 代码映射

 

导入来料尺寸 + 定位误差数据

   │

   ▼ IncomingDataLoader (pandas)

读取两张表:

  来料: 批次, 工件ID, 外径, 内径, 高度, 标称值

  误差: 工件ID, X误差, Y误差, Z误差, 旋转误差

  合并: 按工件ID

   │

   ▼ DimensionAnalyzer (numpy)

尺寸统计:

  各维度: 均值, 标准差, Cp/Cpk

  与标称值偏差分布

   │

   ▼ RegressionModeler (scipy + sklearn)

回归建模:

  多元线性回归: 误差 = β₀ + β₁×外径偏差 + β₂×内径偏差 + β₃×高度偏差

  偏相关系数(控制其他变量后的净相关)

  随机森林特征重要性(非线性验证)

   │

   ▼ SensitivityAnalyzer (numpy)

灵敏度:

  ∂误差/∂各尺寸 = 回归系数标准化

  贡献率 = |βᵢ×σᵢ| / Σ|βⱼ×σⱼ|

   │

   ▼ MonteCarloPropagator (numpy)

误差传播:

  尺寸按正态分布采样(均值, 标准差)

  代入回归模型 → 误差分布

  P95 = 95%分位数

   │

   ▼ CausalGraph (networkx)

因果链路:

  节点: 尺寸维度, 误差分量

  边: 影响方向+权重

   │

   ▼ IncomingVisualizer (matplotlib)

可视化:

  1. 散点矩阵(尺寸 vs 各方向误差)

  2. 回归残差图

  3. 灵敏度排名柱状图

  4. 蒙特卡洛误差分布直方图

  5. 尺寸波动热力图(各批次)

  6. 因果链路图(networkx)

   │

   ▼ SyntheticIncomingData (numpy)

合成数据:

  外径主导(β=0.7), 内径次之(β=0.2), 高度弱(β=0.1)

  可复现

 

3.3 为什么不能只看"尺寸超不超差"

 

视角 问题

看公差范围 每个尺寸单独合格,叠加后可能超差

看报废率 事后统计,不知道根因

回归+灵敏度 量化"哪个尺寸影响最大"

蒙特卡洛 预测"来料波动下误差分布"

因果链路 解释"为什么外径影响大"

 

3.4 分析前后对比

 

维度 经验调参 本程序

尺寸影响 凭感觉 回归系数+灵敏度排名

公差建议 全部收紧(成本高) 重点收紧主导尺寸

误差预测 无 蒙特卡洛 P95

供应商沟通 "你们质量不行" "外径必须 ±0.03"

输出 口头 6图+报告+公差建议

 

四、OOP 代码实现

 

4.1 项目结构

 

incoming_variance_analysis/

├── incoming_variance_analysis/

│ ├── __init__.py

│ ├── incoming_data_loader.py # 来料+误差数据加载

│ ├── dimension_analyzer.py # 尺寸统计(numpy)

│ ├── regression_modeler.py # 回归+偏相关(scipy+sklearn)

│ ├── sensitivity_analyzer.py # 灵敏度分析(numpy)

│ ├── monte_carlo_propagator.py # 蒙特卡洛误差传播

│ ├── causal_graph.py # 因果链路(networkx)

│ ├── visualizer.py # 可视化

│ └── synthetic_incoming_data.py # 合成数据

├── tests/

│ ├── __init__.py

│ └── test_incoming.py

├── results/

│ ├── scatter_matrix.png

│ ├── residual_plot.png

│ ├── sensitivity_rank.png

│ ├── monte_carlo_dist.png

│ ├── batch_heatmap.png

│ ├── causal_network.png

│ ├── sensitivity_table.csv

│ ├── regression_report.txt

│ ├── monte_carlo_stats.csv

│ └ tolerance_recommendation.txt

└── run_incoming.py

 

4.2 核心源码

 

<details>

 

<summary></summary>

 

"""来料尺寸+定位误差数据加载器"""

 

import pandas as pd

from pathlib import Path

from typing import Optional

 

 

class IncomingDataLoader:

    """读取来料尺寸和定位误差数据"""

 

    def __init__(self, incoming_path: str = "incoming_dimensions.csv",

                 error_path: str = "positioning_errors.csv",

                 encoding: str = "utf-8"):

        self.incoming_path = Path(incoming_path)

        self.error_path = Path(error_path)

        self.encoding = encoding

 

    def load(self) -> pd.DataFrame:

        if not self.incoming_path.exists():

            raise FileNotFoundError(self.incoming_path)

        if not self.error_path.exists():

            raise FileNotFoundError(self.error_path)

 

        inc = pd.read_csv(self.incoming_path, encoding=self.encoding)

        err = pd.read_csv(self.error_path, encoding=self.encoding)

 

        req_inc = ["part_id", "batch_id", "outer_dia", "inner_dia", "height"]

        req_err = ["part_id", "error_x", "error_y", "error_z", "error_rot"]

        for c in req_inc + req_err:

            if c not in inc.columns and c not in err.columns:

                raise ValueError(f"缺列: {c}")

 

        inc[req_inc[1:]] = inc[req_inc[1:]].apply(pd.to_numeric, errors="coerce")

        err[req_err] = err[req_err].apply(pd.to_numeric, errors="coerce")

 

        df = pd.merge(inc, err, on="part_id", how="inner")

        df = df.dropna(subset=req_inc[1:]+req_err[1:]).reset_index(drop=True)

 

        # 计算综合误差

        df["total_error"] = np.sqrt(df["error_x"]**2 + df["error_y"]**2 +

                                   df["error_z"]**2 + df["error_rot"]**2)

 

        # 计算与标称值偏差(假设标称外径50, 内径25, 高度30)

        df["outer_dia_dev"] = df["outer_dia"] - 50.0

        df["inner_dia_dev"] = df["inner_dia"] - 25.0

        df["height_dev"] = df["height"] - 30.0

 

        return df

 

</details>

 

<details>

 

<summary></summary>

 

"""尺寸统计分析 (numpy)"""

 

import numpy as np

import pandas as pd

from typing import Dict

 

 

class DimensionAnalyzer:

    """各尺寸维度统计"""

 

    def __init__(self, nominal_outer: float = 50.0,

                 nominal_inner: float = 25.0,

                 nominal_height: float = 30.0):

        self.nominals = {"outer_dia": nominal_outer,

                        "inner_dia": nominal_inner,

                        "height": nominal_height}

 

    def compute(self, df: pd.DataFrame) -> pd.DataFrame:

        dims = ["outer_dia", "inner_dia", "height"]

        results = []

        for dim in dims:

            vals = df[dim].values

            dev = vals - self.nominals[dim]

            mean = np.mean(vals)

            std = np.std(vals, ddof=1)

            # Cp/Cpk

            usl = self.nominals[dim] + 0.05

            lsl = self.nominals[dim] - 0.05

            if std > 0:

                cp = (usl - lsl) / (6 * std)

                cpu = (usl - mean) / (3 * std)

                cpl = (mean - lsl) / (3 * std)

                cpk = min(cpu, cpl)

            else:

                cp = cpk = np.inf

            results.append({

                "dimension": dim,

                "mean": round(mean, 4),

                "std": round(std, 4),

                "min": round(np.min(vals), 4),

                "max": round(np.max(vals), 4),

                "range": round(np.max(vals)-np.min(vals), 4),

                "cp": round(cp, 3),

                "cpk": round(cpk, 3),

            })

        return pd.DataFrame(results)

 

</details>

 

<details>

 

<summary></summary>

 

"""回归建模+偏相关 (scipy + scikit-learn)"""

 

import numpy as np

import pandas as pd

from scipy import stats

from sklearn.linear_model import LinearRegression

from sklearn.ensemble import RandomForestRegressor

from sklearn.metrics import r2_score

from typing import Dict

 

 

class RegressionModeler:

    """多元回归+特征重要性"""

 

    def __init__(self):

        self.lr = LinearRegression()

        self.rf = RandomForestRegressor(n_estimators=100, random_state=42)

 

    def fit(self, df: pd.DataFrame) -> Dict:

        X = df[["outer_dia_dev", "inner_dia_dev", "height_dev"]].values

        y = df["total_error"].values

 

        # 线性回归

        self.lr.fit(X, y)

        y_pred_lr = self.lr.predict(X)

        r2_lr = r2_score(y, y_pred_lr)

 

        # 随机森林

        self.rf.fit(X, y)

        y_pred_rf = self.rf.predict(X)

        r2_rf = r2_score(y, y_pred_rf)

 

        # 偏相关系数

        pcorr = {}

        for i, dim in enumerate(["outer_dia_dev", "inner_dia_dev", "height_dev"]):

            others = [d for d in ["outer_dia_dev", "inner_dia_dev", "height_dev"] if d != dim]

            X_others = df[others].values

            # 残差化

            res_y = y - LinearRegression().fit(X_others, y).predict(X_others)

            res_x = df[dim].values - LinearRegression().fit(X_others, df[dim]).predict(X_others)

            pcorr[dim] = round(stats.pearsonr(res_x, res_y)[0], 4)

 

        return {

            "lr_coefficients": dict(zip(

                ["outer_dia_dev", "inner_dia_dev", "height_dev"],

                [round(c,4) for c in self.lr.coef_])),

            "lr_intercept": round(self.lr.intercept_, 4),

            "lr_r2": round(r2_lr, 4),

            "rf_r2": round(r2_rf, 4),

            "partial_correlations": pcorr,

        }

 

    def predict(self, X: np.ndarray) -> np.ndarray:

        return self.lr.predict(X)

 

</details>

 

<details>

 

<summary></summary>

 

"""灵敏度分析 (numpy)"""

 

import numpy as np

import pandas as pd

from typing import Dict, List

 

 

class SensitivityAnalyzer:

    """计算各尺寸维度对定位误差的灵敏度"""

 

    def __init__(self):

        pass

 

    def compute(self, df: pd.DataFrame, reg_coeffs: Dict) -> pd.DataFrame:

        dims = ["outer_dia_dev", "inner_dia_dev", "height_dev"]

        names = ["外径偏差", "内径偏差", "高度偏差"]

 

        results = []

        total_effect = 0

        effects = []

        for dim in dims:

            std_dim = df[dim].std()

            effect = abs(reg_coeffs[dim]) * std_dim

            effects.append(effect)

            total_effect += effect

 

        for i, (dim, name) in enumerate(zip(dims, names)):

            std_dim = df[dim].std()

            contribution = effects[i] / total_effect * 100 if total_effect > 0 else 0

            results.append({

                "dimension": name,

                "reg_coefficient": reg_coeffs[dim],

                "std": round(std_dim, 4),

                "effect_size": round(effects[i], 4),

                "contribution_pct": round(contribution, 1),

            })

 

        return pd.DataFrame(results).sort_values("contribution_pct",

                                                ascending=False).reset_index(drop=True)

 

</details>

 

<details>

 

<summary></summary>

 

"""蒙特卡洛误差传播 (numpy)"""

 

import numpy as np

import pandas as pd

from typing import Dict, List

 

 

class MonteCarloPropagator:

    """尺寸波动→误差传播模拟"""

 

    def __init__(self, n_sim: int = 10000, rng: np.random.RandomState = None):

        self.n_sim = n_sim

        self.rng = rng or np.random.RandomState(42)

 

    def propagate(self, means: Dict, stds: Dict,

                  coeffs: Dict, intercept: float) -> Dict:

        # 采样

        outer_samples = self.rng.normal(means["outer_dia_dev"],

                                       stds["outer_dia_dev"], self.n_sim)

        inner_samples = self.rng.normal(means["inner_dia_dev"],

                                        stds["inner_dia_dev"], self.n_sim)

        height_samples = self.rng.normal(means["height_dev"],

                                         stds["height_dev"], self.n_sim)

 

        # 误差传播

        errors = (intercept +

                  coeffs["outer_dia_dev"] * outer_samples +

                  coeffs["inner_dia_dev"] * inner_samples +

                  coeffs["height_dev"] * height_samples +

                  self.rng.normal(0, 0.02, self.n_sim)) # 噪声

 

        errors = np.abs(errors)

 

        return {

            "mean_error": round(np.mean(errors), 4),

            "std_error": round(np.std(errors), 4),

            "p50": round(np.percentile(errors, 50), 4),

            "p95": round(np.percentile(errors, 95), 4),

            "p99": round(np.percentile(errors, 99), 4),

            "max": round(np.max(errors), 4),

            "samples": errors,

        }

 

</details>

 

<details>

 

<summary></summary>

 

"""因果链路图 (networkx)"""

 

import networkx as nx

import pandas as pd

from typing import Dict, List

 

 

class CausalGraph:

    """尺寸维度→误差分量因果网络"""

 

    def __init__(self):

        self.G = nx.DiGraph()

 

    def build(self, sensitivity_df: pd.DataFrame,

              reg_coeffs: Dict) -> nx.DiGraph:

        self.G.clear()

        # 尺寸节点

        dims = ["外径偏差", "内径偏差", "高度偏差"]

        errors = ["X方向误差", "Y方向误差", "Z方向误差", "旋转误差"]

 

        for d in dims:

            self.G.add_node(d, ntype="dimension", weight=1.0)

        for e in errors:

            self.G.add_node(e, ntype="error", weight=1.0)

 

        # 边(基于回归系数映射)

        mapping = {

            "外径偏差": ["X方向误差", "旋转误差"],

            "内径偏差": ["Y方向误差"],

            "高度偏差": ["Z方向误差"],

        }

        for dim in dims:

            for err in mapping[dim]:

                weight = abs(reg_coeffs.get(dim.replace("偏差", "_dev"), 0.5))

                self.G.add_edge(dim, err, weight=weight)

 

        return self.G

 

</details>

 

<details>

 

<summary></summary>

 

"""可视化 (matplotlib + networkx)"""

 

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from pathlib import Path

import networkx as nx

 

plt.rcParams["font.sans-serif"] = ["SimHei", "DejaVu Sans"]

plt.rcParams["axes.unicode_minus"] = False

 

 

class IncomingVisualizer:

    def __init__(self, results_dir: str = "results"):

        self.results_dir = Path(results_dir)

        self.results_dir.mkdir(exist_ok=True)

 

    def scatter_matrix(self, df: pd.DataFrame):

        """散点矩阵: 尺寸 vs 误差"""

        fig, axes = plt.subplots(2, 3, figsize=(15, 10))

        dims = ["outer_dia_dev", "inner_dia_dev", "height_dev"]

        errs = ["error_x", "total_error"]

        for i, dim in enumerate(dims):

            for j, err in enumerate(errs):

                ax = axes[j, i]

                ax.scatter(df[dim], df[err], alpha=0.6, s=20, c="#3498DB")

                # 趋势线

                z = np.polyfit(df[dim], df[err], 1)

                p = np.poly1d(z)

                ax.plot(df[dim], p(df[dim]), "r--", linewidth=1)

                ax.set_xlabel(dim)

                ax.set_ylabel(err)

                ax.grid(alpha=0.3)

        plt.tight_layout()

        plt.savefig(self.results_dir/"scatter_matrix.png", dpi=150, bbox_inches="tight")

        plt.close()

 

    def residual_plot(self, df: pd.DataFrame, y_true: np.ndarray, y_pred: np.ndarray):

        """回归残差图"""

        residuals = y_true - y_pred

        fig, ax = plt.subplots(1, 2, figsize=(12, 5))

        ax[0].scatter(y_pred, residuals, alpha=0.6, s=20)

        ax[0].axhline(0, color="r", linestyle="--")

        ax[0].set_xlabel("预测误差")

        ax[0].set_ylabel("残差")

        ax[0].set_title("残差 vs 预测值")

        ax[0].grid(alpha=0.3)

        ax[1].hist(residuals, bins=20, color="#3498DB", alpha=0.7, edgecolor="black")

        ax[1].set_xlabel("残差")

        ax[1].set_ylabel("频次")

        ax[1].set_title("残差分布")

        ax[1].grid(alpha=0.3)

        plt.tight_layout()

        plt.savefig(self.results_dir/"residual_plot.png", dpi=150, bbox_inches="tight")

        plt.close()

 

    def sensitivity_rank(self, sens_df: pd.DataFrame):

        """灵敏度排名"""

        fig, ax = plt.subplots(figsize=(8, 5))

        ax.barh(sens_df["dimension"][::-1],

                sens_df["contribution_pct"][::-1],

                color=["#E74C3C","#3498DB","#2ECC71"][::-1], alpha=0.8)

        ax.set_xlabel("贡献率 (%)")

        ax.set_title("各尺寸维度对定位误差的贡献率", fontsize=13, fontweight="bold")

        ax.grid(axis="x", alpha=0.3)

        for i, v in enumerate(sens_df["contribution_pct"][::-1]):

            ax.text(v+1, i, f"{v}%", va="center", fontsize=9)

        plt.tight_layout()

        plt.savefig(self.results_dir/"sensitivity_rank.png", dpi=150, bbox_inches="tight")

        plt.close()

 

    def monte_carlo_dist(self, mc_result: Dict):

        """蒙特卡洛误差分布"""

        fig, ax = plt.subplots(figsize=(10, 6))

        ax.hist(mc_result["samples"], bins=50, color="#9B59B6",

                alpha=0.7, edgecolor="black", density=True)

        ax.axvline(mc_result["p95"], color="red", linestyle="--", linewidth=2,

                   label=f"P95={mc_result['p95']}mm")

        ax.axvline(mc_result["mean_error"], color="green", linestyle="--", linewidth=2,

                   label=f"均值={mc_result['mean_error']}mm")

        ax.set_xlabel("定位误差 (mm)")

        ax.set_ylabel("概率密度")

        ax.set_title("蒙特卡洛误差传播分布", fontsize=13, fontweight="bold")

        ax.legend()

        ax.grid(alpha=0.3)

        plt.tight_layout()

        plt.savefig(self.results_dir/"monte_carlo_dist.png", dpi=150, bbox_inches="tight")

        plt.close()

 

    def batch_heatmap(self, df: pd.DataFrame):

        """各批次尺寸波动热力图"""

        batch_stats = df.groupby("batch_id")[["outer_dia_dev","inner_dia_dev","height_dev"]].mean()

        fig, ax = plt.subplots(figsize=(8, max(4, len(batch_stats)*0.4)))

        im = ax.imshow(batch_stats.values.T, cmap="RdYlBu_r", aspect="auto")

        ax.set_xticks(range(len(batch_stats)))

        ax.set_xticklabels(batch_stats.index, rotation=45)

        ax.set_yticks(range(3))

        ax.set_yticklabels(["外径偏差","内径偏差","高度偏差"])

        ax.set_title("各批次尺寸偏差热力图", fontsize=13, fontweight="bold")

        plt.colorbar(im, ax=ax)

        plt.tight_layout()

        plt.savefig(self.results_dir/"batch_heatmap.png", dpi=150, bbox_inches="tight")

        plt.close()

 

    def causal_network(self, G: nx.DiGraph):

        """因果链路图"""

        fig, ax = plt.subplots(figsize=(10, 7))

        pos = nx.spring_layout(G, seed=42, k=0.8)

        node_colors = ["#E74C3C" if G.nodes[n].get("ntype")=="dimension" else "#3498DB"

                      for n in G.nodes()]

        node_sizes = [G.nodes[n].get("weight",1)*500 for n in G.nodes()]

        edge_weights = [G[u][v].get("weight",0.5)*3 for u,v in G.edges()]

        nx.draw_networkx_nodes(G, pos, node_color=node_colors,

                              node_size=node_sizes, alpha=0.85, ax=ax)

        nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5,

                              arrows=True, arrowsize=15, ax=ax)

        labels = {n: n for n in G.nodes()}

        nx.draw_networkx_labels(G, pos, labels, font_size=9, ax=ax)

        ax.set_title("尺寸维度→定位误差因果链路", fontsize=13, fontweight="bold")

        ax.axis("off")

        plt.tight_layout()

        plt.savefig(self.results_dir/"causal_network.png", dpi=150, bbox_inches="tight")

        plt.close()

 

</details>

 

<details>

 

<summary></summary>

 

"""合成来料尺寸+定位误差数据"""

 

import numpy as np

import pandas as pd

from pathlib import Path

from typing import Optional

 

 

class SyntheticIncomingGenerator:

    """

    合成来料尺寸+定位误差

    外径主导定位误差

    """

 

    def __init__(self, rng: Optional[np.random.RandomState] = None):

        self.rng = rng or np.random.RandomState(42)

 

    def generate(self, n_parts: int = 60,

                 incoming_path: str = "incoming_dimensions.csv",

                 error_path: str = "positioning_errors.csv") -> tuple:

        batches = [f"B{i:03d}" for i in range(1, 7)]

 

        # 来料尺寸

        inc_records = []

        err_records = []

        part_id = 1

        for _ in range(n_parts):

            batch = self.rng.choice(batches)

            # 外径: 均值50, 标准差0.04(主导)

            outer = self.rng.normal(50.0, 0.04)

            # 内径: 均值25, 标准差0.02

            inner = self.rng.normal(25.0, 0.02)

            # 高度: 均值30, 标准差0.03

            heigh

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

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐