周四上午,机器人上下料工位。
“这批电机端盖,来料是料仓推出来的,位置每次偏一点,”机器人操作工小周蹲在安全栏外,“视觉给个中心,机械臂就按那个点抓,偏个0.5mm还好,偏到1.2mm,夹爪就咬到法兰边,装到机床主轴上同轴度直接飘。”
我接上导出的来料定位日志、视觉检测坐标、机器人实际抓取位姿、装夹后同轴度检测值。
“这里面有啥?”我问。
“来料X/Y偏置、转角θ、视觉识别中心、机器人TCP执行值、装后同轴度都有,”小周说,“可系统就是‘视觉给啥抓啥’,不模拟‘来料偏差分布→抓取点偏移→自适应补偿后残差→同轴度劣化’。想验证补偿算法管不管用,得真跑几百件看报废率。”
“最亏的是中段,”小周补一句,“偏差在±0.8mm以内看着都装上了,可同轴度从0.02mm爬到0.05mm,检具按批抽,正好没抽到边界件。到1.2mm那几件,直接划伤主轴锥孔。”
“我就想干一件事,”小周说,“给来料偏差分布+视觉测量+补偿模型,模拟抓取点偏移,算补偿前后的残差,反推装后同轴度,标出补偿后还剩多少残差、边界件能不能兜住,像个小抓取自适应仿真器,不用先上真机磨夹爪。”
“机器人不是看TCP走没走准,”我接话,“是看‘来料偏多少、视觉量得准不准、补偿量怎么加、残差剩多少’。用 numpy 做坐标变换+补偿递推,pandas 管来料批次,scipy 做误差分布拟合+插值,matplotlib 画偏差散点+补偿前后对比+同轴度曲线,networkx 建‘来料-视觉-补偿-同轴度’关联,sklearn 做合格分级。”
“对,”小周点头,“要能说清‘来料σ=0.45mm,视觉噪声0.08mm,开补偿前残差1.2mm→同轴度0.06mm超差;开补偿后残差0.15mm→同轴度0.018mm合格,边界件也兜住;主因是来料Y向偏移+转角耦合’。”
“OOP 封好,”我开工程,“来料加载器、视觉测量模型、坐标补偿器、抓取残差计算、同轴度映射、合格分类器、可视化器,合成多批次来料,下载就能跑。”
敲了行原型:
# 目标: 来料偏差 → 视觉测量 → 位置自适应补偿 → 残差 → 同轴度
# 方法: 刚体坐标变换 + 补偿闭环 + RF合格分级 + 关联图
小周凑近看:“那以后看报告:来料偏差散点,补偿前后残差对比,同轴度随偏差曲线,关联网络,合格预测散点。新料盘上机前先跑,红圈就是兜不住的件。”
“对,”我接话,“机器人仿真不是‘画个轨迹’,是‘提前看见哪件偏得补偿也救不回’。数字孪生里挂这个抓取补偿看板,就是小周的‘防划伤镜’。”
一、实际应用场景(真实痛点)
场景设定:六轴工业机器人做机加产线上下料,抓取电机端盖类盘类件,来料由振动料仓+输送线供给,存在 X/Y 平移偏差与绕 Z 转角偏差。视觉系统给出识别中心,机器人按识别点规划夹爪 TCP。未开自适应补偿时,偏差直接带入装夹,导致主轴同轴度超差,边界件划伤锥孔。
现场原话(叙事化):
“不是机器人精度不行,”小周说,“是料每次站的位置不一样。视觉说中心在(0,0),实际可能偏了0.6mm还转了2°,夹爪按(0,0)抓,就咬在法兰倒角上,装上去同轴度就废了。”
“最亏的是补偿逻辑,”小周说,“以前写死TCP偏移表,换料盘就废。想验证‘在线算偏差再补’这个算法,得先磨坏几十个夹爪、划几根主轴才知道行不行。”
核心矛盾:“视觉给点即抓+固定补偿表” 与 “来料偏差建模→视觉测量→自适应补偿→残差量化→同轴度预测+关联图” 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》课程模块 本篇痛点对应
工业机器人技术基础:TCP、坐标变换、手眼标定、抓取定位、位姿误差 来料偏差+自适应补偿+残差
数控加工与CAD/CAM技术:装夹同轴度、定位基准 同轴度映射
先进制造技术基础:几何精度、误差传递、定位误差 误差链量化
智能制造与数字孪生:机器人状态+来料状态数字映射、补偿看板 抓取补偿挂孪生
先进制造新模式:自适应工艺、数据驱动补偿知识库 补偿模型复用
一句话总结:我们需要一个“来料位姿偏差→视觉测量→自适应位置补偿→抓取残差→装后同轴度预测→合格分级+关联图”程序,实现从“写死补偿表”到“在线自适应补偿仿真验证”的闭环。
三、核心逻辑讲解(大白话)
3.1 问题本质:把抓取想成“抓一张歪放的硬币”
把端盖想成桌上放的一堆硬币,每次放的位置都偏一点、还转个角度:
* 来料偏差 = 硬币中心偏了 (Δx, Δy),还转了 θ 角
* 视觉测量 = 你眯眼估中心,估得准但也有误差(视觉噪声)
* 不补偿 = 按你估的中心抓,硬币偏多少抓偏多少
* 自适应补偿 = 先算出“偏了多少+转了多少”,让夹爪跟着偏、跟着转,对准真实中心
* 残差 = 补偿后还差的那一点点(视觉误差+算法截断+夹爪间隙)
* 同轴度 = 装到主轴上后,中心轴和主轴轴的偏移量,残差越大它越飘
* 写死表 = 按上批料记个偏移量,换批就错
* 仿真验证 = 先造一堆偏差数据,算补偿前后残差,看边界件兜不兜得住
3.2 业务逻辑 → 代码映射
输入来料位姿批次+视觉参数
│
▼ WorkpieceLoader (pandas)
读取表:
件号, 真实Δx, 真实Δy, 真实θ, 料盘批次, 直径
│
▼ VisionModel (numpy + scipy)
视觉测量:
测值 = 真值 + 高斯噪声(σ_vis)
θ测量带量化误差
输出 (xv, yv, θv)
│
▼ PoseCompensator (numpy)
自适应补偿:
将测量位姿反算到机器人基坐标系
TCP_target = TCP_nominal + R(θv)·[xv,yv] + 夹爪随转补偿
补偿后理论抓取点对齐真实中心
│
▼ GraspResidual (numpy)
残差计算:
残差 = |真实中心 - 补偿后TCP| (含视觉残差+截断)
分补偿前/补偿后两路输出
│
▼ CoaxialMapper (scipy)
同轴度映射:
同轴度 = k * 残差 + 转角耦合项 + 装夹间隙
用样条拟合残差→同轴度
│
▼ QualifyClassifier (sklearn)
合格分级:
特征: 残差, θ, 直径, 补偿开关
标签: 合格(≤0.03) / 临界 / 超差(>0.05)
RF三分类 + 5折宏F1
│
▼ RobotGraspVisualizer (matplotlib + networkx)
可视化:
1. 来料偏差散点(补偿前红/补偿后绿)
2. 补偿前后残差分布对比直方图
3. 同轴度随来料偏移曲线
4. 来料-视觉-补偿-同轴度关联网络
5. 合格预测vs实际散点
6. 单件抓取俯视示意(箭头=补偿向量)
│
▼ SyntheticWorkpieces (numpy)
合成数据:
多批次, 机制: Y向偏移> X向, θ与偏移耦合, 视觉σ可配
3.3 为什么不能“视觉给点即抓”
视角 问题
看机器人重复定位 ±0.02mm很好,但来料偏1mm
写死补偿表 换料盘失效
只看视觉中心 漏掉转角θ耦合
自适应补偿 每件在线算偏移+转角
残差双路对比 补偿前后量化差异
同轴度反推 直接关联装夹质量
RF分级 边界件提前标红
3.4 分析前后对比
维度 传统方式 本程序
补偿方式 固定偏移表 每件自适应位姿补偿
残差可见性 装后检具才知 仿真前置算残差
边界件 装完划主轴才发现 提前标红
主因分析 凭手感调 Y向+θ耦合量化
知识沉淀 老师傅经验 补偿模型知识库
四、OOP 代码实现
4.1 项目结构
robot_grasp_comp/
├── robot_grasp_comp/
│ ├── __init__.py
│ ├── workpiece_loader.py # 来料加载
│ ├── vision_model.py # 视觉测量(numpy+scipy)
│ ├── pose_compensator.py # 位姿自适应补偿(numpy)
│ ├── grasp_residual.py # 抓取残差
│ ├── coaxial_mapper.py # 同轴度映射(scipy)
│ ├── qualify_classifier.py # 合格分级(sklearn)
│ ├── robot_grasp_visualizer.py # 可视化
│ └── synthetic_workpieces.py # 合成来料
├── tests/
│ ├── __init__.py
│ └── test_grasp.py
├── results/
│ ├── deviation_scatter.png
│ ├── residual_compare_hist.png
│ ├── coaxial_vs_offset_curve.png
│ ├── grasp_chain_network.png
│ ├── qualify_pred_scatter.png
│ ├── single_grasp_arrow.png
│ ├── grasp_detail.csv
│ └── grasp_report.txt
└── run_grasp.py
4.2 核心源码
<details>
<summary></summary>
"""来料位姿加载器。"""
import pandas as pd
from pathlib import Path
class WorkpieceLoader:
"""加载来料真实位姿偏差。"""
def __init__(self, filepath: str = "workpieces.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 = ["pid", "true_dx", "true_dy", "true_theta",
"batch", "diameter"]
miss = [c for c in req if c not in df.columns]
if miss:
raise ValueError(f"缺列: {miss}")
for c in req[1:]:
df[c] = pd.to_numeric(df[c], errors="coerce")
return df.dropna(subset=req).reset_index(drop=True)
def summary(self, df: pd.DataFrame) -> str:
s = f"件数: {len(df)}\n"
s += f"X偏差σ={df['true_dx'].std():.3f}mm, Y偏差σ={df['true_dy'].std():.3f}mm\n"
s += f"转角θ范围: {df['true_theta'].min():.2f}~{df['true_theta'].max():.2f}rad\n"
s += f"直径: {df['diameter'].iloc[0]}mm, 批次数: {df['batch'].nunique()}"
return s.rstrip()
</details>
<details>
<summary></summary>
"""视觉测量模型 (numpy + scipy)。"""
import numpy as np
from dataclasses import dataclass
from scipy.stats import norm
@dataclass
class VisionResult:
xv: np.ndarray
yv: np.ndarray
thetav: np.ndarray
class VisionModel:
"""
视觉测量 = 真值 + 高斯噪声
x/y: σ_vis
θ: 量化误差+噪声
"""
def __init__(self, sigma_vis: float = 0.08,
theta_noise: float = 0.01,
rng: np.random.RandomState = None):
self.sigma_vis = sigma_vis
self.theta_noise = theta_noise
self.rng = rng or np.random.RandomState(42)
def measure(self, dx, dy, theta) -> VisionResult:
xv = dx + self.rng.normal(0, self.sigma_vis, len(dx))
yv = dy + self.rng.normal(0, self.sigma_vis, len(dy))
thetav = theta + self.rng.normal(0, self.theta_noise, len(theta))
# 模拟像素量化截断到0.005rad
thetav = np.round(thetav, 3)
return VisionResult(xv, yv, thetav)
</details>
<details>
<summary></summary>
"""位姿自适应补偿器 (numpy)。"""
import numpy as np
from dataclasses import dataclass
@dataclass
class CompResult:
tcp_before: np.ndarray # (N,2) 不补偿TCP
tcp_after: np.ndarray # (N,2) 补偿后TCP
comp_vec: np.ndarray # (N,2) 补偿向量
class PoseCompensator:
"""
基坐标系下:
不补偿: TCP = 标称中心 + 视觉测量值(直接信视觉)
自适应补偿:
TCP = 标称中心 + R(θv)·[xv,yv] (随转对齐)
再叠加夹爪中心对准修正
"""
def __init__(self, nominal: np.ndarray = None):
self.nominal = nominal if nominal is not None else np.zeros(2)
def compensate(self, xv, yv, thetav,
enable: bool = True) -> CompResult:
n = len(xv)
vis = np.column_stack([xv, yv])
if not enable:
tcp_before = self.nominal + vis
return CompResult(tcp_before, tcp_before,
np.zeros_like(vis))
# 随转补偿: 把测量偏移旋转到基坐标
out = np.zeros((n, 2))
for i in range(n):
c, s = np.cos(thetav[i]), np.sin(thetav[i])
R = np.array([[c, -s], [s, c]])
out[i] = self.nominal + R @ np.array([xv[i], yv[i]])
tcp_after = out
comp_vec = tcp_after - (self.nominal + vis)
return CompResult(tcp_before=self.nominal+vis,
tcp_after=tcp_after, comp_vec=comp_vec)
</details>
<details>
<summary></summary>
"""抓取残差计算 (numpy)。"""
import numpy as np
from dataclasses import dataclass
@dataclass
class ResidualResult:
res_before: np.ndarray
res_after: np.ndarray
class GraspResidual:
"""
真实中心 = nominal + [true_dx, true_dy] (已含转角真实中心)
残差 = |真实中心 - TCP|
补偿后残差主要来自视觉噪声+量化截断
"""
def __init__(self, nominal: np.ndarray = None):
self.nominal = nominal if nominal is not None else np.zeros(2)
def compute(self, true_dx, true_dy, comp: CompResult) -> ResidualResult:
true_center = self.nominal + np.column_stack([true_dx, true_dy])
res_before = np.linalg.norm(true_center - comp.tcp_before, axis=1)
res_after = np.linalg.norm(true_center - comp.tcp_after, axis=1)
return ResidualResult(res_before, res_after)
</details>
<details>
<summary></summary>
"""同轴度映射 (scipy)。"""
import numpy as np
from dataclasses import dataclass
from scipy.interpolate import UnivariateSpline
@dataclass
class CoaxialResult:
coaxial: np.ndarray
class CoaxialMapper:
"""
同轴度 = k*残差 + θ耦合项 + 装夹间隙
教学级线性+耦合模型, 可用样条标定
"""
def __init__(self, k: float = 0.045, gap: float = 0.005):
self.k = k
self.gap = gap
def map(self, residual: np.ndarray,
theta: np.ndarray) -> CoaxialResult:
# θ耦合: 转角越大, 残差对同轴度放大越明显
coupling = 1.0 + 2.0 * np.abs(theta)
coaxial = self.k * residual * coupling + self.gap
return CoaxialResult(coaxial)
def fit_spline(self, residual, coaxial):
s = UnivariateSpline(residual, coaxial, k=3, s=len(residual)*1e-4)
return s
</details>
<details>
<summary></summary>
"""合格分级 (sklearn)。"""
import numpy as np
import pandas as pd
from typing import Dict
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, KFold
class QualifyClassifier:
"""合格/临界/超差 三分类。"""
def __init__(self, random_state: int = 42):
self.model_ = None
self.feat = ["residual", "theta_abs", "diameter", "comp_on"]
@staticmethod
def _label(c: float) -> str:
if c <= 0.03:
return "合格"
if c <= 0.05:
return "临界"
return "超差"
def fit(self, df: pd.DataFrame, coaxial: np.ndarray):
y = np.array([self._label(c) for c in coaxial])
self.model_ = RandomForestClassifier(
n_estimators=300, max_depth=6, min_samples_leaf=2,
random_state=42, n_jobs=-1)
self.model_.fit(df[self.feat].values, y)
return self
def cv(self, df: pd.DataFrame, coaxial: np.ndarray) -> Dict:
y = np.array([self._label(c) for c in coaxial])
kf = KFold(5, shuffle=True, random_state=42)
sc = cross_val_score(self.model_, df[self.feat].values, y,
cv=kf, scoring="f1_macro")
imp = dict(zip(self.feat, self.model_.feature_importances_))
return {"f1_macro": float(sc.mean()),
"importance": dict(sorted(imp.items(),
key=lambda x: x[1], reverse=True))}
def predict(self, df: pd.DataFrame) -> np.ndarray:
return self.model_.predict(df[self.feat].values)
</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", "WenQuanYi Micro Hei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
GRADE = {"合格":"#27AE60","临界":"#F39C12","超差":"#E74C3C"}
class RobotGraspVisualizer:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def deviation_scatter(self, dx, dy, res_before, res_after):
fig, ax = plt.subplots(figsize=(8,8))
ax.scatter(dx, dy, c=res_before*1000, cmap="Reds",
s=30, alpha=0.7, label="补偿前")
ax.scatter(dx*0.1, dy*0.1, c=res_after*1000, cmap="Greens",
s=20, marker="x", label="补偿后(放大10倍显示)")
ax.set_xlabel("来料Δx (mm)", fontsize=12)
ax.set_ylabel("来料Δy (mm)", fontsize=12)
ax.set_title("来料偏差散点(红=补偿前残差,绿=补偿后)",
fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3); ax.set_aspect("equal")
plt.tight_layout()
plt.savefig(self.results_dir/"deviation_scatter.png",
dpi=150, bbox_inches="tight")
plt.close()
def residual_hist(self, res_before, res_after):
fig, ax = plt.subplots(figsize=(10,6))
ax.hist(res_before*1000, bins=30, alpha=0.6,
color="#E74C3C", label=f"补偿前均值{res_before.mean()*1000:.1f}μm")
ax.hist(res_after*1000, bins=30, alpha=0.6,
color="#27AE60", label=f"补偿后均值{res_after.mean()*1000:.1f}μm")
ax.set_xlabel("抓取残差 (μm)", fontsize=12)
ax.set_ylabel("件数", fontsize=12)
ax.set_title("补偿前后残差分布对比", fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"residual_compare_hist.png",
dpi=150, bbox_inches="tight")
plt.close()
def coaxial_curve(self, offset_norm, coaxial_before, coaxial_after):
fig, ax = plt.subplots(figsize=(11,6))
x = offset_norm
ax.plot(x, coaxial_before*1000, color="#E74C3C", lw=1.8,
label="补偿前同轴度")
ax.plot(x, coaxial_after*1000, color="#27AE60", lw=1.8,
label="补偿后同轴度")
ax.axhline(30, color="#F39C12", ls="--", lw=1.5, label="合格线30μm")
ax.axhline(50, color="#E74C3C", ls="--", lw=1.5, label="超差线50μm")
ax.set_xlabel("来料偏移幅值 (mm)", fontsize=12)
ax.set_ylabel("同轴度 (μm)", fontsize=12)
ax.set_title("同轴度随来料偏移变化", fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"coaxial_vs_offset_curve.png",
dpi=150, bbox_inches="tight")
plt.close()
def chain_network(self, imp: Dict):
fig, ax = plt.subplots(figsize=(11,7))
G = nx.DiGraph()
nodes = ["来料偏差","视觉测量","自适应补偿","抓取残差","同轴度"]
for n in nodes:
G.add_node(n)
for a,b in zip(nodes[:-1], nodes[1:]):
G.add_edge(a,b, weight=0.8)
for k,v in imp.items():
if k=="residual":
G.add_edge("抓取残差","同轴度", weight=v)
elif k=="theta_abs":
G.add_edge("来料偏差","同轴度", weight=v)
pos = nx.spring_layout(G, seed=42)
nx.draw_networkx_nodes(G,pos,node_color="#3498DB",
node_size=4200,alpha=0.9,ax=ax)
nx.draw_networkx_edges(G,pos,arrowstyle="-|>",arrowsize=22,
edge_color="#555",width=2,ax=ax)
nx.draw_networkx_labels(G,pos,font_size=11,ax=ax,
font_color="white",font_weight="bold")
ax.set_title("来料-视觉-补偿-同轴度关联链",
fontsize=14, fontweight="bold")
ax.axis("off")
plt.tight_layout()
plt.savefig(self.results_dir/"grasp_chain_network.png",
dpi=150, bbox_inches="tight")
plt.close()
def pred_scatter(self, y_true, y_pred):
fig, ax = plt.subplots(figsize=(8,8))
labels = ["合格","临界","超差"]
ct = np.array([labels.index(y) for y in y_true])
cp = np.array([labels.index(y) for y in y_pred])
ax.scatter(ct, cp, c="#2980B9", s=50, edgecolors="k", alpha=0.8)
ax.plot([-0.5,2.5],[-0.5,2.5],"r--",lw=2,label="理想")
ax.set_xticks([0,1,2]); ax.set_xticklabels(labels)
ax.set_yticks([0,1,2]); ax.set_yticklabels(labels)
ax.set_xlabel("实际等级", fontsize=12)
ax.set_ylabel("预测等级", fontsize=12)
ax.set_title("合格分级 预测vs实际", fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"qualify_pred_scatter.png",
dpi=150, bbox_inches="tight")
plt.close()
def single_arrow(self, dx, dy, comp_vec, idx=0):
fig, ax = plt.subplots(figsize=(7,7))
ax.quiver(0,0,dx[idx],dy[idx], angles="xy", scale_units="xy",
scale=1, color="#E74C3C", width=0.008, label="来料真偏差")
ax.quiver(dx[idx],dy[idx], comp_vec[idx,0], comp_vec[idx,1],
angles="xy", scale_units="xy", scale=1, color="#27AE60",
width=0.008, label="补偿向量")
ax.scatter(0,0,c="#2C3E50",s=80,zorder=5,label="标称中心")
ax.set_xlim(-2,2); ax.set_ylim(-2,2)
ax.set_aspect("equal")
ax.set_xlabel("X (mm)", fontsize=12); ax.set_ylabel("Y (mm)", fontsize=12)
ax.set_title(f"单件抓取补偿向量示意(件{idx+1})",
fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"single_grasp_arrow.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 SyntheticWorkpieces:
"""
多批次盘类件
机制:
Y向偏移σ > X向
θ与偏移幅值弱耦合
视觉σ可配
"""
def __init__(self, rng: Optional[np.random.RandomState] = None):
self.rng = rng or np.random.RandomState(42)
def generate(self, out_path: str = "workpieces.csv",
n: int = 200, batch: int = 1,
sigma_x: float = 0.35, sigma_y: float = 0.45,
diameter: float = 120.0) -> pd.DataFrame:
dx = self.rng.normal(0, sigma_x, n)
dy = self.rng.normal(0, sigma_y, n)
r = np.hypot(dx, dy)
theta = 0.02 * r + self.rng.normal(0, 0.015, n) # 偏移越大转角略大
rows = [{
"pid": f"B{batch}_P{i+1:03d}",
"true_dx": round(dx[i], 4),
"true_dy": round(dy[i], 4),
"true_theta": round(theta[i], 4),
"batch": batch,
"diameter": diameter,
} for i in range(n)]
df = pd.DataFrame(rows)
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
df.to_csv(out_path, index=False, encoding="utf-8")
return df
</details>
<details>
<summary></summary>
"""
机器人抓取仿真: 来料偏差→视觉测量→自适应补偿→残差→同轴度
================================================================================
课程映射(滨州职业学院《先进制造技术》):
工业机器人技术基础:TCP/坐标变换/手眼标定/位姿误差/抓取定位
数控加工与CAD/CAM:装夹同轴度/定位基准
先进制造技术基础:几何精度/误差传递
智能制造与数字孪生:来料+机器人补偿看板
先进制造新模式:自适应工艺/补偿知识库
技术栈(严格):
pandas / numpy # 来料表/坐标变换
scipy # 样条+分布
scikit-learn # RF合格分级
matplotlib / networkx# 散点+关联链
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import numpy as np
import pandas as pd
from pathlib import Path
from robot_grasp_comp.workpiece_loader import WorkpieceLoader
from robot_grasp_comp.vision_model import VisionModel
from robot_grasp_comp.pose_compensator import PoseCompensator, CompResult
from robot_grasp_comp.grasp_residual import GraspResidual
from robot_grasp_comp.coaxial_mapper import CoaxialMapper
from robot_grasp_comp.qualify_classifier import QualifyClassifier
from robot_grasp_comp.robot_grasp_visualizer import RobotGraspVisualizer
from robot_grasp_comp.synthetic_workpieces import SyntheticWorkpieces
def main():
print("=" * 70)
print("机器人抓取仿真: 来料偏差→自适应补偿→同轴度预测")
print("=" * 70)
results_dir = Path("results"); results_dir.mkdir(exist_ok=True)
# 1. 合成多批次
print("\n[1/8] 合成来料批次...")
gen = SyntheticWorkpieces(rng=np.random.RandomState(42))
dfs = []
for b in range(3):
d = gen.generate(f"workpieces_b{b+1}.csv", n=200, batch=b+1,
sigma_x=0.35, sigma_y=0.45)
dfs.append(d)
df = pd.concat(dfs, ignore_index=True)
df.to_csv("workpieces.csv", index=False, encoding="utf-8")
print(f" 3批次共{len(df)}件, σx=0.35mm, σy=0.45mm")
# 2. 加载
print("\n[2/8] 加载来料...")
df = WorkpieceLoader("workpieces.csv").load()
print(WorkpieceLoader().summary(df))
dx = df["true_dx"].values
dy = df["true_dy"].values
th = df["true_theta"].values
dia = df["diameter"].values
# 3. 视觉测量
print("\n[3/8] 视觉测量(σ=0.08mm)...")
vm = VisionModel(sigma_vis=0.08, theta_n
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!