python的先进制造技术工业场景模拟第十三篇:读取机器人轨迹采样数据,计算轨迹各段运动速度,标记速度突变点位。
周五下午,机器人弧焊工作站。
"这周第三件了,"工艺工程师小李指着报废的焊接件,"焊缝起始段总有顿挫感——机器人不是平滑过渡,而是像'卡了一下'再继续走。结果起弧点堆焊,背面咬边。三个壳体,每个四百多,一千多块钱没了。"
我调出机器人离线编程软件的轨迹采样日志。屏幕上是一组组密集的坐标点:时间戳、X/Y/Z 位置(mm)、关节角度(°),采样频率 125Hz(每 8ms 一个点),一条 30 秒的焊接轨迹约 3750 行记录。
"软件里不是有轨迹回放吗?"我问。
"有,"小李点开 3D 仿真窗口,"但它是按'点'播放的——你只能看到机器人沿着路径在动。我想知道的是:轨迹上每一段的速度是多少、速度有没有突变、突变点在哪个位置。系统没有'速度剖面分析'这个功能——它只会显示当前点的瞬时速度,突变点藏在上千个点里,肉眼根本看不出来。"
"所以你每次调完工艺,都不知道速度曲线平不平滑?"
"对,"小李苦笑,"焊完发现起弧段有顿挫,调出轨迹回放——点太密了,看起来就是一条连续的线。我需要的是:把整条轨迹的速度算出来,画出速度曲线,标记出速度突变的位置——这样我就能知道是路径规划的问题还是加减速参数没调好。现在只能凭经验猜,猜完了再试焊,试一件废一件。"
"明白了,"我打开 VS Code,"你需要一个程序:读取机器人轨迹采样数据,用相邻两点间的距离除以时间间隔算出每个采样点的速度,然后用数值微分算加速度,找出加速度超过阈值的突变点。输出一张速度剖面图和突变点标记表。这样你一看图就知道速度在哪'卡了'。"
我敲了几行代码:
import pandas as pd
import numpy as np
# 读取轨迹数据
df = pd.read_csv("robot_trajectory.csv", parse_dates=["timestamp"])
# 计算位移
dx = np.diff(df["x_mm"])
dy = np.diff(df["y_mm"])
dz = np.diff(df["z_mm"])
dt = np.diff(df["timestamp"].astype("int64") // 1e9) # 秒
distances = np.sqrt(dx**2 + dy**2 + dz**2)
velocities = distances / dt # m/s
# 加速度(数值微分)
accelerations = np.diff(velocities) / dt[1:]
# 标记突变点(加速度绝对值 > 阈值)
jerk_threshold = 2.0 # m/s^3
jerk_mask = np.abs(accelerations) > jerk_threshold
"这只是核心逻辑,"我说,"完整版会用 OOP 封装:一个类管数据加载(解析时间戳、坐标列),一个类管数据清洗(去重、插值、滤波),一个类管速度计算(位移/时间 + 移动平均平滑),一个类管突变检测(加速度阈值 + 滑动窗口),一个类管可视化(3D 轨迹图 + 速度剖面 + 突变点标记)。数据自包含——用 numpy 合成一条含速度突变的 6 自由度机器人轨迹,读者下载就能跑。"
小李凑近屏幕:"所以你这东西,就是把几千个轨迹点变成一条速度曲线,速度突然跳变的地方标红——我一看就知道机器人'卡'在哪了。"
"对。而且还能做路径优化分析,"我补充,"比如用 NetworkX 把轨迹点连成图,节点是路径点,边权重是速度变化量,用最短路径算法找'最平滑路径'。机器人轨迹的调试不是'试焊看效果',是'用数据看速度剖面,在顿挫出现之前就修正路径'。"
一、实际应用场景(真实痛点)
场景设定:工业机器人(弧焊/喷涂/搬运)在执行任务时,控制系统以固定频率(如 125Hz)记录末端执行器的空间坐标和关节角度。一条 30 秒的轨迹产生约 3750 行采样数据。工艺工程师需要分析轨迹的速度剖面,识别速度突变点(可能导致振动、顿挫或焊缝缺陷)。常见痛点:数据按"点"密集采样,系统只提供 3D 轨迹回放,不提供速度计算和突变检测功能,依赖肉眼观察或试焊反馈。
现场原话(叙事化):
"我不是不想调好路径,"小李说,"每个点的位置都在系统里,8 毫秒一个点。但系统只会告诉我'机器人从这里走到了那里'——它不会告诉我走到中间的时候速度是不是突然变了。我想知道的是:这条轨迹上,速度在哪里突然跳了、跳了多少、是不是超过了机械臂的加减速能力。这些系统给不了我。焊完一看起弧段有顿挫——但那时候已经废了一件了。"
核心矛盾:"高密度空间坐标采样"与"工艺工程师需要的速度剖面与突变标记"之间的分析断层。需要一个"机器人轨迹速度分析与突变检测程序",用
"numpy" 计算位移和速度,用
"scipy" 做数值微分和滤波,用
"matplotlib" 绘制 3D 轨迹和速度剖面,用
"scikit-learn" 做异常检测,输出结构化的速度报告和突变点清单。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》模块 本篇痛点对应
工业机器人技术基础:机器人运动学与轨迹规划 速度剖面分析:基于采样数据的轨迹速度计算与平滑性评估。
先进制造技术基础:先进制造系统的运动控制 加减速优化:通过突变检测识别不合理的加减速段。
智能制造与数字孪生:机器人数字孪生 轨迹可视化:3D 空间轨迹 + 速度颜色映射。
先进制造新模式:数据驱动的工艺优化 根因定位:突变点关联路径段,指导路径重规划。
一句话总结:我们需要构建一个"机器人轨迹速度分析与突变检测程序",用
"numpy" 向量化计算位移和速度,用
"scipy" 做数值微分和滤波,用
"matplotlib" 绘制 3D 轨迹和速度剖面,用
"scikit-learn" 做突变检测,实现从密集坐标点到速度突变报告的转化。
三、核心逻辑讲解(大白话)
3.1 问题本质:把机器人轨迹想象成"汽车行车记录仪"
把机器人的轨迹采样数据想象成"你开车时的 GPS 轨迹":
* 每个采样点 = GPS 定位点:每 8 米记录一次你的位置(x, y, z)。
* 速度 = 两点之间距离 ÷ 时间差:GPS 点之间距离 10 米,时间差 1 秒 → 速度 10 m/s(36 km/h)。
* 加速度 = 速度变化 ÷ 时间差:上一秒 10 m/s,这一秒 15 m/s → 加速度 5 m/s²。
* 速度突变 = 急刹车/急加速:加速度突然超过 3 m/s² → 乘客会前倾/后仰,对应机器人末端"顿挫"。
* 3D 轨迹图 = 行车路线地图:用颜色表示速度——绿色是顺畅,红色是急变速区域。
工业应用:
* numpy 向量化位移计算:
"np.sqrt(np.diff(x)**2 + np.diff(y)**2 + np.diff(z)**2)" 一行代码算出所有相邻点之间的距离,比 for 循环快百倍。
* scipy 数值微分:
"scipy.gradient()" 自动处理不等间距采样,比手动差分更精确。
* scikit-learn 异常检测:
"IsolationForest" 自动识别速度突变点,不需要手动设阈值。
3.2 业务逻辑 → 代码映射
定义机器人轨迹数据模型
│
▼ TrajectoryDataLoader (pandas)
加载轨迹 CSV:
pd.read_csv("robot_trajectory.csv", parse_dates=["timestamp"])
解析时间戳、X/Y/Z 坐标、关节角度
│
▼ DataCleaner (pandas + scipy)
数据清洗:
去重(相同时间戳)
插值(缺失坐标)
scipy.signal.savgol_filter 平滑噪声
│
▼ VelocityCalculator (numpy + scipy)
速度计算:
位移 = sqrt(dx² + dy² + dz²)
速度 = 位移 / dt
scipy.gradient 计算平滑速度
│
▼ JerkDetector (numpy + scikit-learn)
突变检测:
加速度 = np.diff(velocity) / dt
阈值法:|acceleration| > threshold
IsolationForest 异常检测
标记突变点索引
│
▼ TrajectoryVisualizer (matplotlib + networkx)
可视化:
1. 3D 轨迹图(颜色映射速度)
2. 速度剖面曲线 + 突变点标注
3. 加速度分布直方图
4. NetworkX 路径图(节点=采样点,边权重=速度变化)
│
▼ SyntheticDataGenerator (numpy)
合成数据生成:
用 numpy 生成含平滑段 + 突变段的 3D 轨迹
确保数据自包含、可复现
3.3 为什么用向量化而不是逐点循环?
* 问题:3750 行轨迹数据,逐点计算距离和速度——代码冗长,速度慢。
* 处理策略:
"numpy" 向量化操作,底层 C 实现,对 3750 个点求距离只需微秒级。
* 工程合理性:高速采样(500Hz)下,一条轨迹可能有数万点,向量化是唯一选择。
3.4 分析前后对比
维度 3D 轨迹回放 Python 程序分析
判断依据 肉眼看"有没有顿挫" 精确计算加速度
效率 回放完才知道 秒级出速度曲线
突变定位 不知道在哪一段 精确标记突变点索引
根因分析 猜 突变点关联路径段
优化指导 无 输出突变位置供路径重规划
四、OOP 代码实现
4.1 项目结构
robot_trajectory_analyzer/
├── robot_trajectory_analyzer/
│ ├── __init__.py
│ ├── data_loader.py # 轨迹数据加载
│ ├── data_cleaner.py # 数据清洗与平滑
│ ├── velocity_calculator.py # 速度计算
│ ├── jerk_detector.py # 突变检测
│ ├── visualizer.py # 可视化
│ └── synthetic_data.py # 合成数据生成
├── tests/
│ ├── __init__.py
│ └── test_trajectory_analyzer.py # 单元测试
├── results/
│ ├── velocity_profile.csv # 速度剖面数据
│ ├── jerk_points.csv # 突变点清单
│ ├── trajectory_3d.png # 3D 轨迹图
│ ├── velocity_profile.png # 速度剖面曲线
│ ├── acceleration_hist.png # 加速度分布图
│ └── trajectory_report.txt # 文本分析报告
└── run_trajectory.py # 主程序入口
4.2 核心源码
<details>
<summary></summary>
"""机器人轨迹数据加载器"""
import pandas as pd
from pathlib import Path
from typing import Optional
class TrajectoryDataLoader:
"""
机器人轨迹数据加载器
支持 CSV 格式的空间坐标采样数据。
"""
def __init__(self, filepath: str, encoding: str = "utf-8"):
self.filepath = Path(filepath)
self.encoding = encoding
self._raw_df: Optional[pd.DataFrame] = None
def load(self,
timestamp_col: str = "timestamp",
x_col: str = "x_mm",
y_col: str = "y_mm",
z_col: str = "z_mm",
j1_col: str = "j1_deg",
j2_col: str = "j2_deg",
j3_col: str = "j3_deg") -> pd.DataFrame:
"""
加载轨迹数据
Parameters
----------
timestamp_col : str
时间戳列名
x_col, y_col, z_col : str
X/Y/Z 坐标列名
j1_col, j2_col, j3_col : str
关节角度列名
Returns
-------
pd.DataFrame
"""
if not self.filepath.exists():
raise FileNotFoundError(f"文件不存在: {self.filepath}")
self._raw_df = pd.read_csv(
self.filepath,
encoding=self.encoding,
parse_dates=[timestamp_col],
)
# 列名标准化
col_aliases = {
timestamp_col: ["时间", "timestamp", "time", "t"],
x_col: ["X", "x", "x_mm", "pos_x", "X_mm"],
y_col: ["Y", "y", "y_mm", "pos_y", "Y_mm"],
z_col: ["Z", "z", "z_mm", "pos_z", "Z_mm"],
j1_col: ["J1", "j1", "j1_deg", "joint1"],
j2_col: ["J2", "j2", "j2_deg", "joint2"],
j3_col: ["J3", "j3", "j3_deg", "joint3"],
}
rename_map = {}
for target, aliases in col_aliases.items():
if target not in self._raw_df.columns:
for alias in aliases:
if alias in self._raw_df.columns:
rename_map[alias] = target
break
if rename_map:
self._raw_df = self._raw_df.rename(columns=rename_map)
return self._raw_df.copy()
def get_raw_data(self) -> Optional[pd.DataFrame]:
return self._raw_df.copy() if self._raw_df is not None else None
</details>
<details>
<summary></summary>
"""轨迹数据清洗与平滑"""
import numpy as np
import pandas as pd
from scipy.signal import savgol_filter
from typing import Optional, Tuple
class DataCleaner:
"""
机器人轨迹数据清洗器
去重、插值、Savitzky-Golay 平滑。
"""
def __init__(self,
window_length: int = 11,
polyorder: int = 3):
self.window_length = window_length
self.polyorder = polyorder
def clean(self,
df: pd.DataFrame,
timestamp_col: str = "timestamp",
x_col: str = "x_mm",
y_col: str = "y_mm",
z_col: str = "z_mm") -> pd.DataFrame:
"""
清洗轨迹数据
Parameters
----------
df : pd.DataFrame
原始数据
timestamp_col : str
时间戳列名
x_col, y_col, z_col : str
坐标列名
Returns
-------
pd.DataFrame
"""
result = df.copy()
# 时间戳处理
if timestamp_col in result.columns:
result[timestamp_col] = pd.to_datetime(
result[timestamp_col], errors="coerce"
)
result = result.dropna(subset=[timestamp_col])
result = result.sort_values(timestamp_col).reset_index(drop=True)
# 去重
result = result.drop_duplicates(subset=[timestamp_col])
# 坐标数值化
for col in [x_col, y_col, z_col]:
if col in result.columns:
result[col] = pd.to_numeric(result[col], errors="coerce")
# 删除坐标全为空
coord_cols = [c for c in [x_col, y_col, z_col] if c in result.columns]
result = result.dropna(subset=coord_cols, how="all")
return result.reset_index(drop=True)
def smooth(self,
df: pd.DataFrame,
x_col: str = "x_mm",
y_col: str = "y_mm",
z_col: str = "z_mm") -> pd.DataFrame:
"""
对坐标进行 Savitzky-Golay 平滑
Parameters
----------
df : pd.DataFrame
清洗后的数据
x_col, y_col, z_col : str
坐标列名
Returns
-------
pd.DataFrame
"""
result = df.copy()
# 确保窗口长度为奇数
n = len(result)
window = min(self.window_length, n - 1)
if window % 2 == 0:
window -= 1
if window < 3:
return result
for col in [x_col, y_col, z_col]:
if col in result.columns:
try:
result[col] = savgol_filter(
result[col].values, window, self.polyorder
)
except ValueError:
pass # 数据不足,跳过平滑
return result
</details>
<details>
<summary></summary>
"""速度计算"""
import numpy as np
import pandas as pd
from scipy.gradient import gradient
from typing import Dict, Optional
class VelocityCalculator:
"""
机器人轨迹速度计算器
基于位移/时间差计算速度,使用 scipy.gradient 计算平滑速度。
"""
def __init__(self):
pass
def calculate(self,
df: pd.DataFrame,
timestamp_col: str = "timestamp",
x_col: str = "x_mm",
y_col: str = "y_mm",
z_col: str = "z_mm") -> pd.DataFrame:
"""
计算速度剖面
Parameters
----------
df : pd.DataFrame
清洗后的轨迹数据
timestamp_col : str
时间戳列名
x_col, y_col, z_col : str
坐标列名
Returns
-------
pd.DataFrame
新增 distance, velocity, velocity_smooth 列
"""
result = df.copy()
n = len(result)
if n < 2:
return result
# 时间差(秒)
timestamps = pd.to_datetime(result[timestamp_col]).astype("int64") // 1e9
dt = np.diff(timestamps.astype(float))
dt = np.append(dt, dt[-1]) # 最后一个点复制前一个 dt
# 位移
dx = np.diff(result[x_col].values, prepend=result[x_col].values[0])
dy = np.diff(result[y_col].values, prepend=result[y_col].values[0])
dz = np.diff(result[z_col].values, prepend=result[z_col].values[0])
# 距离
distances = np.sqrt(dx**2 + dy**2 + dz**2)
distances[0] = 0.0 # 第一个点距离为 0
# 速度 = 距离 / 时间
velocities = np.zeros(n)
velocities[1:] = distances[1:] / dt[1:]
# 平滑速度(scipy.gradient)
vel_smooth = np.zeros(n)
if n > 1:
vel_smooth = gradient(distances, timestamps)
result["distance_mm"] = distances
result["velocity_mm_s"] = velocities
result["velocity_smooth_mm_s"] = vel_smooth
return result
def get_summary(self, df: pd.DataFrame) -> Dict:
"""速度统计摘要"""
if "velocity_smooth_mm_s" not in df.columns:
return {}
v = df["velocity_smooth_mm_s"].values
v = v[v > 0] # 排除静止点
if len(v) == 0:
return {}
return {
"max_velocity": float(np.max(v)),
"min_velocity": float(np.min(v)),
"mean_velocity": float(np.mean(v)),
"std_velocity": float(np.std(v)),
"median_velocity": float(np.median(v)),
}
</details>
<details>
<summary></summary>
"""速度突变检测"""
import numpy as np
import pandas as pd
from sklearn.ensemble import IsolationForest
from typing import Dict, Optional
class JerkDetector:
"""
机器人轨迹速度突变检测器
基于加速度阈值和 Isolation Forest 异常检测。
"""
def __init__(self,
jerk_threshold: float = 1000.0, # mm/s^3
contamination: float = 0.05):
self.jerk_threshold = jerk_threshold
self.contamination = contamination
def detect(self,
df: pd.DataFrame,
timestamp_col: str = "timestamp",
velocity_col: str = "velocity_smooth_mm_s") -> pd.DataFrame:
"""
检测速度突变点
Parameters
----------
df : pd.DataFrame
含速度数据的 DataFrame
timestamp_col : str
时间戳列名
velocity_col : str
平滑速度列名
Returns
-------
pd.DataFrame
新增 acceleration, is_jerk_threshold, is_jerk_ml 列
"""
result = df.copy()
n = len(result)
if n < 3:
return result
# 时间差
timestamps = pd.to_datetime(result[timestamp_col]).astype("int64") // 1e9
dt = np.diff(timestamps.astype(float))
dt = np.append(dt, dt[-1])
# 加速度(数值微分)
velocities = result[velocity_col].values
acceleration = np.zeros(n)
acceleration[1:] = np.diff(velocities) / dt[1:]
result["acceleration_mm_s2"] = acceleration
# 加加速度(jerk)= 加速度变化率
jerk = np.zeros(n)
jerk[1:] = np.diff(acceleration) / dt[1:]
result["jerk_mm_s3"] = jerk
# 阈值法
result["is_jerk_threshold"] = np.abs(jerk) > self.jerk_threshold
# 机器学习异常检测
if n > 10:
features = np.column_stack([
velocities,
acceleration,
jerk,
])
# 去除 NaN
mask = ~np.isnan(features).any(axis=1)
if mask.sum() > 5:
clf = IsolationForest(
contamination=self.contamination,
random_state=42,
)
pred = clf.fit_predict(features[mask])
is_anomaly = np.zeros(n, dtype=bool)
is_anomaly[mask] = pred == -1
result["is_jerk_ml"] = is_anomaly
else:
result["is_jerk_ml"] = False
else:
result["is_jerk_ml"] = False
return result
def get_jerk_points(self, df: pd.DataFrame) -> pd.DataFrame:
"""获取所有突变点"""
mask = df["is_jerk_threshold"] | df.get("is_jerk_ml", False)
return df[mask].copy()
def summary(self, df: pd.DataFrame) -> Dict:
"""突变摘要"""
jerk_points = self.get_jerk_points(df)
return {
"total_points": len(df),
"jerk_points_threshold": df["is_jerk_threshold"].sum(),
"jerk_points_ml": df.get("is_jerk_ml", pd.Series(False)).sum(),
"jerk_percentage": round(len(jerk_points) / len(df) * 100, 2),
}
</details>
<details>
<summary></summary>
"""轨迹数据可视化"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import networkx as nx
from pathlib import Path
from typing import Optional
plt.rcParams["font.sans-serif"] = ["SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
class TrajectoryVisualizer:
"""机器人轨迹可视化"""
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def plot_3d_trajectory(self,
df: pd.DataFrame,
x_col: str = "x_mm",
y_col: str = "y_mm",
z_col: str = "z_mm",
velocity_col: str = "velocity_smooth_mm_s",
title: str = "3D 轨迹(颜色映射速度)") -> None:
"""绘制 3D 轨迹图,颜色映射速度"""
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(111, projection="3d")
x = df[x_col].values
y = df[y_col].values
z = df[z_col].values
v = df[velocity_col].values
# 颜色映射
scatter = ax.scatter(x, y, z, c=v, cmap="viridis", s=10, alpha=0.8)
ax.set_xlabel("X (mm)")
ax.set_ylabel("Y (mm)")
ax.set_zlabel("Z (mm)")
ax.set_title(title, fontsize=14, fontweight="bold")
cbar = plt.colorbar(scatter, ax=ax, shrink=0.6)
cbar.set_label("速度 (mm/s)")
plt.tight_layout()
plt.savefig(self.results_dir / "trajectory_3d.png",
dpi=150, bbox_inches="tight")
plt.close()
def plot_velocity_profile(self,
df: pd.DataFrame,
timestamp_col: str = "timestamp",
velocity_col: str = "velocity_smooth_mm_s",
jerk_col: str = "is_jerk_threshold",
title: str = "速度剖面曲线") -> None:
"""绘制速度剖面 + 突变点标注"""
fig, ax = plt.subplots(figsize=(16, 6))
timestamps = df[timestamp_col].values
velocities = df[velocity_col].values
# 速度曲线
ax.plot(timestamps, velocities, linewidth=0.8, color="#3498DB",
alpha=0.8, label="速度")
# 突变点标注
if jerk_col in df.columns:
jerk_mask = df[jerk_col]
jerk_times = timestamps[jerk_mask]
jerk_vels = velocities[jerk_mask]
ax.scatter(jerk_times, jerk_vels, color="#E74C3C", s=30,
zorder=5, label="突变点")
ax.set_xlabel("时间", fontsize=12)
ax.set_ylabel("速度 (mm/s)", fontsize=12)
ax.set_title(title, fontsize=14, fontweight="bold")
ax.legend()
ax.grid(alpha=0.3)
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.savefig(self.results_dir / "velocity_profile.png",
dpi=150, bbox_inches="tight")
plt.close()
def plot_acceleration_hist(self,
df: pd.DataFrame,
accel_col: str = "acceleration_mm_s2",
title: str = "加速度分布") -> None:
"""绘制加速度分布直方图"""
fig, ax = plt.subplots(figsize=(10, 6))
accel = df[accel_col].values
accel = accel[~np.isnan(accel) & (accel != 0)]
ax.hist(accel, bins=50, color="#3498DB", edgecolor="white",
alpha=0.85)
ax.axvline(x=0, color="#E74C3C", linestyle="-", linewidth=1, alpha=0.5)
ax.set_xlabel("加速度 (mm/s²)", fontsize=12)
ax.set_ylabel("频次", fontsize=12)
ax.set_title(title, fontsize=14, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir / "acceleration_hist.png",
dpi=150, bbox_inches="tight")
plt.close()
def plot_path_graph(self,
df: pd.DataFrame,
x_col: str = "x_mm",
y_col: str = "y_mm",
jerk_col: str = "is_jerk_threshold",
title: str = "路径拓扑图(边权重=速度变化)") -> None:
"""用 NetworkX 绘制路径拓扑图"""
n = min(len(df), 100) # 限制节点数
if n < 2:
return
G = nx.Graph()
# 采样点
step = max(1, len(df) // n)
sampled = df.iloc[::step].head(n)
for i in range(n):
G.add_node(i, pos=(sampled.iloc[i][x_col], sampled.iloc[i][y_col]))
# 添加边
for i in range(n - 1):
G.add_edge(i
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)