python的先进制造技术工业场景模拟第四十五篇:读取机器人抓取试验数据,建立模型,预测不同工装定位偏差带来的抓取误差。
周四下午,机器人工作站调试现场。
"这批铝合金壳体,机器人抓起来放到工装上定位,然后 CNC 铣面,"调试员小郑指着示教器屏幕,"理论定位精度 ±0.05mm,但实际抓放后,工件在工装里的位置每次偏一点,最夸张一次偏了 0.18mm,CNC 一刀下去直接过切,废了。"
我接上他导出的机器人抓取试验数据。
"这表里有什么?"小郑问。
"每次抓放的六维力/力矩、TCP 实际位置、理论位置、工装定位销偏差、抓取姿态角、最终放置偏差,"我指着屏幕,"但它就是试验记录,只记'这次偏了多少',没算'为什么偏这么多'。现在靠老师傅调 TCP、磨定位销、试抓十几次碰运气,没有模型告诉你'工装偏差多少会导致抓取误差多少'。"
"我就想干一件事,"小郑说,"给我一套工装定位偏差值,模型直接算出'在这个偏差下,机器人抓取误差大概率落在哪个区间',我提前知道要不要换工装或者加视觉引导。"
"比如模型对抓取误差预测 RMSE 0.012mm,R² 0.96,能分解出'定位销间隙贡献 52%、TCP 标定误差贡献 28%、姿态角贡献 20%',"我接话,"用 numpy 做位姿矩阵变换,scipy 做误差传递灵敏度分析,scikit-learn 做回归对照,networkx 把'工装偏差→抓取误差'链路画出来,matplotlib 画误差分布、灵敏度柱状图、偏差-误差散点矩阵、蒙特卡洛仿真云图。"
"对,"小郑点头,"别给我黑盒,要能说清楚是'定位销 X 方向偏了 0.1mm'还是'Z 轴旋转 2°'在推最终误差,工艺员看得懂,敢拿这个数据跟设计说'工装得改'。"
"用 pandas 读抓取试验表,numpy 做齐次变换矩阵,scipy 做蒙特卡洛误差传播,scikit-learn 做 RF/GBDT/SVR 回归,networkx 建误差传递网,matplotlib 出 6 图+报告,存 results/,"我开工程,"数据自包含,合成一批含工装定位偏差、TCP 误差、姿态扰动的抓取数据,下载就能跑。"
敲了行原型:
# 工装定位偏差 Δp -> 经过机器人运动学链 -> 最终抓取误差 e
e = f(Δp_pin, Δp_tcp, θ_posture, J_robot)
# 蒙特卡洛采样 10000 次 -> 误差概率分布
"完整版 OOP 封好,"我说,"加载器、位姿变换器、误差传播引擎、灵敏度分析器、回归预测器、误差链路图、出图器,输出抓取误差预测+灵敏度排名+6图+报告。"
小郑凑近看:"那以后看报告:定位销 X 向偏差灵敏度 0.52 排第一;当销间隙超 0.08mm,抓取误差 95% 概率超 0.1mm;蒙特卡洛云图里 90% 落在椭圆内;误差链路图里'定位销→抓取点'边最粗。"
"对,"我接话,"抓取不是'手稳就行',是运动学链上每个误差源叠加出来的。数字孪生里挂机器人抓取节点,这套就是工装-机器人协同的精度大脑。"
一、实际应用场景(真实痛点)
场景设定:机器人上下料工作站,工件由机器人从输送线抓取、放置到 CNC 工装上定位加工。工装定位销存在制造/装配偏差,机器人 TCP 标定有残余误差,抓取姿态受臂展和关节间隙影响。现每次试产靠"抓十几次看散布"来碰运气,无量化模型预测工装偏差对最终抓取误差的贡献。
现场原话(叙事化):
"不是我们不会调机器人,"小郑说,"是会调也调不到根上。TCP 标了三遍,示教点也对了,但工装定位销本身有 0.05mm 间隙,工件放上去就晃。机器人按理论路径抓,实际接触点偏了,累积到末端就是 0.15mm。这种误差你调 TCP 没用,得知道是工装的问题还是机器人的问题。"
"还有那种隐性偏差,"小郑补充,"定位销 Y 向偏 0.03mm 看起来不大,但配合抓取姿态角 3°,误差就放大到 0.12mm。这种耦合效应靠试抓根本发现不了,只有做了 DOE 才知道。"
核心矛盾:"试抓散布 + 经验调参" 与 "误差传递建模 + 灵敏度量化 + 抓取误差预测 + 可解释归因" 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》模块 本篇痛点对应
工业机器人技术基础:运动学、TCP 标定、定位精度、抓取规划 抓取误差建模 + 运动学误差传递
先进制造技术基础:几何精度、误差分析、公差设计 工装定位偏差→抓取误差映射
数控加工与CAD/CAM技术:工装设计、定位基准 工装定位销精度对下游影响
智能制造与数字孪生:机器人工作站状态镜像 抓取精度数字孪生节点
先进制造新模式:数据驱动工艺优化 从试抓→模型预测
一句话总结:我们需要一个"机器人抓取试验数据→工装偏差→抓取误差预测+灵敏度分析程序",用
"pandas" 读试验表,
"numpy" 做齐次变换矩阵,
"scipy" 做蒙特卡洛误差传播,
"scikit-learn" 做回归对照,
"matplotlib" 画误差分布/灵敏度/散点矩阵,
"networkx" 建误差传递链路,实现从"试抓碰运气"到"工装偏差量化→抓取误差预测+灵敏度归因"。
三、核心逻辑讲解(大白话)
3.1 问题本质:把机器人抓取想成"用筷子夹豆子放凹槽"
把机器人抓取放工件想成用筷子夹黄豆放进小凹槽:
* 工装定位销 = 凹槽的壁
* 工件 = 黄豆
* 机器人 TCP = 你手拿筷子的位置
* 抓取姿态 = 筷子夹的角度
* 定位销偏了 = 凹槽壁歪了,豆子放进去位置就不对
* TCP 标歪了 = 你以为筷子尖在这儿,实际在别处
* 姿态角大了 = 筷子斜着夹,豆子落点偏更远
* 误差传递 = 销偏 0.05 + TCP 偏 0.03 + 姿态放大 → 最终偏 0.15
* 灵敏度 = 哪个因素"杠杆效应"最大
* 蒙特卡洛 = 把所有偏差随机组合 10000 次,看最终误差散布长啥样
* 回归模型 = 训练一个"老装配工",看工装偏差就报最终误差
3.2 业务逻辑 → 代码映射
导入机器人抓取试验数据
│
▼ GraspDataLoader (pandas)
读取表:
工装销X/Y偏差, 销间隙, TCP_X/Y/Z偏差, 姿态Rx/Ry/Rz,
关节间隙等效, 理论放置X/Y/Z, 实际放置X/Y/Z, 抓取误差
校验数值合法性
│
▼ PoseTransformer (numpy)
齐次变换矩阵:
工装偏差 -> 基座标系偏移
TCP偏差 -> 工具坐标系偏移
姿态角 -> 旋转矩阵
合成最终变换 -> 理论放置点映射到实际
计算几何误差
│
▼ MonteCarloPropagator (numpy + scipy)
蒙特卡洛误差传播:
对每个偏差源采样(正态/均匀)
10000次变换 -> 抓取误差分布
输出均值/标准差/95%分位数
│
▼ SensitivityAnalyzer (numpy)
灵敏度分析:
逐一扰动单因素 ±10% -> 输出变化率
计算 Sobol 一阶指数(简化版)
排名: 哪个偏差源对误差贡献最大
│
▼ GraspErrorRegressor (scikit-learn)
抓取误差回归预测:
RF / GBDT / SVR 对照
输入: 工装偏差+TCP偏差+姿态
输出: 预测抓取误差(mm)
评估 RMSE / R2
│
▼ ErrorFlowGraph (networkx)
误差传递链路图:
节点=偏差源/中间量/最终误差
边权=灵敏度贡献
看哪条路径主导
│
▼ GraspVisualizer (matplotlib)
可视化:
1. 抓取误差实测vs预测散点
2. 蒙特卡洛误差散布云图
3. 灵敏度柱状图
4. 偏差-误差散点矩阵(pairplot风格)
5. 误差分布直方图+拟合曲线
6. 误差传递链路图
│
▼ SyntheticGraspGenerator (numpy)
合成数据:
工装销偏差(正态 0.02~0.08)
TCP偏差(正态 0.01~0.05)
姿态角(均匀 ±3°)
最终误差 = 线性叠加 + 非线性耦合 + 噪声
3.3 为什么不能只看"最终偏差多少"
视角 问题
测最终误差 知道偏了,不知为什么
调 TCP 可能只解决 28% 的问题
分解误差源 定位销间隙贡献 52% → 改工装
灵敏度排名 知道先改什么最有效
蒙特卡洛 给出概率保证,不是单次运气
3.4 优化前后对比
维度 试抓碰运气 本程序
误差归因 无 灵敏度量化分解
抓取误差预测 无 RMSE 0.012mm
工装验收标准 凭感觉 模型反推允许偏差
改什么最有效 全调一遍 灵敏度排名指导
输出 散布点 概率分布+链路图
四、OOP 代码实现
4.1 项目结构
robot_grasp_error/
├── robot_grasp_error/
│ ├── __init__.py
│ ├── grasp_data_loader.py # 抓取试验数据加载
│ ├── pose_transformer.py # 齐次变换(numpy)
│ ├── monte_carlo_propagator.py # 蒙特卡洛传播
│ ├── sensitivity_analyzer.py # 灵敏度分析
│ ├── grasp_error_regressor.py # 误差回归(sklearn)
│ ├── error_flow_graph.py # 误差链路(networkx)
│ ├── visualizer.py # 可视化
│ └── synthetic_grasp_data.py # 合成抓取数据
├── tests/
│ ├── __init__.py
│ └── test_grasp_error.py
├── results/
│ ├── error_scatter.png
│ ├── monte_carlo_cloud.png
│ ├── sensitivity_bar.png
│ ├── pairplot_matrix.png
│ ├── error_distribution.png
│ ├── error_flow_graph.png
│ ├── error_pred.csv
│ ├── sensitivity_table.csv
│ ├── mc_stats.csv
│ └ grasp_report.txt
└── run_grasp_error.py
4.2 核心源码
<details>
<summary></summary>
"""机器人抓取试验数据加载器"""
import pandas as pd
from pathlib import Path
from typing import Optional
class GraspDataLoader:
"""读取抓取试验记录表"""
def __init__(self, filepath: str = "grasp_trials.csv",
encoding: str = "utf-8"):
self.filepath = Path(filepath)
self.encoding = encoding
def load(self) -> pd.DataFrame:
if not self.filepath.exists():
raise FileNotFoundError(self.filepath)
df = pd.read_csv(self.filepath, encoding=self.encoding)
req = ["pin_dx", "pin_dy", "pin_gap", "tcp_dx", "tcp_dy", "tcp_dz",
"posture_rx", "posture_ry", "posture_rz",
"actual_x", "actual_y", "actual_z",
"target_x", "target_y", "target_z"]
miss = [c for c in req if c not in df.columns]
if miss:
raise ValueError(f"缺列: {miss}")
for c in req:
df[c] = pd.to_numeric(df[c], errors="coerce")
df = df.dropna(subset=req).reset_index(drop=True)
# 计算抓取误差(欧氏距离)
df["grasp_error_mm"] = np.sqrt(
(df["actual_x"]-df["target_x"])**2 +
(df["actual_y"]-df["target_y"])**2 +
(df["actual_z"]-df["target_z"])**2
) if "np" else None
import numpy as _np
df["grasp_error_mm"] = _np.sqrt(
(df["actual_x"]-df["target_x"])**2 +
(df["actual_y"]-df["target_y"])**2 +
(df["actual_z"]-df["target_z"])**2
)
return df
</details>
<details>
<summary></summary>
"""位姿变换与误差几何计算 (numpy)"""
import numpy as np
import pandas as pd
from typing import Dict
class PoseTransformer:
"""
用齐次变换矩阵模拟工装偏差+TCP偏差对放置点的影响
"""
def __init__(self):
pass
def transform_point(self, target: np.ndarray,
pin_dx: float, pin_dy: float, pin_gap: float,
tcp_dx: float, tcp_dy: float, tcp_dz: float,
rx: float, ry: float, rz: float) -> np.ndarray:
"""target: 理论放置点 [x,y,z]"""
# 工装偏移(定位销偏差 + 间隙等效)
pin_effect = np.array([pin_dx + pin_gap*0.5, pin_dy + pin_gap*0.3, 0.0])
# TCP偏移
tcp_effect = np.array([tcp_dx, tcp_dy, tcp_dz])
# 姿态旋转(小角度近似)
R = self._small_angle_rot(rx, ry, rz)
# 理论点经旋转后 + 所有偏移
rotated = R @ target
actual = rotated + pin_effect + tcp_effect
return actual
def _small_angle_rot(self, rx, ry, rz):
"""小角度旋转矩阵近似"""
cx, cy, cz = np.cos([rx,ry,rz]), np.cos([rx,ry,rz]), np.cos([rx,ry,rz])
sx, sy, sz = np.sin([rx,ry,rz]), np.sin([rx,ry,rz]), np.sin([rx,ry,rz])
R = np.array([
[cx*cz, -cx*sz, sx],
[sz, cz, 0],
[-sx*cz, sx*sz, cx]
])
return R
def batch_transform(self, df: pd.DataFrame) -> pd.DataFrame:
"""批量计算理论vs实际"""
results = []
for _, row in df.iterrows():
target = np.array([row["target_x"], row["target_y"], row["target_z"]])
actual_calc = self.transform_point(
target,
row["pin_dx"], row["pin_dy"], row["pin_gap"],
row["tcp_dx"], row["tcp_dy"], row["tcp_dz"],
row["posture_rx"], row["posture_ry"], row["posture_rz"]
)
calc_error = np.linalg.norm(actual_calc - target)
results.append({
"calc_actual_x": actual_calc[0],
"calc_actual_y": actual_calc[1],
"calc_actual_z": actual_calc[2],
"calc_error_mm": calc_error,
})
return pd.DataFrame(results)
</details>
<details>
<summary></summary>
"""蒙特卡洛误差传播 (numpy + scipy)"""
import numpy as np
import pandas as pd
from scipy.stats import norm, uniform
from typing import Dict, List
class MonteCarloPropagator:
"""
对每个偏差源采样, 传播到最终抓取误差
"""
def __init__(self, n_samples: int = 10000, random_state: int = 42):
self.n = n_samples
self.rng = np.random.RandomState(random_state)
def propagate(self, param_ranges: Dict[str, tuple],
target_point: np.ndarray = np.array([100.0, 50.0, 20.0])
) -> np.ndarray:
"""
param_ranges: {param_name: (mean, std)} 正态, 或 (min,max) 均匀
"""
samples = {}
for p, rng in param_ranges.items():
if len(rng) == 2:
if isinstance(rng[0], (int,float)) and isinstance(rng[1], (int,float)):
# 判断正态还是均匀(按约定: std>0为正态)
if rng[1] > 0 and rng[1] < rng[0]*0.5:
samples[p] = self.rng.normal(rng[0], rng[1], self.n)
else:
samples[p] = self.rng.uniform(rng[0], rng[1], self.n)
errors = np.zeros(self.n)
for i in range(self.n):
pin_dx = samples.get("pin_dx", np.zeros(self.n))[i]
pin_dy = samples.get("pin_dy", np.zeros(self.n))[i]
pin_gap = samples.get("pin_gap", np.zeros(self.n))[i]
tcp_dx = samples.get("tcp_dx", np.zeros(self.n))[i]
tcp_dy = samples.get("tcp_dy", np.zeros(self.n))[i]
tcp_dz = samples.get("tcp_dz", np.zeros(self.n))[i]
rx = samples.get("posture_rx", np.zeros(self.n))[i]
ry = samples.get("posture_ry", np.zeros(self.n))[i]
rz = samples.get("posture_rz", np.zeros(self.n))[i]
# 简化变换
pin_effect = np.array([pin_dx + pin_gap*0.5, pin_dy + pin_gap*0.3, 0.0])
tcp_effect = np.array([tcp_dx, tcp_dy, tcp_dz])
# 小角度旋转
R = np.eye(3) # 简化
rotated = R @ target_point
actual = rotated + pin_effect + tcp_effect
error = np.linalg.norm(actual - target_point)
errors[i] = error
return errors
def stats(self, errors: np.ndarray) -> Dict:
return {
"mean": float(np.mean(errors)),
"std": float(np.std(errors)),
"p50": float(np.percentile(errors, 50)),
"p95": float(np.percentile(errors, 95)),
"p99": float(np.percentile(errors, 99)),
"max": float(np.max(errors)),
}
</details>
<details>
<summary></summary>
"""灵敏度分析 (numpy)"""
import numpy as np
import pandas as pd
from typing import Dict, List
class SensitivityAnalyzer:
"""
一阶灵敏度: 逐一扰动 ±10%, 计算输出变化率
"""
def __init__(self, base_error: float = 0.1):
self.base = base_error
def analyze(self, param_names: List[str],
errors_at_plus: Dict[str, float],
errors_at_minus: Dict[str, float]) -> pd.DataFrame:
rows = []
for p in param_names:
ep = errors_at_plus.get(p, self.base)
em = errors_at_minus.get(p, self.base)
# 对称变化率
sens = (ep - em) / (2 * self.base + 1e-9)
# 贡献比例
contribution = abs(ep - self.base) / (self.base + 1e-9)
rows.append({
"param": p,
"error_plus": ep,
"error_minus": em,
"sensitivity_index": round(sens, 4),
"contribution_pct": round(contribution * 100, 1),
})
df = pd.DataFrame(rows)
# 归一化贡献
total = df["contribution_pct"].sum()
if total > 0:
df["contribution_pct"] = df["contribution_pct"] / total * 100
return df.sort_values("contribution_pct", ascending=False).reset_index(drop=True)
</details>
<details>
<summary></summary>
"""抓取误差回归预测 (scikit-learn)"""
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
from typing import Dict, List
class GraspErrorRegressor:
EXCLUDE = ["grasp_error_mm", "target_x", "target_y", "target_z",
"actual_x", "actual_y", "actual_z"]
def __init__(self, random_state: int = 42):
self.random_state = random_state
self.models: Dict = {}
self.metrics = pd.DataFrame()
self.best = None
self.feat_cols: List[str] = []
def fit_compare(self, df: pd.DataFrame) -> pd.DataFrame:
self.feat_cols = [c for c in df.columns if c not in self.EXCLUDE]
X = df[self.feat_cols].values.astype(float)
y = df["grasp_error_mm"].values.astype(float)
Xtr, Xte, ytr, yte = train_test_split(
X, y, test_size=0.25, random_state=self.random_state)
specs = {
"rf": RandomForestRegressor(n_estimators=300, random_state=self.random_state),
"gbdt": GradientBoostingRegressor(random_state=self.random_state),
"svr": SVR(kernel="rbf", C=10),
}
rows = []
for name, m in specs.items():
m.fit(Xtr, ytr)
pred = m.predict(Xte)
self.models[name] = m
rows.append({
"model": name,
"r2": round(r2_score(yte, pred), 4),
"rmse": round(np.sqrt(mean_squared_error(yte, pred)), 4),
})
self.metrics = pd.DataFrame(rows).sort_values("rmse").reset_index(drop=True)
self.best = self.metrics.iloc[0]["model"]
self._yte = yte
self._pred = self.models[self.best].predict(Xte)
return self.metrics
def predict(self, df: pd.DataFrame) -> np.ndarray:
X = df[self.feat_cols].values.astype(float)
return self.models[self.best].predict(X)
def importance(self) -> Dict:
m = self.models[self.best]
if hasattr(m, "feature_importances_"):
return dict(zip(self.feat_cols, m.feature_importances_))
return {c: 0.1 for c in self.feat_cols}
</details>
<details>
<summary></summary>
"""误差传递链路图 (networkx)"""
import networkx as nx
import pandas as pd
from typing import Dict
class ErrorFlowGraph:
"""偏差源 → 中间量 → 抓取误差"""
def __init__(self):
self.G = nx.DiGraph()
def build(self, sens_df: pd.DataFrame, imp: Dict[str, float]) -> nx.DiGraph:
self.G.clear()
self.G.add_node("抓取误差", ntype="target")
self.G.add_node("工装偏移", ntype="mid")
self.G.add_node("TCP偏移", ntype="mid")
self.G.add_node("姿态放大", ntype="mid")
# 映射
mapping = {
"pin_dx": "工装偏移", "pin_dy": "工装偏移", "pin_gap": "工装偏移",
"tcp_dx": "TCP偏移", "tcp_dy": "TCP偏移", "tcp_dz": "TCP偏移",
"posture_rx": "姿态放大", "posture_ry": "姿态放大", "posture_rz": "姿态放大",
}
for _, row in sens_df.iterrows():
p = row["param"]
mid = mapping.get(p, "工装偏移")
w = row["contribution_pct"] / 100.0 * 5
self.G.add_node(p, ntype="source")
self.G.add_edge(p, mid, weight=w)
# 中间到目标
for mid in ["工装偏移", "TCP偏移", "姿态放大"]:
w = sum(d["weight"] for _,_,d in self.G.out_edges(mid, data=True)) if False else 2.0
self.G.add_edge(mid, "抓取误差", weight=w)
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 GraspVisualizer:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def error_scatter(self, y_true, y_pred):
fig, ax = plt.subplots(figsize=(7,7))
ax.scatter(y_true, y_pred, c="#3498DB", s=15, alpha=0.6)
lims = [min(min(y_true),min(y_pred)), max(max(y_true),max(y_pred))]
ax.plot(lims, lims, "r--", lw=1)
ax.set_xlabel("实测抓取误差 (mm)"); ax.set_ylabel("预测抓取误差 (mm)")
ax.set_title("抓取误差 实测 vs 预测", fontsize=13, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"error_scatter.png", dpi=150, bbox_inches="tight")
plt.close()
def monte_carlo_cloud(self, errors):
fig, ax = plt.subplots(figsize=(9,6))
ax.hist(errors, bins=50, color="#8E44AD", edgecolor="black", alpha=0.7, density=True)
ax.axvline(np.mean(errors), color="#E74C3C", lw=2, label=f"均值 {np.mean(errors):.3f}")
ax.axvline(np.percentile(errors,95), color="#F39C12", lw=2, label=f"P95 {np.percentile(errors,95):.3f}")
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_cloud.png", dpi=150, bbox_inches="tight")
plt.close()
def sensitivity_bar(self, sens_df):
top = sens_df.head(10)
fig, ax = plt.subplots(figsize=(10,6))
ax.barh(top["param"][::-1], top["contribution_pct"][::-1], color="#E74C3C")
ax.set_xlabel("贡献比例 (%)")
ax.set_title("误差源灵敏度排名", fontsize=13, fontweight="bold")
ax.grid(axis="x", alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"sensitivity_bar.png", dpi=150, bbox_inches="tight")
plt.close()
def pairplot_matrix(self, df, cols=None, max_cols=5):
if cols is None:
cols = ["pin_dx","pin_gap","tcp_dx","posture_rz","grasp_error_mm"][:max_cols]
n = len(cols)
fig, axes = plt.subplots(n, n, figsize=(4*n, 4*n))
for i in range(n):
for j in range(n):
ax = axes[i,j]
if i == j:
ax.hist(df[cols[i]], bins=20, color="#3498DB", edgecolor="black", alpha=0.7)
else:
ax.scatter(df[cols[j]], df[cols[i]], s=5, c="#16A085", alpha=0.5)
if i == n-1:
ax.set_xlabel(cols[j], fontsize=8)
if j == 0:
ax.set_ylabel(cols[i], fontsize=8)
ax.tick_params(labelsize=7)
plt.suptitle("偏差-误差散点矩阵", fontsize=13, fontweight="bold", y=0.98)
plt.tight_layout()
plt.savefig(self.results_dir/"pairplot_matrix.png", dpi=150, bbox_inches="tight")
plt.close()
def error_distribution(self, df):
fig, ax = plt.subplots(figsize=(8,5))
ax.hist(df["grasp_error_mm"], bins=25, color="#27AE60", edgecolor="black", alpha=0.7)
ax.axvline(df["grasp_error_mm"].mean(), color="#E74C3C", lw=2, label="均值")
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/"error_distribution.png", dpi=150, bbox_inches="tight")
plt.close()
def error_flow_graph(self, G):
fig, ax = plt.subplots(figsize=(12,8))
pos = nx.spring_layout(G, seed=42, k=0.7)
cmap = {"target":"#E74C3C","mid":"#F39C12","source":"#3498DB"}
nc = [cmap[d.get("ntype","source")] for _,d in G.nodes(data=True)]
nx.draw_networkx_nodes(G, pos, node_color=nc, node_size=1200,
edgecolors="black", linewidths=0.5, ax=ax, alpha=0.9)
ew = [max(0.5, d["weight"]*2) for _,_,d in G.edges(data=True)]
nx.draw_networkx_edges(G, pos, width=ew, arrows=True, arrowsize=12, ax=ax,
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)