python的机器人基础原理工业场景模拟第二十七篇:多工位连续搬运,设置多组关节路点,完成多段连续关节空间多项式轨迹插值。
《Python机器人基础原理》第二十七篇:多工位连续搬运——设置多组关节路点,完成多段连续关节空间多项式轨迹插值
1. 实际工业应用场景
周一早晨,某3C电子装配产线的上下料工位。一台4-DOF俯仰型关节臂+升降轴机械手(基座偏航θ₁ + 大臂俯仰θ₂ + 小臂俯仰θ₃ + 末端升降d₄)负责在三个工位之间连续转运手机中框零件。机器人构型参数:大臂长350 mm、质量2.8 kg(质心距基座0.18 m),小臂长250 mm、质量1.6 kg(质心距肘部0.12 m),升降轴行程150 mm、末端电磁夹爪0.5 kg。关节限位:θ₁±180°、最大角速度3.0 rad/s、最大角加速度8.0 rad/s²;θ₂±150°、最大角速度2.5 rad/s、最大角加速度6.0 rad/s²;θ₃±180°、最大角速度3.5 rad/s、最大角加速度10.0 rad/s²;d₄行程0~150 mm、最大线速度0.3 m/s、最大线加速度0.8 m/s²。
产线节拍要求:从取料工位A→中转工位B→装配工位C→待机位D,完成一个完整循环不超过6.0 s,且工位之间切换时机械臂不能停顿。
小赵拿到任务书:4个路点(取料→中转→装配→待机),每个路点的关节角度已知,需要生成连续平滑的多段轨迹。他翻出上篇写的单段五次多项式代码,试着把4个路点拆成3段独立轨迹,每段之间重新从静止启动、到静止停止,每段给3秒。
小李端着咖啡走过来:"你这每段3秒,3段就是9秒。产线节拍卡的是6秒,你超了50%。"
小赵一拍脑门:"……对。那我压缩一下,A→B给2秒,B→C给1.5秒,C→D给2.5秒,加起来正好6秒。"
"时间分配合理了。但你每段都从零速启动、零速停止?"
"对,上篇就是这么做的。"
"那你想过没有——A→B段结束时要停下来,B→C段又要重新加速。光加减速就浪费了多少时间?而且每段之间速度硬跳到零再跳上来,伺服驱动器每次都要重新建立位置环,末端夹爪上的零件会晃。"
小赵想了想:"那让段与段之间速度不归零?"
"对。多段连续轨迹,段与段之间位置连续是基本要求,速度连续才是关键。你需要在每个中间路点指定速度——或者让算法自动计算中间路点的速度,然后每段用满足边界条件的多项式拼接。"
本篇解决的问题:给定N个关节空间路点(位置),为每个中间路点自动计算连续速度(采用三次样条的速度边界条件),然后相邻路点之间用五次多项式插值,保证段间位置和速度连续,输出各关节多段连续轨迹的位置、速度、加速度曲线,全程校验是否超限。
⚠️ 声明:本篇全部代码为课程离线仿真,运行在普通PC上,用于离线多段轨迹规划校验。不可直接用于真实机器人控制器、伺服驱动器或产线设备。真实工业现场需结合实时插补、前瞻控制、软限位保护。代码作者不对直接使用本代码于真实设备造成的任何后果负责。
2. 痛点分析
课程模块 本篇对应的工程痛点
第6章 轨迹规划与控制 → 6.3 多项式轨迹规划 多段独立轨迹段间速度不连续→伺服频繁加减速→节拍损失+末端振动
第6章 轨迹规划与控制 → 6.4 样条插值 中间路点速度未知→需要自动计算(三次样条边界条件)→保证段间连续
第6章 轨迹规划与控制 → 6.2 速度规划 多段连续轨迹需全程校验速度/加速度是否超出硬件限位
本篇代码解决 L2 离线多段轨迹规划校验层。输入:N个关节路点位置、每段运动时间、采样频率。通过三次样条计算中间路点速度,相邻路点间用五次多项式插值,逐点计算位置/速度/加速度,全程校验限位。输出:多段连续轨迹曲线、段间连续性报告、限位校验报告。
不覆盖:在线轨迹修正(传感器反馈实时调整)、高阶连续(加加速度jerk连续→七次多项式)、时间最优分配(自动计算每段最短时间)、笛卡尔空间多段规划。
3. 核心逻辑讲解
物理直觉
多段连续搬运就像开车走城市快速路,中间经过多个匝道出入口。如果每到一个出入口你就把车完全刹停再重新起步,后面的车会按喇叭,你自己也浪费时间。正确的做法是:提前规划好每个匝道的速度,经过时不停车,只是稍微调整油门深浅。
单段五次多项式是"从家开车到公司,起点终点都停车"。多段连续轨迹是"从家出发,经过加油站、超市、朋友家,最后到公司——中间不停,但每个途经点的速度要提前算好,保证进出每个路段时车速匹配"。
中间路点速度怎么定?
中间路点的速度不是随便给的。工程上最常用的方法是三次样条插值:假设中间路点的加速度是连续的(即整条轨迹的二阶导数连续),利用这个条件和已知的起点/终点速度(通常为零),可以唯一确定每个中间路点的速度。
这本质上是求解一个三对角线性方程组。对每个内部路点i,加速度连续条件展开后得到方程:
t_i · v{i-1} + 2(t{i-1} + t_i) · v_i + t{i-1} · v{i+1} = 3[ (Δq_i/t_i)·t{i-1} + (Δq{i-1}/t_{i-1})·t_i ]
其中t_i是第i段的持续时间,Δq_i是第i段的关节角度变化量。左侧v{i-1}的系数是t_i(当前段时长),v_i的系数是2(t{i-1}+t_i),v{i+1}的系数是t{i-1}(前一段时长)。右侧是位置差分与时间的交叉加权组合。所有内部路点的方程联立构成三对角矩阵,用托马斯算法(Thomas algorithm)求解。
本篇的混合方案:样条速度 + 五次多项式(加速度边界为零)
设计选择说明:样条计算给出路点速度后,标准做法是每段用三次Hermite插值(位置+速度共4个边界条件,段间C²连续)。但本篇选择每段用五次多项式,并强制起止加速度为零。这是一个有意为之的工程折中:
- 好处:五次多项式求解简单,加速度在路点处被钳制为零,波形更"温和",计算量小
- 代价:样条推导假设加速度连续且取自然值,但零加速度边界人为覆盖了这个结果。加速度在路点处确实是连续的(两侧都是0),但这个"连续"是钳制出来的,不是样条推导的最优值。每段五次多项式为了同时满足非零端速度和零端加速度,内部加速度波形会比Hermite方案摆动更大,加速度峰值偏高
这是离线仿真中常见的简化策略。工业现场若对加速度波形敏感(如精密装配),应采用三次Hermite或七次多项式。
业务逻辑 → 代码映射
输入:路点位置 waypoints[n_wp][n_joints]、每段时间 segment_times[n_seg]、采样数 N
↓
对每个关节 j = 0,1,2(旋转关节)+ j=3(升降轴,单独处理单位):
步骤1:三次样条计算中间路点速度
构建三对角矩阵(系数如上)
边界条件:v[0] = 0, v[n_wp-1] = 0
托马斯算法求解 → 得到所有路点速度 v[i]
↓
步骤2:相邻路点间五次多项式插值
对每段 s = 0..n_seg-1:
边界条件:q(0) = wp[s], q(T) = wp[s+1]
q'(0) = v[s], q'(T) = v[s+1]
q''(0) = 0, q''(T) = 0
求解6个系数 → 逐点评估
↓
步骤3:拼接所有段,全程校验限位(旋转关节用rad/s,升降轴用m/s)
步骤4:段间连续性回归检查(验证浮点拼接精度)
↓
输出:连续多段位置/速度/加速度曲线、4张图表
关键公式文字翻译
五次多项式系数求解(连续边界版):与单段不同,这里起点和终点的速度不再为零,而是由样条计算给出。所以a₁ = v_start,a₂ = a_start/2(这里a_start=0)。剩下a₃、a₄、a₅由终点位置、终点速度、终点加速度(为零)三个条件联立求解3×3矩阵。
托马斯算法:三对角矩阵的前向消元将下对角线元素消去,变成上双对角矩阵;然后回代求解。时间复杂度O(n),比高斯消元O(n³)高效得多,适合路点数量较多的场景。
4. OOP代码实现
技术栈
严格限定:numpy, scipy, matplotlib。不引入pybullet、roboticstoolbox等任何第三方机器人库。
项目结构树
multi_segment_trajectory_sim/
├── run_multi_segment_trajectory_sim.py
├── requirements.txt
├── robot/
│ ├── __init__.py
│ ├── models.py
│ ├── spline_velocity.py
│ ├── trajectory_generator.py
│ └── visualize_trajectory.py
├── tests/
│ ├── __init__.py
│ └── test_multi_segment_trajectory_sim.py
└── results/
核心源码
"robot/models.py" — 数据模型
"""
机器人数据模型:关节参数、限位、路点定义
本篇包含3个旋转关节(rad) + 1个平移升降轴(m),单位分离处理
"""
from dataclasses import dataclass, field
from typing import List
@dataclass
class JointLimit:
"""单关节限位结构体"""
joint_name: str
min_value: float # 最小位置(旋转关节为rad,平移轴为m)
max_value: float # 最大位置
max_velocity: float # 最大速度(旋转关节为rad/s,平移轴为m/s)
max_acceleration: float # 最大加速度(旋转关节为rad/s²,平移轴为m/s²)
is_revolute: bool = True # True=旋转关节, False=平移轴
@dataclass
class JointLimitSet:
"""关节限位集合,4个关节:θ₁/θ₂/θ₃(旋转) + d₄(平移)"""
limits: List[JointLimit] = field(default_factory=list)
def check_all_positions(self, values: List[float]) -> tuple:
"""校验一组关节位置是否全部在限位内"""
violations = []
for i, val in enumerate(values):
if i < len(self.limits):
if val < self.limits[i].min_value or val > self.limits[i].max_value:
violations.append(i)
return len(violations) == 0, violations
def check_all_velocities(self, velocities: List[float]) -> tuple:
"""校验一组速度是否全部在限位内"""
violations = []
for i, vel in enumerate(velocities):
if i < len(self.limits):
if abs(vel) > self.limits[i].max_velocity:
violations.append(i)
return len(violations) == 0, violations
def check_all_accelerations(self, accelerations: List[float]) -> tuple:
"""校验一组加速度是否全部在限位内"""
violations = []
for i, acc in enumerate(accelerations):
if i < len(self.limits):
if abs(acc) > self.limits[i].max_acceleration:
violations.append(i)
return len(violations) == 0, violations
@dataclass
class Waypoint:
"""单个路点的关节位置(前3个为弧度,第4个为米)"""
positions: List[float]
label: str = ""
@dataclass
class SegmentTime:
"""每段运动时间分配"""
duration: float
"robot/spline_velocity.py" — 三次样条速度计算
"""
三次样条速度计算器
通过加速度连续条件求解中间路点速度
求解三对角线性方程组,使用托马斯算法(Thomas algorithm)
"""
import numpy as np
from typing import List
class CubicSplineVelocityCalculator:
"""
对每个关节独立计算路点速度
边界条件:起点和终点速度为零
内部路点满足加速度连续(二阶导数连续)
三对角矩阵系数:t_i·v_{i-1} + 2(t_{i-1}+t_i)·v_i + t_{i-1}·v_{i+1} = RHS
"""
def __init__(self, waypoint_positions: List[List[float]],
segment_times: List[float]):
"""
waypoint_positions: shape [n_waypoints, n_joints]
segment_times: shape [n_segments], n_segments = n_waypoints - 1
"""
self.waypoints = np.array(waypoint_positions)
self.segment_times = np.array(segment_times)
self.n_waypoints = self.waypoints.shape[0]
self.n_joints = self.waypoints.shape[1]
self.n_segments = len(segment_times)
if self.n_waypoints != self.n_segments + 1:
raise ValueError(
f"路点数({self.n_waypoints})必须等于段数+1({self.n_segments + 1})"
)
def compute_velocities(self) -> np.ndarray:
"""为每个关节计算所有路点的速度"""
velocities = np.zeros((self.n_waypoints, self.n_joints))
for j in range(self.n_joints):
velocities[:, j] = self._solve_single_joint(j)
return velocities
def _solve_single_joint(self, joint_idx: int) -> np.ndarray:
"""
对单个关节求解三对角系统
托马斯算法(Thomas algorithm / TDMA)
系数与文字公式严格一致:
v_{i-1}系数 = t_i
v_i系数 = 2(t_{i-1} + t_i)
v_{i+1}系数 = t_{i-1}
"""
n = self.n_waypoints
q = self.waypoints[:, joint_idx]
t = self.segment_times
# 主对角线a、下对角线b、上对角线c、右侧d
a = np.zeros(n)
b = np.zeros(n)
c = np.zeros(n)
d = np.zeros(n)
# 边界条件:v[0] = 0, v[n-1] = 0
a[0] = 1.0
d[0] = 0.0
a[n-1] = 1.0
d[n-1] = 0.0
for i in range(1, n-1):
a[i] = 2.0 * (t[i-1] + t[i]) # v_i的系数
b[i] = t[i] # v_{i-1}的系数(下对角线)
c[i] = t[i-1] # v_{i+1}的系数(上对角线)
d[i] = 3.0 * (
(q[i+1] - q[i]) / t[i] * t[i-1]
+ (q[i] - q[i-1]) / t[i-1] * t[i]
)
# 托马斯算法前向消元
for i in range(1, n):
w = b[i] / a[i-1]
a[i] = a[i] - w * c[i-1]
d[i] = d[i] - w * d[i-1]
# 回代
v = np.zeros(n)
v[n-1] = d[n-1] / a[n-1]
for i in range(n-2, -1, -1):
v[i] = (d[i] - c[i] * v[i+1]) / a[i]
return v
"robot/trajectory_generator.py" — 多段五次多项式轨迹生成
"""
多段五次多项式轨迹生成器
相邻路点间用五次多项式插值,段间位置和速度连续
混合方案:样条速度 + 零加速度边界(工程折中,非最优C²连续)
"""
import numpy as np
from typing import List, Tuple, Dict
from robot.models import JointLimitSet
from robot.spline_velocity import CubicSplineVelocityCalculator
class QuinticSegmentTrajectory:
"""
单段五次多项式轨迹(连续边界版)
边界条件:起止位置、起止速度(非零)、起止加速度(强制为零)
"""
def __init__(self, start_pos: float, end_pos: float,
start_vel: float, end_vel: float,
start_acc: float, end_acc: float,
duration: float):
if duration <= 0:
raise ValueError(f"段时间必须大于0: {duration}")
self.T = duration
self.coeffs = self._solve(start_pos, end_pos,
start_vel, end_vel,
start_acc, end_acc)
def _solve(self, q0, qT, v0, vT, a0, aT) -> np.ndarray:
"""求解五次多项式系数"""
a0_coeff = q0
a1_coeff = v0
a2_coeff = a0 / 2.0
T = self.T
M = np.array([
[T**3, T**4, T**5],
[3*T**2, 4*T**3, 5*T**4],
[6*T, 12*T**2, 20*T**3]
])
b = np.array([
qT - (a0_coeff + a1_coeff*T + a2_coeff*T**2),
vT - (a1_coeff + 2*a2_coeff*T),
aT - (2*a2_coeff)
])
try:
a3_a4_a5 = np.linalg.solve(M, b)
except np.linalg.LinAlgError:
raise ValueError(f"段轨迹求解失败:系数矩阵奇异,段时间T={T}可能过小")
return np.array([a0_coeff, a1_coeff, a2_coeff,
a3_a4_a5[0], a3_a4_a5[1], a3_a4_a5[2]])
def evaluate(self, t: float) -> Tuple[float, float, float]:
"""计算指定时刻的位置、速度、加速度"""
if t < 0:
t = 0.0
if t > self.T:
t = self.T
a = self.coeffs
pos = a[0] + a[1]*t + a[2]*t**2 + a[3]*t**3 + a[4]*t**4 + a[5]*t**5
vel = a[1] + 2*a[2]*t + 3*a[3]*t**2 + 4*a[4]*t**3 + 5*a[5]*t**4
acc = 2*a[2] + 6*a[3]*t + 12*a[4]*t**2 + 20*a[5]*t**3
return pos, vel, acc
class MultiSegmentTrajectoryGenerator:
"""
多段连续轨迹生成器
步骤:1) 三次样条计算中间路点速度 2) 每段五次多项式插值 3) 拼接校验
"""
def __init__(self, joint_limits: JointLimitSet):
self.limits = joint_limits
def generate(self, waypoint_positions: List[List[float]],
segment_times: List[float]) -> Dict:
"""生成多段连续轨迹"""
waypoints = np.array(waypoint_positions)
n_waypoints, n_joints = waypoints.shape
n_segments = len(segment_times)
# 步骤1:三次样条计算路点速度
spline_calc = CubicSplineVelocityCalculator(
waypoint_positions, segment_times
)
velocities = spline_calc.compute_velocities()
# 步骤2:为每段生成五次多项式
all_time = []
all_positions = []
all_velocities = []
all_accelerations = []
all_segment_ids = []
continuity_report = []
current_time_offset = 0.0
for s in range(n_segments):
T_seg = segment_times[s]
n_samples = max(int(T_seg * 200), 50)
t_points = np.linspace(0, T_seg, n_samples)
seg_positions = np.zeros((n_samples, n_joints))
seg_velocities = np.zeros((n_samples, n_joints))
seg_accelerations = np.zeros((n_samples, n_joints))
for j in range(n_joints):
traj = QuinticSegmentTrajectory(
start_pos=waypoints[s, j],
end_pos=waypoints[s+1, j],
start_vel=velocities[s, j],
end_vel=velocities[s+1, j],
start_acc=0.0,
end_acc=0.0,
duration=T_seg
)
for i, t in enumerate(t_points):
seg_positions[i, j], seg_velocities[i, j], seg_accelerations[i, j] = traj.evaluate(t)
# 段间连续性回归检查(验证浮点拼接精度)
if s < n_segments - 1:
last_idx = n_samples - 1
next_start_pos = waypoints[s+1, :]
next_start_vel = velocities[s+1, :]
pos_error = np.max(
np.abs(seg_positions[last_idx, :] - next_start_pos)
)
vel_error = np.max(
np.abs(seg_velocities[last_idx, :] - next_start_vel)
)
continuity_report.append({
'segment': s,
'waypoint_index': s+1,
'position_error_max': pos_error,
'velocity_error_max': vel_error,
'position_ok': pos_error < 1e-10,
'velocity_ok': vel_error < 1e-10
})
seg_time = t_points + current_time_offset
current_time_offset += T_seg
all_time.append(seg_time)
all_positions.append(seg_positions)
all_velocities.append(seg_velocities)
all_accelerations.append(seg_accelerations)
all_segment_ids.append(np.full(n_samples, s))
# 拼接
full_time = np.concatenate(all_time)
full_positions = np.vstack(all_positions)
full_velocities = np.vstack(all_velocities)
full_accelerations = np.vstack(all_accelerations)
full_segment_ids = np.concatenate(all_segment_ids)
# 步骤3:全程限位校验
velocity_violations = []
acceleration_violations = []
for i in range(len(full_time)):
vel_list = full_velocities[i, :].tolist()
acc_list = full_accelerations[i, :].tolist()
_, v_viol = self.limits.check_all_velocities(vel_list)
_, a_viol = self.limits.check_all_accelerations(acc_list)
for j in v_viol:
velocity_violations.append((
i, int(full_segment_ids[i]), j,
vel_list[j], self.limits.limits[j].max_velocity
))
for j in a_viol:
acceleration_violations.append((
i, int(full_segment_ids[i]), j,
acc_list[j], self.limits.limits[j].max_acceleration
))
return {
'time': full_time,
'positions': full_positions,
'velocities': full_velocities,
'accelerations': full_accelerations,
'segment_ids': full_segment_ids,
'continuity_report': continuity_report,
'velocity_violations': velocity_violations,
'acceleration_violations': acceleration_violations,
'waypoint_velocities': velocities,
'n_joints': n_joints,
'n_segments': n_segments,
'n_waypoints': n_waypoints
}
"robot/visualize_trajectory.py" — 可视化模块
"""
多段连续轨迹规划可视化
4张核心图表:旋转关节位置曲线、旋转关节速度曲线、旋转关节加速度曲线、
段间连续性+峰值对比(升降轴单独分面)
"""
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class TrajectoryVisualizer:
def __init__(self, results_dir="results"):
self.results_dir = results_dir
import os
os.makedirs(results_dir, exist_ok=True)
def plot_revolute_positions(self, timestamps, positions, segment_ids,
waypoint_times, waypoint_positions):
"""
① 旋转关节(θ₁/θ₂/θ₃)多段位置曲线+路点标记
"""
fig, ax = plt.subplots(figsize=(14, 5))
joint_names = ['θ₁ (Base Yaw)', 'θ₂ (Elbow Pitch)', 'θ₃ (Wrist Pitch)']
colors = ['steelblue', 'orange', 'green']
for j in range(3):
ax.plot(timestamps, positions[:, j], '-',
color=colors[j], linewidth=1.2, label=f'{joint_names[j]}')
for wp_t in waypoint_times:
ax.axvline(x=wp_t, color='gray', linestyle=':', linewidth=0.8, alpha=0.5)
for wp_t, wp_pos in zip(waypoint_times, waypoint_positions):
for j in range(3):
ax.plot(wp_t, wp_pos[j], marker='o', color=colors[j],
markersize=4, fillstyle='full')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Joint Angle (rad)')
ax.set_title('Multi-Segment Revolute Joint Position Profiles')
ax.legend(loc='upper right', fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = f"{self.results_dir}/01_revolute_position_curves.png"
plt.savefig(path, dpi=150)
plt.close()
return path
def plot_revolute_velocities(self, timestamps, velocities, segment_ids,
joint_limits, waypoint_times):
"""
② 旋转关节多段角速度曲线+限位线
"""
fig, axes = plt.subplots(3, 1, figsize=(14, 9), sharex=True)
joint_names = ['θ₁ (Base Yaw)', 'θ₂ (Elbow Pitch)', 'θ₃ (Wrist Pitch)']
colors = ['steelblue', 'orange', 'green']
for j in range(3):
ax = axes[j]
ax.plot(timestamps, velocities[:, j], '-',
color=colors[j], linewidth=1.0, label=f'Actual {joint_names[j]}')
limit = joint_limits.limits[j].max_velocity
ax.axhline(y=limit, color='red', linestyle='--', linewidth=0.8,
label=f'Max (+{limit} rad/s)')
ax.axhline(y=-limit, color='red', linestyle='--', linewidth=0.8,
label=f'Min (-{limit} rad/s)')
ax.axhline(y=0, color='black', linestyle='-', linewidth=0.3, alpha=0.5)
for wp_t in waypoint_times[1:-1]:
ax.axvline(x=wp_t, color='gray', linestyle=':', linewidth=0.5, alpha=0.3)
ax.set_ylabel('Angular Velocity\n(rad/s)')
ax.legend(loc='upper right', fontsize=6)
ax.grid(True, alpha=0.3)
ax.set_title(f'{joint_names[j]} Velocity Profile', fontsize=9)
axes[2].set_xlabel('Time (s)')
plt.tight_layout()
path = f"{self.results_dir}/02_revolute_velocity_curves.png"
plt.savefig(path, dpi=150)
plt.close()
return path
def plot_revolute_accelerations(self, timestamps, accelerations, segment_ids,
joint_limits, waypoint_times):
"""
③ 旋转关节多段角加速度曲线+限位线
"""
fig, axes = plt.subplots(3, 1, figsize=(14, 9), sharex=True)
joint_names = ['θ₁ (Base Yaw)', 'θ₂ (Elbow Pitch)', 'θ₃ (Wrist Pitch)']
colors = ['steelblue', 'orange', 'green']
for j in range(3):
ax = axes[j]
ax.plot(timestamps, accelerations[:, j], '-',
color=colors[j], linewidth=1.0, label=f'Actual {joint_names[j]}')
limit = joint_limits.limits[j].max_acceleration
ax.axhline(y=limit, color='red', linestyle='--', linewidth=0.8,
label=f'Max (+{limit} rad/s²)')
ax.axhline(y=-limit, color='red', linestyle='--', linewidth=0.8,
label=f'Min (-{limit} rad/s²)')
ax.axhline(y=0, color='black', linestyle='-', linewidth
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)