机器人离线编程误差建模与现场偏差估算——用统计学习把"虚拟轨迹"校准到"真实世界"

 

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

 

时间:周三上午 9:00,焊接机器人工作站。

 

地点:某工程机械结构件车间,一台 6 轴弧焊机器人刚执行完离线编程(OLP)生成的焊接程序。现场工程师小赵拿着激光跟踪仪,逐个测量焊缝实际位置与理论位置的偏差。屏幕上跳动的数字让他皱眉:

 

"第 3 段焊缝,理论坐标 X=1250.0, Y=830.5, Z=420.0,实测 X=1253.2, Y=828.1, Z=419.3——偏差 3.8mm。客户要求 ≤1.5mm。"小赵把数据记在本子上,"这已经是今天第 7 个超差点了。离线编程软件里仿真一切正常,导出程序直接跑,结果到处偏。我得一个点一个点地手动修正 TCP(工具中心点)。这条焊缝全长 2.4 米,有 40 多个示教点,每个点都要激光跟踪仪测一遍再改程序——今天一整天又搭进去了。"

 

"问题出在哪?"我问。

 

"离线编程的虚拟模型和现场实际机器人之间存在系统误差。"小赵放下本子,"机器人底座安装偏差、工具 TCP 标定的微小误差、工件夹具定位误差、甚至机器人各轴减速器背隙——这些在离线软件里都是零,但现场全都有。离线程序直接跑,轨迹就是飘的。"

 

"那你们现在怎么处理?"我问。

 

"三点法标定 TCP,然后靠激光跟踪仪逐点测量、手动补偿。一条 2.4 米的焊缝,补偿完要 4 个小时。如果换个工位或者换把焊枪,又得重来。"

 

"你需要的不是更好的标定工具,是一个误差预测模型。"我说,"你手头应该有历史数据——过去半年每条焊缝的离线理论坐标、现场实测坐标、当时的标定参数(TCP 位置、基座标系偏差、工件摆放角度)。用这些数据训练一个模型,输入新的离线程序坐标 + 当前标定状态,直接输出'预测偏差'和'补偿后的修正坐标'。这样你只需要抽测几个关键点验证模型,不用逐点测量了。"

 

"用 Python 能搞?"小赵问。

 

"pandas 加载历史误差数据集,numpy 做坐标变换矩阵运算,scipy 做误差分布的统计检验(确认是系统误差还是随机误差),scikit-learn 的 MultiOutputRegressor + RandomForest 做多输出回归(同时预测 ΔX/ΔY/ΔZ 三个方向的偏差),matplotlib 画误差空间分布 3D 散点图 + 偏差热力图 + 残差诊断,networkx 建'误差源-偏差方向'相关网络定位主要误差贡献源。纯 Python,不依赖任何商业离线编程软件。"

 

"那我现有的激光跟踪仪数据能直接用?"小赵问。

 

"能。你把过去半年的测量记录导出来,CSV 格式,包含每段焊缝的理论坐标、实测坐标、TCP 标定参数、基座标系偏移量、工件类型——喂给模型就行。第一次跑可能精度一般,但随着数据积累会越来越准。"

 

二、痛点分析(映射到滨州职业学院课程模型)

 

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

工业机器人技术基础 机器人运动学标定、TCP 标定、基座标系建立、轨迹精度

数控加工与 CAD/CAM 技术 刀路/轨迹生成与精度补偿

柔性制造系统 FMS 与先进生产管理 机器人工作站快速换产、程序复用性

智能制造与数字孪生 虚拟-实际偏差建模、数字孪生校准

先进制造新模式 数据驱动的自适应编程(减少人工示教)

 

一句话总结:本篇解决一个"给定机器人离线编程误差数据集(理论坐标 + 实测坐标 + 标定参数 + 工件信息)→ 建立偏差预测模型 → 输入新离线程序 → 输出预测偏差与补偿坐标 → 可视化误差分布"的精度补偿问题,映射到课程就是 工业机器人标定误差建模 + 数字孪生虚实校准 + 自适应轨迹补偿。

 

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

 

3.1 问题本质:把离线编程想成"在手机导航上规划路线,但GPS飘了50米"

 

离线编程软件里的机器人模型是"理想世界"——底座安装绝对水平、TCP 零误差、工件摆放分毫不差。但现场是"真实世界"——底座歪了 0.3°、TCP 偏了 1.2mm、夹具每次夹紧位置有 0.5mm 散差。所以离线生成的轨迹在真实机器人上跑,就像导航让你直行但你实际在旁边车道——方向是对的,但位置偏了。

 

偏差不是随机的。它有几个"固定套路":

 

- 平移偏差:整个轨迹系统性偏移(底座安装偏差 + 基座标系未对准)

- 旋转偏差:离机器人底座越远,偏差越大(底座倾角导致)

- 工具相关偏差:换焊枪/换抓手后偏差模式变了(TCP 标定误差)

- 工件相关偏差:不同夹具/不同工件类型,偏差方向不同

 

3.2 业务逻辑 → 代码映射

 

输入: 机器人离线编程误差数据集 (CSV)

  文件: olp_error_dataset.csv

  列: 理论坐标(X,Y,Z), 实测坐标(X,Y,Z), TCP标定参数,

      基座标系偏移, 工件类型, 机器人轴角度(J1~J6)

   │

   ▼ models.py (dataclass)

数据模型:

  RobotPose(理论位姿) / MeasuredPose(实测位姿)

  CalibrationParams(TCP/基座标) / ErrorRecord(误差记录)

  ErrorPrediction(预测偏差)

   │

   ▼ data_loader.py (pandas)

数据加载器:

  读 CSV → 计算偏差向量(ΔX,ΔY,ΔZ) → 计算偏差幅值

   → 异常值剔除(3σ)

   │

   ▼ error_model.py (sklearn + scipy)

误差建模:

  1. 特征工程: 坐标位置 + 距离因子 + 轴角度 + 标定参数

  2. MultiOutputRegressor(RandomForest): 预测 ΔX,ΔY,ΔZ

  3. 误差分解: 平移分量 + 旋转分量 (通过线性回归分离)

  4. 统计检验: Shapiro-Wilk检验偏差正态性 (scipy)

   │

   ▼ compensator.py

补偿器:

  输入离线坐标 → 模型预测偏差 → 反向补偿 → 输出修正坐标

   │

   ▼ visualizer.py (matplotlib + networkx)

可视化:

  1. 误差空间分布 3D 散点 (颜色=偏差幅值)

  2. 偏差方向玫瑰图 (ΔX/ΔY/ΔZ 分布)

  3. 偏差 vs 距离散点图 (验证旋转误差假设)

  4. 误差源相关网络 (networkx)

  5. 补偿前后对比图

   │

   ▼ 输出

  预测偏差 + 补偿坐标 + 误差分析报告

 

3.3 为什么"逐点激光跟踪仪测量"不够用

 

维度 逐点测量补偿 误差预测模型

时间 每点 3~5 分钟 批量秒级预测

一致性 依赖操作工经验 模型一致输出

换产 重新测量 加载对应工件类型模型

误差分解 无法区分平移/旋转 模型自动分离

积累效应 每次从零开始 数据越多越准

 

四、OOP 代码实现

 

4.1 项目结构

 

robot_olp_error/

├── robot_olp_error/

│ ├── __init__.py

│ ├── models.py # 数据模型

│ ├── data_loader.py # CSV加载

│ ├── error_model.py # 误差建模

│ ├── compensator.py # 补偿器

│ ├── visualizer.py # 可视化

│ └── report_generator.py # 分析报告

├── tests/

│ ├── __init__.py

│ └── test_olp_error.py

├── sample_data/

│ └── olp_error_dataset.csv

├── results/

│ ├── error_3d_scatter.png

│ ├── error_rose.png

│ ├── error_vs_distance.png

│ ├── error_source_network.png

│ ├── compensation_compare.png

│ └── error_report.txt

├── run_olp_error.py

└── README.md

 

4.2 核心源码

 

<details><summary></summary>

 

"""机器人离线编程误差数据模型 (dataclass)。"""

 

from dataclasses import dataclass, field

from typing import List, Optional, Dict

import numpy as np

 

 

@dataclass

class RobotPose:

    """理论位姿 (离线编程输出)。"""

    x_mm: float = 0.0

    y_mm: float = 0.0

    z_mm: float = 0.0

    rx_deg: float = 0.0

    ry_deg: float = 0.0

    rz_deg: float = 0.0

    joint_angles: List[float] = field(default_factory=lambda: [0]*6)

 

    def to_array(self) -> np.ndarray:

        return np.array([self.x_mm, self.y_mm, self.z_mm])

 

    def to_feature_array(self, include_joints: bool = True) -> np.ndarray:

        """构建特征向量。"""

        features = [self.x_mm, self.y_mm, self.z_mm,

                   self.rx_deg, self.ry_deg, self.rz_deg]

        if include_joints:

            features.extend(self.joint_angles)

        return np.array(features)

 

 

@dataclass

class MeasuredPose:

    """实测位姿 (激光跟踪仪测量)。"""

    x_mm: float = 0.0

    y_mm: float = 0.0

    z_mm: float = 0.0

    rx_deg: float = 0.0

    ry_deg: float = 0.0

    rz_deg: float = 0.0

 

    def to_array(self) -> np.ndarray:

        return np.array([self.x_mm, self.y_mm, self.z_mm])

 

 

@dataclass

class CalibrationParams:

    """标定参数。"""

    tcp_x_mm: float = 0.0

    tcp_y_mm: float = 0.0

    tcp_z_mm: float = 0.0

    base_x_mm: float = 0.0

    base_y_mm: float = 0.0

    base_z_mm: float = 0.0

    base_rx_deg: float = 0.0

    base_ry_deg: float = 0.0

    base_rz_deg: float = 0.0

 

    def to_feature_array(self) -> np.ndarray:

        return np.array([

            self.tcp_x_mm, self.tcp_y_mm, self.tcp_z_mm,

            self.base_x_mm, self.base_y_mm, self.base_z_mm,

            self.base_rx_deg, self.base_ry_deg, self.base_rz_deg,

        ])

 

 

@dataclass

class ErrorRecord:

    """单条误差记录。"""

    record_id: str

    pose: RobotPose

    measured: MeasuredPose

    calib: CalibrationParams

    workpiece_type: str = ""

    program_name: str = ""

 

    @property

    def error_vector(self) -> np.ndarray:

        """偏差向量 (实测 - 理论)。"""

        return self.measured.to_array() - self.pose.to_array()

 

    @property

    def error_magnitude(self) -> float:

        """偏差幅值 (mm)。"""

        return np.linalg.norm(self.error_vector)

 

    @property

    def error_x(self) -> float:

        return self.measured.x_mm - self.pose.x_mm

 

    @property

    def error_y(self) -> float:

        return self.measured.y_mm - self.pose.y_mm

 

    @property

    def error_z(self) -> float:

        return self.measured.z_mm - self.pose.z_mm

 

    def to_feature_array(self, include_joints: bool = True) -> np.ndarray:

        """完整特征向量。"""

        pose_feat = self.pose.to_feature_array(include_joints)

        calib_feat = self.calib.to_feature_array()

        dist_from_base = np.linalg.norm(self.pose.to_array())

        return np.concatenate([pose_feat, calib_feat, [dist_from_base]])

 

    def to_target_array(self) -> np.ndarray:

        """目标: 偏差向量。"""

        return self.error_vector

 

 

@dataclass

class ErrorPrediction:

    """误差预测结果。"""

    predicted_error: np.ndarray = field(default_factory=lambda: np.zeros(3))

    compensated_pose: np.ndarray = field(default_factory=lambda: np.zeros(3))

    confidence_radius: float = 0.0

 

    @property

    def predicted_magnitude(self) -> float:

        return np.linalg.norm(self.predicted_error)

 

</details>

 

<details><summary></summary>

 

"""机器人离线编程误差数据加载器 (pandas)。"""

 

import pandas as pd

import numpy as np

from pathlib import Path

from typing import List, Tuple

from .models import RobotPose, MeasuredPose, CalibrationParams, ErrorRecord

 

 

class DataLoader:

    """加载离线编程误差数据集。"""

 

    def load_all(self, data_dir: str = "sample_data"

                 ) -> Tuple[np.ndarray, np.ndarray, List[ErrorRecord]]:

        """加载数据,返回 (X_features, y_errors, records)。"""

        data_path = Path(data_dir)

        csv_path = data_path / "olp_error_dataset.csv"

 

        if csv_path.exists():

            df = pd.read_csv(csv_path)

        else:

            df = self._generate_synthetic_data()

 

        # 清洗

        df = df.replace([np.inf, -np.inf], np.nan).dropna(

            subset=["theory_x", "theory_y", "theory_z",

                    "measured_x", "measured_y", "measured_z"])

        df = self._remove_outliers(df, "error_magnitude")

 

        records = []

        for _, row in df.iterrows():

            pose = RobotPose(

                x_mm=row["theory_x"], y_mm=row["theory_y"],

                z_mm=row["theory_z"],

                rx_deg=row.get("theory_rx", 0),

                ry_deg=row.get("theory_ry", 0),

                rz_deg=row.get("theory_rz", 0),

                joint_angles=[row.get(f"j{i+1}", 0) for i in range(6)],

            )

            measured = MeasuredPose(

                x_mm=row["measured_x"], y_mm=row["measured_y"],

                z_mm=row["measured_z"],

                rx_deg=row.get("measured_rx", 0),

                ry_deg=row.get("measured_ry", 0),

                rz_deg=row.get("measured_rz", 0),

            )

            calib = CalibrationParams(

                tcp_x_mm=row.get("tcp_x", 0),

                tcp_y_mm=row.get("tcp_y", 0),

                tcp_z_mm=row.get("tcp_z", 0),

                base_x_mm=row.get("base_x", 0),

                base_y_mm=row.get("base_y", 0),

                base_z_mm=row.get("base_z", 0),

                base_rx_deg=row.get("base_rx", 0),

                base_ry_deg=row.get("base_ry", 0),

                base_rz_deg=row.get("base_rz", 0),

            )

 

            rec = ErrorRecord(

                record_id=str(row.get("record_id", f"R{len(records)+1}")),

                pose=pose, measured=measured, calib=calib,

                workpiece_type=row.get("workpiece_type", "unknown"),

                program_name=row.get("program_name", ""),

            )

            records.append(rec)

 

        # 构建特征矩阵和目标矩阵

        X = np.array([r.to_feature_array() for r in records])

        y = np.array([r.to_target_array() for r in records])

 

        return X, y, records

 

    def _remove_outliers(self, df: pd.DataFrame,

                        column: str, sigma: float = 3.0) -> pd.DataFrame:

        """3σ 异常值剔除。"""

        if column not in df.columns:

            return df

        mean = df[column].mean()

        std = df[column].std()

        lower = mean - sigma * std

        upper = mean + sigma * std

        return df[(df[column] >= lower) & (df[column] <= upper)]

 

    def _generate_synthetic_data(self) -> pd.DataFrame:

        """生成合成误差数据 (模拟真实误差模式)。"""

        np.random.seed(42)

        records = []

 

        workpiece_types = ["flange", "bracket", "pipe", "frame"]

        programs = ["weld_seam_A", "weld_seam_B", "cut_path_C", "glue_D"]

 

        # 模拟系统误差参数

        base_offset = np.array([1.5, -0.8, 0.3]) # 基座标系偏移

        base_tilt = np.array([0.3, -0.2, 0.1]) # 底座倾角 (度)

        tcp_error = np.array([0.8, 1.2, -0.5]) # TCP标定误差

 

        sample_id = 1

        for wp in workpiece_types:

            for prog in programs:

                # 每条程序生成 15~25 个点

                n_points = np.random.randint(15, 26)

                for i in range(n_points):

                    # 理论坐标 (在工作空间内随机分布)

                    theory = np.random.uniform([-500, -300, 200],

                                              [1500, 1200, 800])

                    # 关节角度

                    joints = np.random.uniform(-120, 120, 6)

 

                    # 误差模型: 系统误差 + 距离相关(旋转) + 随机噪声

                    dist = np.linalg.norm(theory)

                    rotation_error = np.cross(

                        np.deg2rad(base_tilt), theory) * 1000 # 放大到mm级

 

                    systematic = base_offset + tcp_error

                    total_error = (systematic +

                                rotation_error / 500 +

                                np.random.normal(0, 0.5, 3))

 

                    measured = theory + total_error

 

                    # 标定参数 (每次有微小变化)

                    calib = {

                        "tcp_x": tcp_error[0] + np.random.normal(0, 0.1),

                        "tcp_y": tcp_error[1] + np.random.normal(0, 0.1),

                        "tcp_z": tcp_error[2] + np.random.normal(0, 0.1),

                        "base_x": base_offset[0] + np.random.normal(0, 0.05),

                        "base_y": base_offset[1] + np.random.normal(0, 0.05),

                        "base_z": base_offset[2] + np.random.normal(0, 0.05),

                        "base_rx": base_tilt[0] + np.random.normal(0, 0.02),

                        "base_ry": base_tilt[1] + np.random.normal(0, 0.02),

                        "base_rz": base_tilt[2] + np.random.normal(0, 0.02),

                    }

 

                    records.append({

                        "record_id": f"R{sample_id:04d}",

                        "theory_x": theory[0], "theory_y": theory[1],

                        "theory_z": theory[2],

                        "theory_rx": 0, "theory_ry": 0, "theory_rz": 0,

                        "measured_x": measured[0], "measured_y": measured[1],

                        "measured_z": measured[2],

                        "measured_rx": 0, "measured_ry": 0, "measured_rz": 0,

                        **calib,

                        "workpiece_type": wp,

                        "program_name": prog,

                        "j1": joints[0], "j2": joints[1], "j3": joints[2],

                        "j4": joints[3], "j5": joints[4], "j6": joints[5],

                        "error_magnitude": np.linalg.norm(total_error),

                    })

                    sample_id += 1

 

        df = pd.DataFrame(records)

        df.to_csv(Path("sample_data") / "olp_error_dataset.csv", index=False)

        return df

 

</details>

 

<details><summary></summary>

 

"""机器人误差建模 (sklearn + scipy)。"""

 

import numpy as np

from typing import Dict, List, Tuple

from sklearn.ensemble import RandomForestRegressor

from sklearn.multioutput import MultiOutputRegressor

from sklearn.model_selection import cross_val_score

from sklearn.metrics import mean_squared_error, r2_score

from scipy import stats

import warnings

warnings.filterwarnings("ignore")

 

from .models import ErrorRecord, ErrorPrediction

 

 

class ErrorModel:

    """

    机器人离线编程误差预测模型:

    1. RandomForest 多输出回归 (ΔX, ΔY, ΔZ)

    2. 误差分解: 平移 + 旋转分量

    3. 统计检验: 偏差分布正态性

    """

 

    def __init__(self, n_estimators: int = 100, random_state: int = 42):

        self.n_estimators = n_estimators

        self.random_state = random_state

        self.model_ = MultiOutputRegressor(

            RandomForestRegressor(n_estimators=n_estimators,

                                 random_state=random_state,

                                 max_depth=10),

            n_jobs=-1

        )

        self.is_fitted_ = False

        self.feature_names_ = None

        self.metrics_ = {}

 

    def fit(self, X: np.ndarray, y: np.ndarray,

            records: List[ErrorRecord] = None) -> dict:

        """拟合误差模型。"""

        self.model_.fit(X, y)

        self.is_fitted_ = True

 

        # 交叉验证

        cv_scores = cross_val_score(self.model_, X, y,

                                   cv=5, scoring='neg_root_mean_squared_error',

                                   n_jobs=-1)

 

        # 训练集评估

        y_pred = self.model_.predict(X)

        mse_per_dim = [mean_squared_error(y[:, i], y_pred[:, i])

                      for i in range(3)]

        r2_per_dim = [r2_score(y[:, i], y_pred[:, i])

                     for i in range(3)]

 

        self.metrics_ = {

            "cv_rmse_mean": -cv_scores.mean(),

            "cv_rmse_std": cv_scores.std(),

            "train_rmse_x": np.sqrt(mse_per_dim[0]),

            "train_rmse_y": np.sqrt(mse_per_dim[1]),

            "train_rmse_z": np.sqrt(mse_per_dim[2]),

            "train_r2_x": r2_per_dim[0],

            "train_r2_y": r2_per_dim[1],

            "train_r2_z": r2_per_dim[2],

            "train_r2_avg": np.mean(r2_per_dim),

        }

 

        return self.metrics_

 

    def predict(self, X: np.ndarray) -> np.ndarray:

        """预测偏差向量。"""

        if not self.is_fitted_:

            raise ValueError("模型尚未拟合")

        return self.model_.predict(X)

 

    def predict_single(self, record: ErrorRecord) -> ErrorPrediction:

        """单点预测与补偿。"""

        X = record.to_feature_array().reshape(1, -1)

        pred_error = self.predict(X)[0]

 

        # 补偿: 理论坐标 - 预测偏差

        theory = record.pose.to_array()

        compensated = theory - pred_error

 

        # 置信半径 (基于交叉验证 RMSE)

        conf_radius = self.metrics_.get("cv_rmse_mean", 2.0)

 

        return ErrorPrediction(

            predicted_error=pred_error,

            compensated_pose=compensated,

            confidence_radius=conf_radius,

        )

 

    def decompose_error(self, records: List[ErrorRecord]) -> Dict:

        """

        误差分解: 平移分量 vs 旋转分量。

        平移 = 所有点偏差的均值

        旋转 = 残差 (偏差 - 平移) 与距离的相关性

        """

        if not records:

            return {}

 

        all_errors = np.array([r.error_vector for r in records])

        positions = np.array([r.pose.to_array() for r in records])

        distances = np.linalg.norm(positions, axis=1)

 

        # 平移分量 (均值)

        translation = np.mean(all_errors, axis=0)

 

        # 残差

        residuals = all_errors - translation

 

        # 残差与距离的相关性 (旋转误差指标)

        residual_magnitudes = np.linalg.norm(residuals, axis=1)

        dist_corr, dist_p = stats.pearsonr(distances, residual_magnitudes)

 

        # 各方向残差统计

        residual_stats = {}

        for i, dim in enumerate(['X', 'Y', 'Z']):

            residual_stats[dim] = {

                'mean': np.mean(residuals[:, i]),

                'std': np.std(residuals[:, i]),

            }

 

        return {

            'translation': translation,

            'translation_magnitude': np.linalg.norm(translation),

            'residual_mean': np.mean(residual_magnitudes),

            'residual_std': np.std(residual_magnitudes),

            'distance_correlation': dist_corr,

            'distance_p_value': dist_p,

            'residual_stats': residual_stats,

        }

 

    def normality_test(self, records: List[ErrorRecord]) -> Dict:

        """Shapiro-Wilk 正态性检验。"""

        results = {}

        for i, dim in enumerate(['X', 'Y', 'Z']):

            errors = [r.error_vector[i] for r in records]

            stat, p = stats.shapiro(errors)

            results[dim] = {'statistic': stat, 'p_value': p,

                          'is_normal': p > 0.05}

        return results

 

</details>

 

<details><summary></summary>

 

"""机器人轨迹补偿器。"""

 

import numpy as np

from typing import Dict, List

from .models import ErrorRecord, ErrorPrediction

from .error_model import ErrorModel

 

 

class TrajectoryCompensator:

    """

    批量补偿离线编程轨迹:

    输入: 一组理论位姿 + 标定参数

    输出: 补偿后的实际坐标

    """

 

    def __init__(self, error_model: ErrorModel):

        self.error_model = error_model

 

    def compensate_trajectory(self, records: List[ErrorRecord]

                             ) -> List[ErrorPrediction]:

        """批量补偿。"""

        if not records:

            return []

 

        X = np.array([r.to_feature_array() for r in records])

        predictions = self.error_model.predict(X)

 

        results = []

        for i, rec in enumerate(records):

            pred_error = predictions[i]

            theory = rec.pose.to_array()

            compensated = theory - pred_error

            conf_radius = self.error_model.metrics_.get("cv_rmse_mean", 2.0)

 

            results.append(ErrorPrediction(

                predicted_error=pred_error,

                compensated_pose=compensated,

                confidence_radius=conf_radius,

            ))

 

        return results

 

    def compensate_single_pose(self, theory_x: float, theory_y: float,

                              theory_z: float,

                              joint_angles: List[float] = None,

                              calib_params: Dict = None

                              ) -> Dict:

        """单点位补偿 (便捷接口)。"""

        from .models import RobotPose, MeasuredPose, CalibrationParams, ErrorRecord

 

        if joint_angles is None:

            joint_angles = [0] * 6

        if calib_params is None:

            calib_params = {}

 

        pose = RobotPose(x_mm=theory_x, y_mm=theory_y, z_mm=theory_z,

                        joint_angles=joint_angles)

        measured = MeasuredPose() # 占位

        calib = CalibrationParams(**calib_params)

 

        rec = ErrorRecord(record_id="temp", pose=pose,

                         mea

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

Logo

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

更多推荐