python的先进制造技术工业场景模拟第一百篇:编写机器人离线程序仿真,模拟模型标定误差,计算轨迹点位偏差大小。
周三下午,机器人焊接工作站。
“老周,这机器人又跑偏了,”调试员小赵揉着眼睛,“离线编程里轨迹漂漂亮亮,导到真机上一跑,焊枪尖离工件差了快3毫米。系统里TCP标定值还是上次校的,谁知道中间夹具动过、基座微偏、甚至机器人自己齿轮间隙都变了。”
我接上导出的机器人D-H参数表、标定记录、离线轨迹点云、实机示教对比点、误差日志。
“这里面有啥?”我问。
“模型里每个关节的α、a、d、θ理论值,标定值,离线生成的焊接轨迹点(几百个),还有实机用激光跟踪仪采的十几个对比点,”小赵说,“可离线软件只显示‘轨迹已生成’,不告诉你标了和没标之间差多少,哪个关节的误差贡献最大,点位偏差在空间里怎么分布的。”
“最亏的是返工,”小赵补一句,“离线程序导进去,试跑发现偏差大,只能回手动示教。一个复杂焊缝几百个点,逐个改坐标,改完下次标定一变又白改。以前靠老师傅‘感觉哪个轴偏了’手动调D-H,调完也不知道改善多少。”
“我就想干一件事,”小赵说,“给一套机器人模型,模拟标定误差——就是往D-H参数里加随机偏移量,然后重新算正向运动学,看每个轨迹点和理论位置的偏差有多大。要能量化:TCP末端最大偏多少、平均偏多少、哪个关节的误差对末端影响最大、误差在空间里是不是有方向性。像个小误差传播仿真器,不用每次都搬激光跟踪仪。”
“机器人离线程序误差不是看‘轨迹漂不漂’,”我接话,“是看‘D-H参数偏移→关节微小误差→正向运动学递推→末端位姿偏差→轨迹点云偏移量→空间分布→主因关节识别’。用 numpy 做齐次变换和误差递推,scipy 做误差分布拟合,pandas 管轨迹点,matplotlib 画偏差矢量场+散点+热力,networkx 建‘关节-误差-末端偏差’关联图,sklearn 做偏差等级分类。”
“对,”小赵点头,“要能说清‘基座标偏移0.5mm时TCP末端最大偏2.3mm,方向沿Y轴正方向;2轴α角误差0.3°贡献了末端偏差的41%;轨迹中段偏差最大,两端小’。”
“OOP 封好,”我开工程,“机器人模型、D-H误差注入器、正向运动学求解器、轨迹偏差计算器、误差分布分析器、偏差分类器、可视化器,合成6轴机器人+轨迹,下载就能跑。”
敲了行原型:
# 目标: D-H标定误差 → 正向运动学 → 末端位姿偏差 → 轨迹点云偏移
# 方法: numpy齐次变换 + scipy分布拟合 + RF分类 + networkx关联
小赵凑近看:“那以后看报告:各关节误差贡献柱图,轨迹点偏差散点,偏差矢量场,关节-误差-末端关联图,偏差等级预测。改个标定精度,绿区就是合格区间。”
“对,”我接话,“误差仿真不是‘看轨迹歪没歪’,是‘看见哪个关节在偷偷把枪尖推偏’。数字孪生里挂这个误差看板,就是小赵的‘标定尺’。”
一、实际应用场景(真实痛点)
场景设定:6轴关节型工业机器人弧焊工作站,离线编程软件生成焊接轨迹(空间点位序列),导入真机前需做模型标定。当前离线软件仅显示"轨迹已生成",不量化标定误差对末端执行器(焊枪TCP)的空间偏差影响。
现场原话(叙事化):
"不是离线软件不行,"小赵说,"是标定误差看不见。D-H参数标完,基座标、工具标、工件标各有一堆数字,每个都有±误差。可这些误差叠到末端TCP上到底偏多少?软件不告诉你。导到真机上,焊枪尖离焊缝差两三毫米,只能手动拖回去。"
"最亏的是反复标定,"小赵说,"标定一次大半天,改一个关节的补偿值,全轨迹都要重算。以前靠经验——'2轴好像低了,调高一点',调完试跑,还偏就再调。像蒙眼调收音机,拧一下听一下。"
核心矛盾:"离线轨迹生成 + 一次性标定" 与 "D-H参数误差→关节空间→笛卡尔空间→末端位姿偏差→轨迹点云偏移量→空间分布→主因关节识别" 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》课程模块 本篇痛点对应
工业机器人技术基础:D-H参数、正向/逆向运动学、标定、精度补偿 D-H误差注入→正向运动学递推→末端偏差
先进制造技术基础:几何误差、精度分配、误差传递 误差传播链建模
数控加工与CAD/CAM技术:刀具补偿、坐标系变换 TCP位姿偏差映射
智能制造与数字孪生:机器人数字模型、标定数据驱动 误差看板挂孪生
先进制造新模式:数据驱动精度补偿、自适应标定 按误差贡献排序优先标定
一句话总结:我们需要一个"D-H参数误差注入→齐次变换递推→末端位姿偏差→轨迹点云偏移→空间分布分析→偏差等级分类→关节贡献关联图"程序,实现从"标完就导"到"标前先算偏差、标后量化改善"的闭环。
三、核心逻辑讲解(大白话)
3.1 问题本质:把机器人想成"叠罗汉"
把6轴机器人想成6个人叠罗汉:
- 每个人代表一个关节,叠在一起最上面的人手里举着焊枪(TCP)
- 每个人的站姿都有微小偏差:脚没踩正(基座标偏)、腰歪一点(连杆扭角α偏)、手伸长不一(连杆长a偏)、站的高低不对(偏置d偏)
- 下面的人歪1°,传到最上面可能偏好几毫米
- 标定就是量每个人的偏差,补偿回去
- 但补偿哪个最划算? 得算每个偏差对最上面那个人位置的影响有多大
3.2 业务逻辑 → 代码映射
输入: 理论D-H参数 + 标定误差范围 + 离线轨迹点(关节角序列)
│
▼ RobotModel (numpy)
机器人模型:
6轴D-H表: [α, a, d, θ] × 6
齐次变换矩阵 T_i = RotX(α)·TransX(a)·TransZ(d)·RotZ(θ)
正向运动学: T_total = T_1 × T_2 × ... × T_6
末端位姿 = T_total 的平移分量 + 旋转分量
│
▼ DHErrorInjector (numpy)
误差注入:
给每个D-H参数加偏移: Δα, Δa, Δd, Δθ ~ 均匀/正态
可设单参数偏移(敏感性分析) 或 全参数偏移(综合误差)
│
▼ ForwardKinematics (numpy)
正向运动学求解:
对每组(理论+偏移)D-H参数算 T_total_err
末端位置 P_err = T_total_err[:3,3]
理论位置 P_ref = T_total_ref[:3,3]
偏差 ΔP = P_err - P_ref
偏差大小 ‖ΔP‖ = sqrt(dx²+dy²+dz²)
│
▼ TrajectoryDeviation (numpy)
轨迹偏差计算:
对离线轨迹每个点(关节角组)算偏差
输出: 每点偏差向量 + 偏差大小
│
▼ ErrorDistribution (scipy)
误差分布分析:
偏差大小分布: 正态/对数正态拟合
方向分布: 各轴偏差均值/方差
空间热力: 按工作空间分区统计
│
▼ JointContribution (numpy)
关节贡献分析:
单参数敏感性: 只偏一个参数, 看末端偏多少
贡献比 = 单参数偏差 / 全参数偏差
│
▼ DeviationClassifier (sklearn)
偏差等级分类:
特征: 各关节误差值, 位置, 轨迹段
标签: 优(‖ΔP‖≤0.5mm)/良(0.5~2mm)/超差(>2mm)
RF分类 + 5折宏F1
│
▼ RobotViz (matplotlib + networkx)
可视化:
1. 轨迹点偏差散点(3D或2D投影)
2. 偏差矢量场(箭头=偏差方向)
3. 各关节误差贡献柱图
4. 关节-误差-末端关联图(networkx)
5. 偏差等级预测vs实际散点
6. 工作空间偏差热力图
│
▼ SyntheticRobot (numpy)
合成数据:
6轴机器人理论D-H + 焊接轨迹(50点) + 误差注入
3.3 为什么不能"标完就导"
视角 问题
离线软件 只显示"轨迹已生成",不量化误差
一次性标定 不知道哪个参数对末端影响最大
手动试调 蒙眼拧收音机
误差传播仿真 看见每个关节偏差对末端的贡献
偏差矢量场 看偏差往哪个方向偏
关节贡献排序 优先标定贡献最大的关节
3.4 分析前后对比
维度 传统方式 本程序
误差可见性 试跑才发现偏 标前算出偏差分布
标定优先级 全标/凭经验 按贡献排序标定
补偿效果 调完不知改善多少 量化偏差降低量
空间分布 不知道偏哪 矢量场显示方向
知识沉淀 师傅手感 误差传播指纹库
四、OOP 代码实现
4.1 项目结构
robot_calib_error_sim/
├── robot_calib_error_sim/
│ ├── __init__.py
│ ├── robot_model.py # 机器人D-H模型(numpy)
│ ├── dh_error_injector.py # 误差注入(numpy)
│ ├── forward_kinematics.py # 正向运动学(numpy)
│ ├── trajectory_deviation.py # 轨迹偏差计算
│ ├── error_distribution.py # 误差分布分析(scipy)
│ ├── joint_contribution.py # 关节贡献分析
│ ├── deviation_classifier.py # 偏差等级分类(sklearn)
│ ├── robot_viz.py # 可视化
│ └── synthetic_robot.py # 合成机器人+轨迹
├── tests/
│ ├── __init__.py
│ └── test_robot_calib.py
├── results/
│ ├── deviation_scatter.png
│ ├── deviation_vector.png
│ ├── joint_contribution_bar.png
│ ├── joint_error_tcp_graph.png
│ ├── deviation_grade_scatter.png
│ ├── workspace_heatmap.png
│ ├── trajectory_detail.csv
│ └── calib_report.txt
└── run_robot_calib.py
4.2 核心源码
<details><summary></summary>
"""6轴机器人D-H模型 (numpy)。"""
import numpy as np
from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class DHParam:
"""单关节D-H参数。"""
alpha: float # 连杆扭角 (rad)
a: float # 连杆长度 (mm)
d: float # 关节偏置 (mm)
theta: float # 关节角 (rad)
joint_type: str = "revolute" # revolute/prismatic
@dataclass
class RobotModel:
"""6轴关节型机器人。"""
dh_params: List[DHParam]
name: str = "6DOF_Welding"
def __post_init__(self):
assert len(self.dh_params) == 6, "需要6个关节D-H参数"
def get_dh_array(self) -> np.ndarray:
"""返回 (6,4) 数组: [alpha, a, d, theta]。"""
arr = np.zeros((6, 4))
for i, p in enumerate(self.dh_params):
arr[i] = [p.alpha, p.a, p.d, p.theta]
return arr
</details>
<details><summary></summary>
"""D-H误差注入器 (numpy)。"""
import numpy as np
from dataclasses import dataclass
from typing import Tuple, Optional
from .robot_model import RobotModel, DHParam
@dataclass
class DHErrors:
"""D-H参数误差集合。"""
delta_alpha: np.ndarray # (6,) rad
delta_a: np.ndarray # (6,) mm
delta_d: np.ndarray # (6,) mm
delta_theta: np.ndarray # (6,) rad
class DHErrorInjector:
"""
给理论D-H参数注入误差。
支持: 全随机注入 / 单参数偏移 / 指定误差向量
"""
def __init__(self, seed: int = 42):
self.seed = seed
self.rng = np.random.RandomState(seed)
def inject_random(self, robot: RobotModel,
bounds: Optional[dict] = None) -> Tuple[RobotModel, DHErrors]:
"""
随机注入误差。
bounds: {param: (min, max)} 默认合理范围
"""
if bounds is None:
bounds = {
"alpha": (-0.005, 0.005), # ±0.005 rad ≈ ±0.3°
"a": (-1.0, 1.0), # ±1mm
"d": (-1.0, 1.0), # ±1mm
"theta": (-0.003, 0.003), # ±0.003 rad ≈ ±0.17°
}
d_alpha = self.rng.uniform(*bounds["alpha"], 6)
d_a = self.rng.uniform(*bounds["a"], 6)
d_d = self.rng.uniform(*bounds["d"], 6)
d_theta = self.rng.uniform(*bounds["theta"], 6)
err = DHErrors(d_alpha, d_a, d_d, d_theta)
err_robot = self._apply_errors(robot, err)
return err_robot, err
def inject_single_joint(self, robot: RobotModel,
joint_idx: int, param: str,
delta: float) -> RobotModel:
"""单关节单参数偏移(敏感性分析)。"""
err = DHErrors(
delta_alpha=np.zeros(6),
delta_a=np.zeros(6),
delta_d=np.zeros(6),
delta_theta=np.zeros(6),
)
if param == "alpha":
err.delta_alpha[joint_idx] = delta
elif param == "a":
err.delta_a[joint_idx] = delta
elif param == "d":
err.delta_d[joint_idx] = delta
elif param == "theta":
err.delta_theta[joint_idx] = delta
return self._apply_errors(robot, err), err
def _apply_errors(self, robot: RobotModel, err: DHErrors) -> RobotModel:
new_params = []
for i, p in enumerate(robot.dh_params):
new_params.append(DHParam(
alpha=p.alpha + err.delta_alpha[i],
a=p.a + err.delta_a[i],
d=p.d + err.delta_d[i],
theta=p.theta + err.delta_theta[i],
joint_type=p.joint_type,
))
return RobotModel(new_params, robot.name)
</details>
<details><summary></summary>
"""正向运动学求解器 (numpy)。"""
import numpy as np
from typing import Tuple
from .robot_model import RobotModel
class ForwardKinematics:
"""
基于D-H参数的正向运动学。
T_i = RotX(α) · TransX(a) · TransZ(d) · RotZ(θ)
"""
@staticmethod
def dh_matrix(alpha: float, a: float, d: float, theta: float) -> np.ndarray:
"""单关节齐次变换矩阵。"""
ct = np.cos(theta)
st = np.sin(theta)
ca = np.cos(alpha)
sa = np.sin(alpha)
T = np.array([
[ct, -st*ca, st*sa, a*ct],
[st, ct*ca, -ct*sa, a*st],
[0, sa, ca, d],
[0, 0, 0, 1],
], dtype=float)
return T
def solve(self, robot: RobotModel,
joint_angles: np.ndarray) -> np.ndarray:
"""
计算末端位姿。
joint_angles: (6,) 关节角(rad)
返回: 4x4 齐次变换矩阵
"""
T = np.eye(4)
for i, p in enumerate(robot.dh_params):
theta = p.theta + joint_angles[i] if p.joint_type == "revolute" else p.theta
Ti = self.dh_matrix(p.alpha, p.a, p.d, theta)
T = T @ Ti
return T
def get_position(self, robot: RobotModel,
joint_angles: np.ndarray) -> np.ndarray:
"""返回末端位置 (3,)。"""
T = self.solve(robot, joint_angles)
return T[:3, 3]
def get_pose(self, robot: RobotModel,
joint_angles: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""返回 (position (3,), rotation_matrix (3,3))。"""
T = self.solve(robot, joint_angles)
return T[:3, 3], T[:3, :3]
</details>
<details><summary></summary>
"""轨迹偏差计算器 (numpy)。"""
import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import List
from .robot_model import RobotModel
from .forward_kinematics import ForwardKinematics
@dataclass
class PointDeviation:
point_idx: int
joint_angles: np.ndarray
ref_position: np.ndarray
err_position: np.ndarray
deviation_vector: np.ndarray
deviation_magnitude: float
class TrajectoryDeviation:
"""
对离线轨迹每个点计算偏差。
"""
def __init__(self, fk: ForwardKinematics):
self.fk = fk
def compute(self, ref_robot: RobotModel, err_robot: RobotModel,
trajectory: np.ndarray) -> List[PointDeviation]:
"""
trajectory: (N, 6) 关节角序列
"""
results = []
for i in range(len(trajectory)):
angles = trajectory[i]
p_ref = self.fk.get_position(ref_robot, angles)
p_err = self.fk.get_position(err_robot, angles)
dev_vec = p_err - p_ref
dev_mag = np.linalg.norm(dev_vec)
results.append(PointDeviation(
i, angles, p_ref, p_err, dev_vec, dev_mag
))
return results
def to_dataframe(self, deviations: List[PointDeviation]) -> pd.DataFrame:
rows = []
for d in deviations:
rows.append({
"point_idx": d.point_idx,
"ref_x": d.ref_position[0],
"ref_y": d.ref_position[1],
"ref_z": d.ref_position[2],
"err_x": d.err_position[0],
"err_y": d.err_position[1],
"err_z": d.err_position[2],
"dev_dx": d.deviation_vector[0],
"dev_dy": d.deviation_vector[1],
"dev_dz": d.deviation_vector[2],
"dev_magnitude": d.deviation_magnitude,
})
return pd.DataFrame(rows)
</details>
<details><summary></summary>
"""误差分布分析 (scipy)。"""
import numpy as np
import pandas as pd
from scipy.stats import norm, lognorm
from typing import Dict
class ErrorDistribution:
def analyze(self, deviations_df: pd.DataFrame) -> Dict:
"""分析偏差大小分布。"""
mags = deviations_df["dev_magnitude"].values
dx = deviations_df["dev_dx"].values
dy = deviations_df["dev_dy"].values
dz = deviations_df["dev_dz"].values
result = {
"count": len(mags),
"mean_mag": float(np.mean(mags)),
"max_mag": float(np.max(mags)),
"min_mag": float(np.min(mags)),
"std_mag": float(np.std(mags)),
"p95_mag": float(np.percentile(mags, 95)),
"mean_dx": float(np.mean(dx)),
"mean_dy": float(np.mean(dy)),
"mean_dz": float(np.mean(dz)),
"std_dx": float(np.std(dx)),
"std_dy": float(np.std(dy)),
"std_dz": float(np.std(dz)),
}
# 正态拟合
try:
params = norm.fit(mags)
result["norm_fit"] = {"mu": params[0], "sigma": params[1]}
except Exception:
result["norm_fit"] = None
# 方向主导轴
dir_vars = {"X": result["std_dx"], "Y": result["std_dy"], "Z": result["std_dz"]}
result["dominant_axis"] = max(dir_vars, key=dir_vars.get)
result["direction_bias"] = {
"X": result["mean_dx"],
"Y": result["mean_dy"],
"Z": result["mean_dz"],
}
return result
</details>
<details><summary></summary>
"""关节贡献分析器。"""
import numpy as np
import pandas as pd
from .robot_model import RobotModel
from .dh_error_injector import DHErrorInjector
from .forward_kinematics import ForwardKinematics
from .trajectory_deviation import TrajectoryDeviation
class JointContribution:
"""
单参数敏感性分析 → 各关节各参数对末端偏差的贡献比。
"""
def __init__(self):
self.fk = ForwardKinematics()
self.injector = DHErrorInjector(seed=42)
def analyze(self, ref_robot: RobotModel,
trajectory: np.ndarray,
base_errors: object) -> pd.DataFrame:
"""
对全参数误差下的每个关节每个参数做敏感性分析。
base_errors: DHErrors对象(全参数偏移)
"""
# 全参数偏差基准
err_robot, _ = self.injector._apply_errors_from_dherrors(
ref_robot, base_errors)
td = TrajectoryDeviation(self.fk)
base_devs = td.compute(ref_robot, err_robot, trajectory)
base_mean_mag = np.mean([d.deviation_magnitude for d in base_devs])
rows = []
params = ["alpha", "a", "d", "theta"]
for j in range(6):
for p in params:
# 单参数偏移(用base_errors中该参数的实际偏移量)
delta = getattr(base_errors, f"delta_{p}")[j]
if abs(delta) < 1e-10:
contribution = 0.0
else:
single_err_robot, _ = self.injector.inject_single_joint(
ref_robot, j, p, delta)
single_devs = td.compute(ref_robot, single_err_robot, trajectory)
single_mean = np.mean([d.deviation_magnitude for d in single_devs])
contribution = single_mean / (base_mean_mag + 1e-10)
rows.append({
"joint": j + 1,
"param": p,
"delta": delta,
"contribution": contribution,
})
return pd.DataFrame(rows)
</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 DeviationClassifier:
"""优(≤0.5mm)/良(0.5~2mm)/超差(>2mm)。"""
def __init__(self, random_state: int = 42):
self.model_ = None
self.feat = ["dev_dx", "dev_dy", "dev_dz", "dev_magnitude"]
@staticmethod
def _label(mag):
if mag <= 0.5:
return "优"
if mag <= 2.0:
return "良"
return "超差"
def fit(self, df: pd.DataFrame):
y = df["dev_magnitude"].apply(self._label).values
self.model_ = RandomForestClassifier(
n_estimators=300, max_depth=5, min_samples_leaf=1,
random_state=42, n_jobs=-1)
self.model_.fit(df[self.feat].values, y)
return self
def cv(self, df: pd.DataFrame) -> Dict:
y = df["dev_magnitude"].apply(self._label).values
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
class RobotViz:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def deviation_scatter(self, df):
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, col, title in zip(axes, ["dev_dx","dev_dy","dev_dz"],
["ΔX (mm)","ΔY (mm)","ΔZ (mm)"]):
ax.scatter(df.point_idx, df[col], c=df["dev_magnitude"],
cmap="YlOrRd", s=30, edgecolors="k", alpha=0.8)
ax.set_xlabel("轨迹点序号")
ax.set_ylabel(title)
ax.set_title(f"偏差分量 {title}")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"deviation_scatter.png",
dpi=150, bbox_inches="tight")
plt.close()
def deviation_vector(self, df, sample_n=20):
fig, ax = plt.subplots(figsize=(8, 8))
# 取末端位置均值作原点
origin = df[["ref_x","ref_y","ref_z"]].mean().values
sampled = df.iloc[::max(1,len(df)//sample_n)]
for _, row in sampled.iterrows():
start = row[["ref_x","ref_y"]].values
dx = row["dev_dx"] * 5 # 放大显示
dy = row["dev_dy"] * 5
ax.quiver(start[0], start[1], dx, dy,
angles="xy", scale_units="xy", scale=1,
color="#E74C3C", alpha=0.7, width=0.003)
ax.set_xlabel("X (mm)", fontsize=12)
ax.set_ylabel("Y (mm)", fontsize=12)
ax.set_title("偏差矢量场(俯视XY, 箭头放大5×)",
fontsize=13, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"deviation_vector.png",
dpi=150, bbox_inches="tight")
plt.close()
def joint_contribution_bar(self, contrib_df):
fig, ax = plt.subplots(figsize=(12, 5))
# 按关节汇总
joint_sum = contrib_df.groupby("joint")["contribution"].sum().reset_index()
bars = ax.bar(joint_sum.joint.astype(str), joint_sum.contribution,
color="#2980B9", edgecolor="k", alpha=0.85)
# 标注最大贡献参数
for j in joint_sum.joint:
sub = contrib_df[contrib_df.joint == j]
max_row = sub.loc[sub.contribution.idxmax()]
ax.text(j-1, sub.contribution.sum()+0.02,
f"{max_row.param}", ha="center", fontsize=8, color="#E74C3C")
ax.set_xlabel("关节号", fontsize=12)
ax.set_ylabel("总贡献比", fontsize=12)
ax.set_title("各关节误差对末端偏差的贡献",
fontsize=13, fontweight="bold")
ax.grid(alpha=0.3, axis="y")
plt.tight_layout()
plt.savefig(self.results_dir/"joint_contribution_bar.png",
dpi=150, bbox_inches="tight")
plt.close()
def joint_error_tcp_graph(self):
fig, ax = plt.subplots(figsize=(10, 6))
G = nx.DiGraph()
for j in range(1, 7):
G.add_node(f"J{j}", bipartite="joint")
G.add_node("TCP_X"); G.add_node("TCP_Y"); G.add_node("TCP_Z")
G.add_node("末端偏差")
for j in range(1, 7):
G.add_edge(f"J{j}", "末端偏差", weight=0.3)
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)