周二下午,机器人调试间。

"标定做完了,可我不知道到底改善了多少,"调试员小赵把两份 CSV 甩在桌上,"一份是标定前,一份是标定后,每个点位记录了实际坐标和理论坐标,差值就是误差。可二十个点位,每个点位 XYZ 三个方向,我怎么跟工艺科说'标定有效'?"

我点开文件。

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

"每个点位一行,理论坐标 X/Y/Z,实际坐标 X/Y/Z,标定前一份、标定后一份,"我指着数据,"问题是你只看了某几个点的单轴误差,没算整体定位误差的统计量。"

"我就想干一件事,"小赵说,"把标定前后的定位误差做个对比——均值降了多少、最大误差降了多少、每个方向的改善幅度、有没有哪个点位改善不明显甚至变差了。最好画个图,让工艺科一眼看出来标定值不值。"

"比如点位 P05,标定前误差 2.1mm,标定后 0.4mm,降了 80%,"我接话,"但点位 P12 标定前 1.8mm,标定后 1.5mm,只降了 16%,那就要看是不是那个位置关节柔性大,或者标定参数没覆盖到。"

"对,"小赵点头,"还有,我想看 XYZ 三个方向各自改善了多少,是不是某个方向标定效果特别差。以前只能说'差不多准了',现在要拿数据说话。"

"用 pandas 读两份标定数据,numpy 算欧氏距离误差向量,scipy 做配对 t 检验验证改善显著性,matplotlib 画误差对比散点+改善幅度柱状图+方向分解图,scikit-learn 聚类找改善不明显的异常点位,networkx 建'点位-方向-改善幅度'关系网,"我开工程,"数据自包含,合成 20 个点位的标定前后坐标,下载就能跑。"

敲了行原型:

err_before = np.linalg.norm(pts[["actual_x","actual_y","actual_z"]].values - 

                            pts[["theory_x","theory_y","theory_z"]].values, axis=1)

 

"完整版用 OOP 封好,"我说,"一个类管数据加载,一个类算误差,一个类做前后对比统计,一个类做显著性检验,一个类找改善异常点位,一个类建关系网,一个类出图。输出标定前后误差分布、改善幅度、异常点位清单,存 results/。"

小赵凑近看:"那以后看报告:标定前均误差 2.34mm,标定后 0.52mm,整体改善 77.8%,X 方向改善 82%,Y 方向 71%,Z 方向 79%。P12 只改善 16%,标红,建议重新采集该区域标定数据。"

"对,"我接话,"机器人标定不是'做了就行',得量化改善幅度。数字孪生里机器人模型要校准,这张误差对比表就是校准效果的验收单。"

 

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

 

场景设定:六轴工业机器人现场标定(四点法/激光跟踪仪),采集标定前后各 N 个示教点的理论坐标与实际坐标。调试员只能肉眼对比个别点位,无法统计整体改善幅度,也无法识别标定覆盖薄弱区域,导致"标定有效"缺乏量化证据。

 

现场原话(叙事化):

 

"不是标定没用,"小赵说,"是没法证明。我花两天标定完,工艺科问'好了多少',我说'感觉准了'。人家不信,让我拿数据。我翻出标定前后坐标表,二十个点,每个点 XYZ 三个数,我总不能让人一个个减吧?后来我拿 Excel 减了几个,发现大部分点误差从 2mm 降到 0.5mm,但有俩点只降了一点,我说不清为啥。"

"还有 XYZ 方向,有的点 X 方向改善特别明显,Z 方向几乎没变,这跟标定方法有关系——四点法对 XY 平面敏感,Z 方向补偿弱。"

核心矛盾:"标定前后坐标流水" 与 "定位误差向量计算 + 前后配对统计 + 方向分解 + 改善显著性检验 + 异常点位识别" 之间的断层。

 

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

 

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

工业机器人技术基础:机器人标定、DH参数修正、定位精度 标定前后定位误差量化对比

先进制造技术基础:精密测量与误差分析 三维空间误差向量统计

智能制造与数字孪生:机器人数字模型校准 标定效果验收的数据底座

数控加工与CAD/CAM技术:加工精度保障 机器人末端精度对标机床精度

FMS与先进生产管理:设备能力验证 标定作为设备维护标准流程的量化验收

 

一句话总结:我们需要一个"机器人标定前后定位误差改善幅度分析程序",用 

"pandas" 加载标定数据,

"numpy" 算三维误差向量,

"scipy" 做配对 t 检验,

"scikit-learn" 聚类找改善异常点位,

"networkx" 建点位-方向关系网,实现从"感觉准了"到"量化改善幅度 + 显著性验证 + 薄弱点位定位"。

 

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

 

3.1 问题本质:把机器人想成"手眼协调测试"

 

把机器人末端想成你的手指去点靶纸:

 

* 理论坐标 = 靶心位置(你应该点到的地方)

* 实际坐标 = 手指落点(实际点到的地方)

* 定位误差 = 手指离靶心多远(三维空间直线距离)

* 标定前 = 没练过的手,到处偏

* 标定后 = 练过的手,偏差小了

* 改善幅度 = (标定前误差 - 标定后误差) / 标定前误差

* 方向分解 = 偏左偏右(ΔX) vs 偏高偏低(ΔZ),看哪个方向改善最明显

* 异常点位 = 别人都进步了 80%,就它只进步 15%,说明那个区域标定没覆盖到

* 配对 t 检验 = 统计学上确认"标定确实有用",不是运气

 

3.2 业务逻辑 → 代码映射

 

加载标定前后坐标数据

   │

   ▼ CalibDataLoader (pandas)

读取 CSV:

  point_id, theory_x, theory_y, theory_z,

  actual_before_x, actual_before_y, actual_before_z,

  actual_after_x, actual_after_y, actual_after_z

  校验坐标完整性

   │

   ▼ ErrorCalculator (numpy)

三维误差向量:

  err = √[(Ax-Tx)² + (Ay-Ty)² + (Az-Tz)²]

  方向分量误差:|Ax-Tx|, |Ay-Ty|, |Az-Tz|

   │

   ▼ ImprovementAnalyzer (pandas/numpy/scipy)

前后对比:

  标定前均值/中位/最大 vs 标定后

  改善幅度 = (err_before - err_after) / err_before

  方向级改善:ΔX/ΔY/ΔZ 各自均值变化

  scipy 配对 t 检验:标定前后误差差异显著性

   │

   ▼ ImprovementAnomalyDetector (scikit-learn)

异常点位识别:

  特征=[改善幅度, 标定后误差]

  KMeans 找改善不明显的点位

   │

   ▼ CalibGraph (networkx)

关系网:

  点位-方向-改善幅度 三层图

  边权=改善幅度(越小越粗=越薄弱)

   │

   ▼ CalibVisualizer (matplotlib)

可视化:

  1. 标定前后误差箱线图

  2. 各点位改善幅度柱状图

  3. XYZ方向误差分解对比

  4. 异常点位散点(标定前误差 vs 改善幅度)

  5. 点位-方向关系网

   │

   ▼ SyntheticCalibGenerator (numpy)

合成数据:

  20点位,标定前误差 1.5~3.5mm

  标定后大部分降 70~85%,少量只降 10~20%

 

3.3 为什么需要配对 t 检验

 

方法 问题

看均值下降 可能是随机波动,不是标定效果

看最大误差 受极端值影响

配对 t 检验 同一点位前后配对,消除点位间差异,严格验证

 

3.4 分析前后对比

 

维度 调试员手算 本程序

误差计算 单点手减 全点位向量化

整体改善 感觉 均值+中位+最大,配对t检验

方向分解 无 XYZ 各自改善幅度

薄弱点位 靠回忆 聚类自动标红

验收报告 口头 统计+图表+显著性

 

四、OOP 代码实现

 

4.1 项目结构

 

robot_calib_eval/

├── robot_calib_eval/

│ ├── __init__.py

│ ├── calib_loader.py # 标定数据加载

│ ├── error_calculator.py # 三维误差计算

│ ├── improvement_analyzer.py # 前后对比统计(scipy)

│ ├── improvement_anomaly.py # 改善异常点位识别

│ ├── calib_graph.py # 点位-方向关系网

│ ├── visualizer.py # 可视化

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

├── tests/

│ ├── __init__.py

│ └── test_calib_eval.py

├── results/

│ ├── error_box_before_after.png

│ ├── improvement_bar.png

│ ├── direction_decomp.png

│ ├── anomaly_scatter.png

│ ├── calib_graph.png

│ ├── error_detail.csv

│ ├── improvement_summary.csv

│ ├── anomaly_points.csv

│ └── calib_report.txt

└── run_calib_eval.py

 

4.2 核心源码

 

<details>

 

<summary></summary>

 

"""机器人标定数据加载器"""

 

import pandas as pd

from pathlib import Path

from typing import Optional, Tuple

 

 

class CalibDataLoader:

    """加载标定前后坐标数据(单文件双时段 或 两个文件)"""

 

    def __init__(self, filepath: str = "calib_data.csv",

                 encoding: str = "utf-8"):

        self.filepath = Path(filepath)

        self.encoding = encoding

        self._raw: Optional[pd.DataFrame] = None

 

    def load(self) -> pd.DataFrame:

        if not self.filepath.exists():

            raise FileNotFoundError(f"文件不存在: {self.filepath}")

        self._raw = pd.read_csv(self.filepath, encoding=self.encoding)

 

        rename = {}

        for tgt, al in {

            "point_id": ["point_id", "点位", "id"],

            "theory_x": ["theory_x", "理论X", "tx"],

            "theory_y": ["theory_y", "理论Y", "ty"],

            "theory_z": ["theory_z", "理论Z", "tz"],

            "actual_before_x": ["actual_before_x", "标定前X", "abx"],

            "actual_before_y": ["actual_before_y", "标定前Y", "aby"],

            "actual_before_z": ["actual_before_z", "标定前Z", "abz"],

            "actual_after_x": ["actual_after_x", "标定后X", "aax"],

            "actual_after_y": ["actual_after_y", "标定后Y", "aay"],

            "actual_after_z": ["actual_after_z", "标定后Z", "aaz"],

        }.items():

            if tgt not in self._raw.columns:

                for a in al:

                    if a in self._raw.columns:

                        rename[a] = tgt

                        break

        self._raw = self._raw.rename(columns=rename)

 

        req = ["point_id", "theory_x", "theory_y", "theory_z",

               "actual_before_x", "actual_before_y", "actual_before_z",

               "actual_after_x", "actual_after_y", "actual_after_z"]

        miss = [c for c in req if c not in self._raw.columns]

        if miss:

            raise ValueError(f"缺少必要列: {miss}")

 

        for c in req[1:]: # 坐标列转数值

            self._raw[c] = pd.to_numeric(self._raw[c], errors="coerce")

        self._raw = self._raw.dropna(subset=req[1:]).reset_index(drop=True)

        return self._raw.copy()

 

</details>

 

<details>

 

<summary></summary>

 

"""三维定位误差计算器 (numpy)"""

 

import numpy as np

import pandas as pd

from typing import Optional, Tuple

 

 

class ErrorCalculator:

    """计算标定前后各点位三维定位误差"""

 

    def __init__(self):

        pass

 

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

        out = df.copy()

        theory = out[["theory_x", "theory_y", "theory_z"]].values

        before = out[["actual_before_x", "actual_before_y", "actual_before_z"]].values

        after = out[["actual_after_x", "actual_after_y", "actual_after_z"]].values

 

        # 欧氏距离

        err_b = np.linalg.norm(before - theory, axis=1)

        err_a = np.linalg.norm(after - theory, axis=1)

 

        out["err_before_mm"] = np.round(err_b, 4)

        out["err_after_mm"] = np.round(err_a, 4)

 

        # 方向分量绝对误差

        d_b = np.abs(before - theory)

        d_a = np.abs(after - theory)

        for i, ax in enumerate(["x", "y", "z"]):

            out[f"err_before_{ax}_mm"] = np.round(d_b[:, i], 4)

            out[f"err_after_{ax}_mm"] = np.round(d_a[:, i], 4)

 

        return out

 

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

        """计算改善幅度"""

        out = df.copy()

        # 改善幅度 (err_before - err_after) / err_before

        imp = np.where(

            out["err_before_mm"] > 0,

            (out["err_before_mm"] - out["err_after_mm"]) / out["err_before_mm"],

            0.0

        )

        out["improvement_ratio"] = np.round(imp, 4)

 

        # 绝对改善量

        out["improvement_abs_mm"] = np.round(

            out["err_before_mm"] - out["err_after_mm"], 4

        )

 

        # 方向级改善

        for ax in ["x", "y", "z"]:

            imp_ax = np.where(

                out[f"err_before_{ax}_mm"] > 0,

                (out[f"err_before_{ax}_mm"] - out[f"err_after_{ax}_mm"]) /

                out[f"err_before_{ax}_mm"],

                0.0

            )

            out[f"imp_{ax}"] = np.round(imp_ax, 4)

 

        return out

 

</details>

 

<details>

 

<summary></summary>

 

"""标定前后改善统计分析 (scipy)"""

 

import numpy as np

import pandas as pd

from scipy import stats

from typing import Optional, Dict

 

 

class ImprovementAnalyzer:

    """配对t检验 + 方向分解统计"""

 

    def __init__(self, conf: float = 0.95):

        self.conf = conf

 

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

        """整体统计量"""

        eb = df["err_before_mm"].values

        ea = df["err_after_mm"].values

        return {

            "n_points": len(df),

            "before_mean": round(float(eb.mean()), 4),

            "before_median": round(float(np.median(eb)), 4),

            "before_max": round(float(eb.max()), 4),

            "before_std": round(float(eb.std(ddof=1)), 4),

            "after_mean": round(float(ea.mean()), 4),

            "after_median": round(float(np.median(ea)), 4),

            "after_max": round(float(ea.max()), 4),

            "after_std": round(float(ea.std(ddof=1)), 4),

            "improvement_mean": round(

                (eb.mean() - ea.mean()) / eb.mean() * 100, 2

            ) if eb.mean() > 0 else 0.0,

        }

 

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

        """配对t检验: 标定前后误差差异"""

        eb = df["err_before_mm"].values

        ea = df["err_after_mm"].values

        if len(eb) < 2:

            return {"t_stat": np.nan, "p_value": np.nan}

        t, p = stats.ttest_rel(eb, ea)

        return {

            "t_stat": round(float(t), 4),

            "p_value": round(float(p), 4),

            "significant": bool(p < (1 - self.conf)),

        }

 

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

        """各方向改善统计"""

        rows = []

        for ax in ["x", "y", "z"]:

            eb = df[f"err_before_{ax}_mm"].values

            ea = df[f"err_after_{ax}_mm"].values

            imp = df[f"imp_{ax}"].values

            rows.append({

                "axis": ax.upper(),

                "before_mean_mm": round(float(eb.mean()), 4),

                "after_mean_mm": round(float(ea.mean()), 4),

                "improvement_ratio": round(float(imp.mean()), 4),

                "improvement_pct": round(float(imp.mean()) * 100, 2),

            })

        return pd.DataFrame(rows).sort_values("improvement_pct", ascending=True).reset_index(drop=True)

 

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

        """按改善幅度排序"""

        sub = df[["point_id", "err_before_mm", "err_after_mm",

                   "improvement_ratio", "improvement_abs_mm"]].copy()

        sub = sub.sort_values("improvement_ratio").reset_index(drop=True)

        return sub

 

</details>

 

<details>

 

<summary></summary>

 

"""改善异常点位识别 (scikit-learn)"""

 

import numpy as np

import pandas as pd

from sklearn.cluster import KMeans

from sklearn.preprocessing import StandardScaler

from typing import Optional

 

 

class ImprovementAnomalyDetector:

    """找改善不明显的点位"""

 

    def __init__(self, n_clusters: int = 2, random_state: int = 42,

                 low_imp_thresh: float = 0.3):

        self.n_clusters = n_clusters

        self.random_state = random_state

        self.low_imp_thresh = low_imp_thresh

        self.scaler = StandardScaler()

 

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

        out = df.copy()

        # 特征:改善幅度 + 标定后误差

        X = out[["improvement_ratio", "err_after_mm"]].values

        Xs = self.scaler.fit_transform(X)

        k = min(self.n_clusters, len(out))

        km = KMeans(n_clusters=k, random_state=self.random_state, n_init=10)

        out["cluster"] = km.fit_predict(Xs)

        # 改善幅度低的簇

        cmean = out.groupby("cluster")["improvement_ratio"].mean()

        low_cluster = cmean.idxmin()

        out["cluster_is_low_imp"] = out["cluster"] == low_cluster

        # 规则:改善<30% 或 标定后误差>1.5mm

        out["rule_low"] = (out["improvement_ratio"] < self.low_imp_thresh) | \

                          (out["err_after_mm"] > 1.5)

        out["is_anomaly"] = out["cluster_is_low_imp"] | out["rule_low"]

        return out.sort_values("improvement_ratio").reset_index(drop=True)

 

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

        sub = df[df["is_anomaly"]]

        return sub[["point_id", "err_before_mm", "err_after_mm",

                    "improvement_ratio", "improvement_abs_mm"]].copy()

 

</details>

 

<details>

 

<summary></summary>

 

"""点位-方向-改善幅度关系网 (networkx)"""

 

import networkx as nx

import pandas as pd

from typing import Optional

 

 

class CalibGraph:

    """建三层关系网"""

 

    def __init__(self):

        self.G = nx.Graph()

 

    def build(self, df: pd.DataFrame) -> nx.Graph:

        self.G.clear()

        for _, r in df.iterrows():

            pid = f"PT:{r['point_id']}"

            self.G.add_node(pid, ntype="point")

            for ax in ["X", "Y", "Z"]:

                axn = f"AX:{ax}"

                self.G.add_node(axn, ntype="axis")

                imp_val = float(r[f"imp_{ax.lower()}"])

                # 改善幅度越小(越薄弱)边越粗

                w = max(0.1, (1.0 - imp_val) * 10)

                self.G.add_edge(pid, axn, weight=round(w, 2),

                                improvement=round(imp_val, 4))

        return self.G

 

    def weak_combinations(self) -> pd.DataFrame:

        """找改善最弱的点位-方向组合"""

        rows = []

        for u, v, d in self.G.edges(data=True):

            if self.G.nodes[u].get("ntype") == "point":

                rows.append({

                    "point": u.split(":")[-1],

                    "axis": v.split(":")[-1],

                    "improvement": d.get("improvement", 0),

                    "weakness_weight": d.get("weight", 0),

                })

            elif self.G.nodes[v].get("ntype") == "point":

                rows.append({

                    "point": v.split(":")[-1],

                    "axis": u.split(":")[-1],

                    "improvement": d.get("improvement", 0),

                    "weakness_weight": d.get("weight", 0),

                })

        df = pd.DataFrame(rows)

        if df.empty:

            return df

        return df.sort_values("weakness_weight", ascending=False).reset_index(drop=True)

 

</details>

 

<details>

 

<summary></summary>

 

"""可视化 (matplotlib)"""

 

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from pathlib import Path

 

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

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

 

 

class CalibVisualizer:

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

        self.results_dir = Path(results_dir)

        self.results_dir.mkdir(exist_ok=True)

 

    def error_box_before_after(self, df):

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

        data = [df["err_before_mm"].values, df["err_after_mm"].values]

        ax.boxplot(data, labels=["标定前", "标定后"], showmeans=True)

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

        ax.set_title("标定前后定位误差箱线图", fontsize=14, fontweight="bold")

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

        plt.tight_layout()

        plt.savefig(self.results_dir / "error_box_before_after.png",

                     dpi=150, bbox_inches="tight")

        plt.close()

 

    def improvement_bar(self, rank_df):

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

        x = range(len(rank_df))

        colors = ["#E74C3C" if r < 0.3 else "#F39C12" if r < 0.6 else "#27AE60"

                  for r in rank_df["improvement_ratio"]]

        ax.bar(x, rank_df["improvement_ratio"] * 100, color=colors)

        ax.set_xticks(list(x))

        ax.set_xticklabels(rank_df["point_id"], rotation=45, ha="right", fontsize=9)

        ax.set_ylabel("改善幅度 (%)")

        ax.set_title("各点位标定改善幅度", fontsize=14, fontweight="bold")

        ax.axhline(50, ls="--", c="gray", label="50%基准线")

        ax.axhline(80, ls="--", c="green", label="80%优秀线")

        ax.legend()

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

        plt.tight_layout()

        plt.savefig(self.results_dir / "improvement_bar.png",

                     dpi=150, bbox_inches="tight")

        plt.close()

 

    def direction_decomp(self, dir_stat):

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

        x = np.arange(len(dir_stat))

        w = 0.3

        ax.bar(x - w/2, dir_stat["before_mean_mm"], w,

               label="标定前", color="#95A5A6")

        ax.bar(x + w/2, dir_stat["after_mean_mm"], w,

               label="标定后", color="#3498DB")

        ax.set_xticks(x)

        ax.set_xticklabels(dir_stat["axis"], fontsize=12)

        ax.set_ylabel("平均误差 (mm)")

        ax.set_title("XYZ方向误差分解对比", fontsize=14, fontweight="bold")

        ax.legend()

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

        # 标注改善百分比

        for i, r in dir_stat.iterrows():

            ax.text(i + w/2, float(r["after_mean_mm"]) + 0.05,

                    f"{r['improvement_pct']}%", ha="center", fontsize=9,

                    color="green", fontweight="bold")

        plt.tight_layout()

        plt.savefig(self.results_dir / "direction_decomp.png",

                     dpi=150, bbox_inches="tight")

        plt.close()

 

    def anomaly_scatter(self, df):

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

        normal = df[~df["is_anomaly"]]

        anom = df[df["is_anomaly"]]

        ax.scatter(normal["err_before_mm"], normal["improvement_ratio"] * 100,

                   c="#3498DB", label="正常改善", s=40, alpha=0.7)

        ax.scatter(anom["err_before_mm"], anom["improvement_ratio"] * 100,

                   c="#E74C3C", label="改善异常", s=80, edgecolor="k")

        for _, r in anom.iterrows():

            ax.annotate(r["point_id"], (r["err_before_mm"],

                        r["improvement_ratio"] * 100),

                        fontsize=8, color="red")

        ax.set_xlabel("标定前误差 (mm)")

        ax.set_ylabel("改善幅度 (%)")

        ax.set_title("异常点位识别散点图", fontsize=14, fontweight="bold")

        ax.legend()

        ax.grid(alpha=0.3)

        plt.tight_layout()

        plt.savefig(self.results_dir / "anomaly_scatter.png",

                     dpi=150, bbox_inches="tight")

        plt.close()

 

    def graph_plot(self, G):

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

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

        ncolor = []

        for n, d in G.nodes(data=True):

            t = d.get("ntype")

            ncolor.append({"point": "#2ECC71", "axis": "#E74C3C"}.get(t, "#999"))

        sizes = [300 + G.degree(n) * 40 for n in G.nodes()]

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

                               node_size=sizes, ax=ax, alpha=0.9)

        edges = list(G.edges(data=True))

        ws = [d["weight"] for *_, d in edges]

        nx.draw_networkx_edges(G, pos, width=ws, alpha=0.4, ax=ax,

                                edge_color="#E67E22")

        labels = {n: n.split(":")[-1] for n, d in G.nodes(data=True)

                  if d.get("ntype") == "axis"}

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

        ax.set_title("点位-方向改善关系网\n(绿=点位 红=方向 橙线越粗=该方向改善越弱)",

                     fontsize=13, fontweight="bold")

        ax.axis("off")

        plt.tight_layout()

        plt.savefig(self.results_dir / "calib_graph.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 SyntheticCalibGenerator:

    """

    生成 20 个点位的标定前后坐标

    标定前误差 1.5~3.5mm (随机偏移)

    标定后大部分降 70~85%,少量只降 10~20% (薄弱区域)

    """

 

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

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

 

    def generate(self, output_path: str = "calib_data.csv",

                 n_points: int = 20) -> pd.DataFrame:

        records = []

        for i in range(1, n_points + 1):

            pid = f"P{i:02d}"

            # 理论坐标 (工作空间内随机点)

            tx = float(self.rng.uniform(-800, 800))

            ty = float(self.rng.uniform(-600

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

Logo

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

更多推荐