python的机器人基础原理工业场景模拟第九十篇:焊接产线,多组不同工具TCP偏移,循环运行正运动学,绘制TCP偏移与末端位置误差的变化曲线。
《Python机器人基础原理》第九十篇:焊接产线——多组TCP偏移下的正运动学误差分析
声明:本篇全部为课程离线仿真代码,不能直接用于真实机器人控制器,规避工程误导风险。
1. 实际工业应用场景
周三上午,汽车零部件焊接车间。一台6-DOF弧焊机器人(额定负载6.0 kg,重复定位精度±0.05 mm)正在做焊枪TCP标定验证。
调试工程师老赵拿着校准记录本:"这把焊枪标称TCP偏移是
"[0, 0, 0.12] m"——焊嘴在第六轴法兰端面沿Z方向伸出120 mm。但现场实际测量发现,法兰端面到焊嘴的距离有±3 mm的批次公差。我手头有5把焊枪,实测TCP分别是:"
焊枪编号 dx (m) dy (m) dz (m) 来源
1号(标称) 0 0 0.120 设计值
2号 0.001 -0.002 0.118 实测
3号 -0.0015 0.001 0.123 实测
4号 0.0025 0.0005 0.117 实测
5号 -0.001 -0.0015 0.121 实测
"昨天焊接出来的焊缝偏了将近4 mm——超差了。TCP批次公差±3 mm只能解释其中约3 mm,剩下1 mm来自标定误差和热变形。我打算写个仿真:把这5把实测TCP输入进去,再在它们周围随机撒一批扰动,固定几种典型姿态跑正运动学,看看误差方向怎么随姿态变化。"
年轻工程师小周问:"姿态变化会影响误差方向?"
"当然。TCP偏移是相对于末端法兰坐标系的。法兰朝不同方向时,同一个
"[Δdx, Δdy, Δdz]"在基坐标系下的投影完全不同。J1转90°时,原本沿Z的偏移变成沿Y——误差方向跟着旋转。这个规律用旋转矩阵
"Δp = R · Δtcp"就能解释,但我想用仿真数据把曲线画出来,给现场标定提供直观依据。"
构型约定:本篇采用3-DOF简化模型(R-R-R构型)聚焦TCP位置分析。J4在上一篇中被证明对位置无贡献,本篇直接省略。标准DH参数建模,TCP作为纯平移齐次矩阵右乘在法兰变换之后。技术栈限定numpy + matplotlib。
⚠️ 声明:本篇全部代码为课程离线仿真,运行在普通PC上,用于TCP标定误差分析验证。代码中不包含任何真实机器人控制器实时运算或安全逻辑。不可直接用于产线设备。场景中的额定负载6.0 kg、重复定位精度±0.05 mm为现场机器人实际参数,本篇纯运动学分析不涉及动力学与精度模型,留待后续篇章。
2. 痛点分析
课程模块 本篇对应的工程痛点
第2章 位姿描述与坐标变换 → 2.2 齐次变换矩阵 TCP偏移是末端坐标系到工具坐标系的附加平移,通过右乘齐次矩阵实现
第2章 → 2.1 旋转矩阵与坐标变换 误差方向
"Δp = R·Δtcp",姿态旋转矩阵决定误差在基坐标系的投影分布
第3章 连杆参数及运动学求解 → 3.2 正运动学 TCP误差通过链式乘法传递,需验证
"\|\|Δp\|\| = \|\|Δtcp\|\|"保范数恒等式
第6章 轨迹规划与控制 → 6.1 路径精度 焊接对TCP精度要求±0.5 mm,需量化不同姿态下误差分量分布
本篇代码解决 L2 离线仿真校核层。输入:5把实测TCP + 随机扰动组、3种典型姿态。处理:①对每组TCP和每种姿态运行正运动学;②计算与标称TCP的末端位置误差;③验证
"||Δp|| = ||Δtcp||"恒等式;④分析误差分量随姿态的变化。输出:误差-偏移关系图 + 多姿态误差分量对比 + 4张分析图表。
不覆盖:逆运动学、TCP四点标定算法、动力学、实时控制。
3. 核心逻辑讲解
物理直觉
TCP偏移误差在基坐标系下的表现由旋转矩阵决定。设末端法兰相对于基坐标系的旋转矩阵为
"R"(来自正运动学前3×3子块),TCP偏移扰动为
"Δtcp = [Δdx, Δdy, Δdz]^T",则末端位置误差为:
"Δp = R · Δtcp"
因为
"R"是正交矩阵(
"R^T R = I"),它保持向量长度不变:
"||Δp||₂ = ||R · Δtcp||₂ = ||Δtcp||₂"
这是数学恒等式——误差模长严格等于TCP偏移偏差模长,斜率精确等于1。仿真不是"发现"这个规律,而是校核代码实现是否正确:如果斜率偏离1,说明变换链写错了。
更有教学价值的是分量层面:
"Δp"的X/Y/Z分量取决于
"R"的每一行与
"Δtcp"的点积。同一个
"Δtcp",J1从0°转到90°时
"R"变化,误差分量重新分配。本篇用3种姿态验证这一现象。
业务逻辑 → 代码映射
输入:5把实测TCP + 随机扰动组、3种关节角姿态
↓
【步骤1:计算标称TCP在姿态A下的末端位置】
p_nominal_A = FK(θ_A) · T_tcp_nominal
↓
【步骤2:循环每组TCP、每种姿态】
for each tcp in tcp_list:
for each pose in poses:
p = FK(θ_pose) · T_tcp
error_vec = p - p_nominal_pose
error_mag = ||error_vec||₂
offset_mag = ||tcp - tcp_nominal||
记录 (offset_mag, error_mag, error_vec, pose_id)
↓
【步骤3:验证恒等式】
检查 |error_mag - offset_mag| < 1e-10 对所有样本成立
↓
【步骤4:可视化】
图①:offset_mag vs error_mag(验证斜率=1)
图②:多姿态下各分量误差对比
图③:误差向量三维分布
图④:不同TCP末端位置散布
关键公式文字翻译
TCP附加变换:
"T_total = T_flange · T_tcp"。
"T_tcp"是4×4齐次矩阵,平移部分为
"[dx, dy, dz]^T",旋转为单位阵。物理含义:在末端法兰坐标系内附加一个位置偏移。
误差传播:
"Δp = R · Δtcp"。
"R"是3×3旋转矩阵(法兰相对于基座的方向),
"Δtcp"是TCP偏移扰动向量。误差方向 =
"R"的各行对
"Δtcp"的加权组合。
保范数恒等式:
"||Δp||₂ = ||Δtcp||₂"。正交变换不改变向量长度。这是理论预期,仿真用于校核实现正确性。
4. OOP代码实现
技术栈
限定:numpy, matplotlib。不引入pybullet、roboticstoolbox等任何第三方重型库。全部自实现。
项目结构树
welding_tcp_offset/
├── run_tcp_analysis.py
├── requirements.txt
├── robot/
│ ├── __init__.py
│ ├── models.py
│ ├── dh_kinematics.py
│ ├── tcp_analyzer.py
│ └── visualize.py
├── tests/
│ ├── __init__.py
│ └── test_tcp.py
├── results/
└── output/
核心源码
"robot/models.py" — 数据模型
"""
数据模型:关节限位、DH参数、TCP偏移、机器人配置
全部参数内置,自包含可复现
"""
import numpy as np
import math
from dataclasses import dataclass, field
from typing import List, Tuple
@dataclass
class JointLimits:
"""
单个关节的限位定义
lower: 下限位 (rad)
upper: 上限位 (rad)
name: 关节名称
"""
lower: float
upper: float
name: str = ""
def __post_init__(self):
if self.lower >= self.upper:
raise ValueError(
f"关节{self.name}下限位({self.lower})必须<上限位({self.upper})")
if not (math.isfinite(self.lower) and math.isfinite(self.upper)):
raise TypeError(f"关节{self.name}限位必须为有限数值")
@dataclass
class TCPOffset:
"""
TCP(工具中心点)偏移量
dx, dy, dz: 相对于末端法兰坐标系的平移 (m)
name: 标识名称
"""
dx: float
dy: float
dz: float
name: str = ""
def to_homogeneous(self):
"""转换为4×4齐次变换矩阵(纯平移,无旋转)"""
T = np.eye(4)
T[0, 3] = self.dx
T[1, 3] = self.dy
T[2, 3] = self.dz
return T
def magnitude(self):
"""偏移向量模长"""
return math.sqrt(self.dx**2 + self.dy**2 + self.dz**2)
def difference_norm(self, other: "TCPOffset"):
"""与另一个TCP的偏移量差模长"""
dx = self.dx - other.dx
dy = self.dy - other.dy
dz = self.dz - other.dz
return math.sqrt(dx**2 + dy**2 + dz**2)
def as_array(self):
"""返回numpy数组"""
return np.array([self.dx, self.dy, self.dz], dtype=float)
@dataclass
class RobotConfig:
"""
机器人完整配置(3-DOF简化模型,J4对位置无贡献已省略)
"""
joint_limits: List[JointLimits] = field(default_factory=list)
dh_params: List[Tuple[float, float, float, float]] = field(
default_factory=list)
nominal_tcp: TCPOffset = None
def __post_init__(self):
if not self.joint_limits:
self.joint_limits = [
JointLimits(math.radians(-165), math.radians(165),
"J1_基座回转"),
JointLimits(math.radians(-20), math.radians(85),
"J2_大臂俯仰"),
JointLimits(math.radians(-90), math.radians(60),
"J3_小臂俯仰"),
]
if not self.dh_params:
# 标准DH参数:(θ_offset, d, a, α)
# 连杆1: 基座高0.45m
# 连杆2: 大臂长0.55m, α=-90°
# 连杆3: 小臂长0.45m
self.dh_params = [
(0.0, 0.45, 0.0, 0.0),
(0.0, 0.0, 0.55, -math.pi / 2),
(0.0, 0.0, 0.45, 0.0),
]
if self.nominal_tcp is None:
self.nominal_tcp = TCPOffset(
0.0, 0.0, 0.12, "1号标称焊枪")
"robot/dh_kinematics.py" — 正运动学
"""
标准DH正运动学:3-DOF弧焊机械臂
变换顺序:Rz(θ) · Tz(d) · Tx(a) · Rx(α)
支持附加TCP齐次变换
J4已省略(对位置无贡献,仅影响末端姿态)
"""
import numpy as np
import math
from robot.models import RobotConfig
class DHKinematics:
def __init__(self, config: RobotConfig):
self.config = config
def _rot_z(self, theta):
c, s = math.cos(theta), math.sin(theta)
return np.array([
[c, -s, 0, 0],
[s, c, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
], dtype=float)
def _trans_z(self, d):
M = np.eye(4)
M[2, 3] = d
return M
def _trans_x(self, a):
M = np.eye(4)
M[0, 3] = a
return M
def _rot_x(self, alpha):
c, s = math.cos(alpha), math.sin(alpha)
return np.array([
[1, 0, 0, 0],
[0, c, -s, 0],
[0, s, c, 0],
[0, 0, 0, 1]
], dtype=float)
def forward_kinematics(self, joint_angles: list):
"""正运动学:不含TCP,返回法兰端4×4矩阵"""
if len(joint_angles) != 3:
raise ValueError("本模型需要3个关节角")
T_total = np.eye(4)
for i, (theta_offset, d, a, alpha) in enumerate(
self.config.dh_params):
theta = joint_angles[i] + theta_offset
T_i = (
self._rot_z(theta)
@ self._trans_z(d)
@ self._trans_x(a)
@ self._rot_x(alpha)
)
T_total = T_total @ T_i
return T_total
def get_flange_rotation(self, joint_angles: list):
"""
返回法兰相对于基坐标系的3×3旋转矩阵R
用于误差方向分析:Δp = R · Δtcp
"""
T = self.forward_kinematics(joint_angles)
return T[:3, :3].copy()
def forward_kinematics_with_tcp(self, joint_angles: list,
tcp):
"""
正运动学 + TCP附加变换
返回末端执行器(焊嘴)的4×4齐次矩阵
"""
T_flange = self.forward_kinematics(joint_angles)
T_tcp = tcp.to_homogeneous()
return T_flange @ T_tcp
def get_end_effector_position(self, joint_angles: list,
tcp):
"""返回焊嘴位置 [x, y, z] (m)"""
T = self.forward_kinematics_with_tcp(joint_angles, tcp)
return T[:3, 3].copy()
"robot/tcp_analyzer.py" — TCP偏移分析器
"""
TCP偏移误差分析器
输入:5把实测TCP + 随机扰动、多种姿态
循环运行正运动学,计算误差,验证保范数恒等式
"""
import numpy as np
from robot.models import RobotConfig, TCPOffset
from robot.dh_kinematics import DHKinematics
class TCPAnalyzer:
def __init__(self, config: RobotConfig,
kinematics: DHKinematics):
self.config = config
self.kinematics = kinematics
def get_five_real_tcps(self):
"""返回5把实测焊枪的TCP"""
return [
TCPOffset(0.0, 0.0, 0.120, "1号标称"),
TCPOffset(0.001, -0.002, 0.118, "2号实测"),
TCPOffset(-0.0015, 0.001, 0.123, "3号实测"),
TCPOffset(0.0025, 0.0005, 0.117, "4号实测"),
TCPOffset(-0.001, -0.0015, 0.121, "5号实测"),
]
def generate_random_variations(self, n_variations=15,
max_deviation=0.005,
seed=42):
"""
在标称TCP附近生成随机扰动组
用于补充5把实测之外的统计样本
"""
rng = np.random.default_rng(seed)
nominal = self.config.nominal_tcp
variations = []
for i in range(n_variations):
dx = nominal.dx + rng.uniform(
-max_deviation, max_deviation)
dy = nominal.dy + rng.uniform(
-max_deviation, max_deviation)
dz = nominal.dz + rng.uniform(
-max_deviation, max_deviation)
variations.append(
TCPOffset(dx, dy, dz,
f"随机扰动_{i+1}"))
return variations
def analyze_single_pose(self, joint_angles, tcp_list):
"""
对单一姿态分析所有TCP的误差
返回结构化结果字典
"""
nominal_tcp = self.config.nominal_tcp
p_nominal = self.kinematics.get_end_effector_position(
joint_angles, nominal_tcp)
results = {
'joint_angles': joint_angles,
'p_nominal': p_nominal,
'tcp_names': [],
'tcp_offsets': [],
'position_errors': [],
'error_components': [],
'offset_magnitudes': [],
'norm_identity_check': [],
}
for tcp in tcp_list:
p_tcp = self.kinematics.get_end_effector_position(
joint_angles, tcp)
error_vec = p_tcp - p_nominal
error_mag = np.linalg.norm(error_vec)
offset_mag = tcp.difference_norm(nominal_tcp)
# 保范数恒等式检查:|error_mag - offset_mag| 应<1e-10
identity_diff = abs(error_mag - offset_mag)
results['tcp_names'].append(tcp.name)
results['tcp_offsets'].append(tcp.as_array())
results['position_errors'].append(error_mag)
results['error_components'].append(error_vec.tolist())
results['offset_magnitudes'].append(offset_mag)
results['norm_identity_check'].append(identity_diff)
return results
def analyze_multiple_poses(self, poses, tcp_list):
"""
对多种姿态分别分析,返回按姿态组织的字典
"""
all_results = {}
for pose_id, joint_angles in poses.items():
all_results[pose_id] = self.analyze_single_pose(
joint_angles, tcp_list)
return all_results
"robot/visualize.py" — 可视化
"""
TCP偏移误差分析可视化:4张图表
① offset_mag vs error_mag(验证保范数恒等式,斜率=1)
② 多姿态下各分量误差对比(验证误差方向随姿态旋转)
③ 误差向量三维分布
④ 不同TCP末端位置散布
"""
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans',
'Noto Sans CJK SC']
plt.rcParams['axes.unicode_minus'] = False
class TCPVisualizer:
def __init__(self, results_dir="results"):
self.results_dir = results_dir
import os
os.makedirs(results_dir, exist_ok=True)
def plot_offset_vs_error(self, results):
"""
① TCP偏移量偏差 vs 末端位置误差
核心图:验证||Δp|| = ||Δtcp||恒等式
斜率=1是理论预期,仿真用于校核实现正确性
"""
fig, ax = plt.subplots(figsize=(9, 6))
offset_mags = results['offset_magnitudes']
errors = results['position_errors']
ax.scatter(offset_mags, errors, c='steelblue',
s=50, alpha=0.7, edgecolors='navy',
label='仿真数据点')
# 理论预期线:y = x(保范数恒等式)
max_val = max(max(offset_mags), max(errors))
ax.plot([0, max_val], [0, max_val], 'r--', linewidth=2,
label='理论预期: ||Δp|| = ||Δtcp|| (斜率=1)')
# 线性拟合(校核用,预期斜率≈1)
if len(offset_mags) > 1:
coeffs = np.polyfit(offset_mags, errors, 1)
fit_line = np.poly1d(coeffs)
x_sorted = np.sort(offset_mags)
ax.plot(x_sorted, fit_line(x_sorted), 'g-.',
linewidth=1.5,
label=f'拟合: y={coeffs[0]:.6f}x+{coeffs[1]:.6f}')
ax.set_xlabel('TCP偏移偏差模长 (m)')
ax.set_ylabel('末端位置误差模长 (m)')
ax.set_title('TCP偏移偏差 vs 末端位置误差\n'
'(斜率=1为保范数恒等式理论预期,用于校核实现)')
ax.grid(True, alpha=0.3)
ax.legend()
plt.tight_layout()
path = f"{self.results_dir}/01_offset_vs_error.png"
plt.savefig(path, dpi=150)
plt.close()
return path
def plot_multi_pose_components(self, all_results, poses):
"""
② 多姿态下各分量误差对比
验证误差方向随姿态旋转:Δp = R · Δtcp
"""
fig, axes = plt.subplots(len(poses), 1,
figsize=(12, 4 * len(poses)))
if len(poses) == 1:
axes = [axes]
for idx, (pose_id, results) in enumerate(
all_results.items()):
ax = axes[idx]
error_comps = np.array(
results['error_components'])
offset_mags = results['offset_magnitudes']
x_pos = np.arange(len(offset_mags))
width = 0.25
ax.bar(x_pos - width, error_comps[:, 0],
width, label='ΔX', alpha=0.8, color='red')
ax.bar(x_pos, error_comps[:, 1],
width, label='ΔY', alpha=0.8, color='green')
ax.bar(x_pos + width, error_comps[:, 2],
width, label='ΔZ', alpha=0.8, color='blue')
ax.set_xlabel('TCP编号')
ax.set_ylabel('误差分量 (m)')
ax.set_title(
f'姿态 {pose_id}: '
f'J1={np.degrees(results["joint_angles"][0]):.0f}° '
f'J2={np.degrees(results["joint_angles"][1]):.0f}° '
f'J3={np.degrees(results["joint_angles"][2]):.0f}°\n'
f'误差方向由旋转矩阵R决定 (Δp = R·Δtcp)')
ax.set_xticks(x_pos)
ax.set_xticklabels(
[f"TCP{i+1}" for i in range(len(offset_mags))],
rotation=45)
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = f"{self.results_dir}/02_multi_pose_components.png"
plt.savefig(path, dpi=150)
plt.close()
return path
def plot_error_3d_vectors(self, results):
"""③ 误差向量三维分布"""
fig = plt.figure(figsize=(9, 7))
ax = fig.add_subplot(111, projection='3d')
error_comps = np.array(results['error_components'])
ax.quiver(np.zeros(len(error_comps)),
np.zeros(len(error_comps)),
np.zeros(len(error_comps)),
error_comps[:, 0],
error_comps[:, 1],
error_comps[:, 2],
length=1.0, normalize=False,
colors='steelblue', alpha=0.6)
ax.set_xlabel('ΔX (m)')
ax.set_ylabel('ΔY (m)')
ax.set_zlabel('ΔZ (m)')
ax.set_title('TCP偏移导致的末端位置误差向量(三维)')
plt.tight_layout()
path = f"{self.results_dir}/03_error_3d_vectors.png"
plt.savefig(path, dpi=150)
plt.close()
return path
def plot_end_positions_distribution(self, all_results, poses,
tcp_list, kinematics,
config):
"""
④ 不同TCP的末端位置分布(标称姿态下)
使用传入的kinematics和config,避免重建导致不一致
"""
# 使用第一个姿态
first_pose_id = list(poses.keys())[0]
results = all_results[first_pose_id]
fig = plt.figure(figsize=(9, 7))
ax = fig.add_subplot(111, projection='3d')
p_nominal = results['p_nominal']
ax.scatter([p_nominal[0]], [p_nominal[1]],
[p_nominal[2]], c='red', s=100,
marker='*', label='标称TCP', zorder=10)
xs, ys, zs = [], [], []
for tcp in tcp_list:
pos = kinematics.get_end_effector_position(
poses[first_pose_id], tcp)
xs.append(pos[0])
ys.append(pos[1])
zs.append(pos[2])
ax.scatter(xs[1:], ys[1:], zs[1:], c='steelblue',
s=30, alpha=0.6, label='实测/扰动TCP')
ax.set_xlabel('X (m)')
ax.set_ylabel('Y (m)')
ax.set_zlabel('Z (m)')
ax.set_title('不同TCP偏移下的末端位置分布(标称姿态)')
ax.legend()
plt.tight_layout()
path = f"{self.results_dir}/04_end_positions_distribution.png"
plt.savefig(path, dpi=150)
plt.close()
return path
5. 可视化要求
序号 图表文件 服务结论
①
"01_offset_vs_error.png" 验证
"\|\|Δp\|\| = \|\|Δtcp\|\|"恒等式:仿真点应落在y=x线上,拟合斜率≈1校核实现正确
②
"02_multi_pose_components.png" 多姿态下误差分量对比:验证同一Δtcp在不同姿态下误差方向不同(Δp = R·Δtcp)
③
"03_error_3d_vectors.png" 误差向量三维分布:直观展示误差方向的空间分布
④
"04_end_positions_distribution.png" 不同TCP末端位置散布:标称TCP为基准,实测/扰动TCP围绕其分布
6. README使用说明 + 核心知识点卡片
快速开始
pip install -r requirements.txt
python -m pytest tests/test_tcp.py -v # 3/3 测试通过
python run_tcp_analysis.py # 运行TCP分析
数据格式说明
# 修改标称TCP:编辑 RobotConfig.nominal_tcp
# 修改关节角姿态:编辑 run_tcp_analysis.py 中的 poses 字典
# 修改5把实测TCP:编辑 analyzer.get_five_real_tcps()
# 修改随机扰动范围:编辑 analyzer.generate_random_variations(max_deviation=)
参数 修改位置 说明
标称TCP
"TCPOffset(0, 0, 0.12)" 米
关节角
"poses"字典 弧度
扰动范围
"max_deviation=" 米(默认0.005)
随机种子
"seed=" 整数
📌 知识点卡片
TCP与正运动学(对应课本第2章2.1/2.2节、第3章3.2节)
- TCP附加变换:
"T_total = T_flange · T_tcp",TCP是末端法兰坐标系内的纯平移
- 误差传播:
"Δp = R · Δtcp",误差方向由法兰旋转矩阵决定
- 保范数恒等式:
"||Δp||₂ = ||Δtcp||₂",正交变换保长度,斜率=1是理论预期
- 姿态依赖性:同一TCP偏移在不同姿态下误差分量不同,标定需在典型姿态下验证
7. 总结与工程师思考
工业落地难处
一、TCP标定本身有误差。 现场四点法标定精度通常±0.5~1.0 mm,且标定结果依赖操作者的示教精度。本篇假设5把焊枪TCP已知,实际中它们本身就是待标定量。
二、焊枪磨损导致TCP动态漂移。 焊接过程中焊丝消耗、焊嘴磨损,TCP偏移量缓慢变化。本篇假设TCP固定,实际需定期重标定。
三、温度变形与TCP误差耦合。 焊接高温使连杆热膨胀,DH参数漂移,TCP误差与热变形叠加。纯运动学模型无法分离这两种误差源——昨天焊缝偏4 mm中约3 mm来自TCP批次公差,剩余~1 mm可能就是热变形贡献的。
工程师心得
老赵看着
"01_offset_vs_error.png"上完美贴合y=x线的散点:"斜率=1,跟理论预期完全一致——说明变换链没写错。
"||Δp|| = ||Δtcp||"这个恒等式验证了,但真正有意思的是
"02_multi_pose_components.png"。"
小周指着三种姿态下的分量柱状图:"J1从0°转到90°再到180°,同一把2号焊枪的误差分量完全变了——0°时ΔX最大,90°时ΔY最大,180°时ΔX反向。这就是
"Δp = R·Δtcp"的直观演示。"
"对。所以现场标定不能只在一个姿态下测。如果焊枪安装侧向偏差有2 mm,在某些姿态下这2 mm会完全投影到焊接方向(比如Z),导致焊缝偏移。多姿态标定才能把各个分量都暴露出来。"
8. 写作与风格限制
本篇全部代码为课程离线仿真,运行在普通PC上,用于TCP标定误差分析。代码中不包含任何真实机器人控制器实时运算或安全逻辑。 完全去营销,中立客观。拒绝论文式生硬开头,保持故事叙事感。全部物理量标注完整单位。
9. 文末附录
环境配置
pip install numpy>=1.24.0 matplotlib>=3.7.0
"requirements.txt":
numpy>=1.24.0
matplotlib>=3.7.0
运行说明
cd welding_tcp_offset
pip install -r requirements.txt
python -m pytest tests/ -v
python run_tcp_analysis.py
参数修改指南
想改什么 改哪里 示例
标称TCP
"TCPOffset(0, 0, 0.12)" 改dz=0.15
关节角姿态
"poses"字典 添加新姿态
5把实测TCP
"get_five_real_tcps()" 修改坐标值
扰动范围
"max_deviation=0.005" 改0.010
输出文件清单
文件 含义
"results/01_offset_vs_error.png" 偏移偏差 vs 位置误差(验证斜率=1)
"results/02_multi_pose_components.png" 多姿态误差分量对比
"results/03_error_3d_vectors.png" 误差向量三维分布
"results/04_end_positions_distribution.png" 末端位置散布
"output/tcp_error_data.csv" 详细误差数据
仿真结果摘要
统计项 数值 数据来源
标称TCP [0, 0, 0.12] m 配置
实测TCP组数 5
"get_five_real_tcps()"
随机扰动组数 15 参数
测试姿态数 3 poses字典
保范数恒等式最大偏差 < 1e-12 m
"norm_identity_check"
拟合斜率 ≈1.000000 01图
测试通过 3/3 pytest
正文完。以下为
"run_tcp_analysis.py" 完整主入口 +
"tests/test_tcp.py" 完整测试文件。
"run_tcp_analysis.py"
"""
焊接产线:多组TCP偏移下的正运动学误差分析 主入口
运行:python run_tcp_analysis.py
"""
import numpy as np
import os
import csv
import math
from robot.models import RobotConfig, TCPOffset
from robot.dh_kinem
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)