《Python机器人基础原理》第三十二篇码垛工位——给笛卡尔起点终点生成直线路径逐点调用逆运动学换算关节角度1. 实际工业应用场景周三下午某电商物流分拣中心。一条传送带将码放整齐的纸箱送到抓取工位一台4-DOF码垛机器人需要把纸箱从传送带上逐一搬运到托盘上。机器人构型基座偏航θ₁±170°最大角速度2.5 rad/s转动惯量1.2 kg·m²、大臂俯仰θ₂-10°~110°臂长650 mm质量6.5 kg质心距肩关节280 mm、小臂俯仰θ₃-120°~120°臂长550 mm质量3.8 kg质心距肘关节220 mm、末端偏航θ₄±180°质量1.2 kg。吸盘夹具安装在法兰上TCP距法兰100 mm额定吸取力120 N。纸箱规格400 mm × 300 mm × 200 mm质量5.0 kg。传送带速度0.5 m/s码垛节拍要求≤6.0 s/箱。小赵在工位上反复调试示教点额头上的汗擦了又冒出来。传送带每来一个箱子我得重新示教一遍抓取点和放置点。箱子尺寸一变整条轨迹全废。小李端着咖啡走过来看了一眼示教器屏幕你现在是逐点示教关节角度对。抓取点一个关节角组合放置点一个关节角组合中间靠示教器自动插值。问题就在这儿。你示教的是关节空间但工艺需求是笛卡尔空间——传送带给的是箱子在基座坐标系下的位姿你直接逆解成关节角就行了。箱子换尺寸改一下笛卡尔坐标重新调一次逆解轨迹自动生成。逆解实时算离线仿真先跑通。给定笛卡尔起点和终点在两点之间生成直线路径逐点调用逆运动学换算关节角度。你验证一下关节角度有没有超出限位、正逆解往返误差有没有超标没问题再下发给控制器。直线路径上的点怎么分配等间距采样。起点终点之间的欧氏距离除以你想要的步长比如每5 mm一个点。每个点做一次逆解。逆解失败的点标记出来说明那个位置机械臂够不到。那我试试。本篇解决的问题给定笛卡尔空间中直线的起点P_start和终点P_end在两点之间等间距线性插值生成直线路径对路径上的每个采样点逐点调用逆运动学换算为4-DOF机械臂的关节角度输出完整的关节角度序列并校验关节限位和正逆解往返精度。⚠️ 声明本篇全部代码为课程离线仿真运行在普通PC上用于离线码垛轨迹规划校验。不可直接用于真实机器人控制器或产线设备。真实工业现场需结合实时逆解、碰撞检测、传送带同步跟踪、真空吸盘控制。代码作者不对直接使用本代码于真实设备造成的任何后果负责。2. 痛点分析课程模块 本篇对应的工程痛点第3章 连杆参数及运动学求解 → 3.3 逆运动学 给定笛卡尔空间直线路径上的采样点逐点求解关节角度逆解失败点标记与中断第6章 轨迹规划与控制 → 6.2 轨迹规划基础 笛卡尔空间直线插值等间距采样生成路径点步长控制影响逆解计算量和轨迹平滑度第3章 连杆参数及运动学求解 → 3.2 正运动学 正逆解往返精度校验逆解得到的关节角经正运动学算回笛卡尔位置验证误差在容差内本篇代码解决 L2 离线码垛轨迹规划校验层。输入笛卡尔起点/终点位姿、采样步长、4-DOF机械臂几何参数。输出直线路径上各采样点的关节角度序列、正逆解往返精度报告、关节限位校验结果。不覆盖实时逆运动学求解器、传送带动态跟踪、真空吸盘启停时序、碰撞检测、多箱体码垛堆叠策略。关于θ₄的说明本篇4-DOF构型中θ₄末端偏航不参与TCP位置逆解仅为接口预留。码垛场景下吸盘姿态保持竖直向下恒定θ₄≡0。正运动学函数不接收θ₄参数避免读者误以为它影响位置。3. 核心逻辑讲解物理直觉码垛机器人干的活儿看起来简单从A点拿东西放到B点。但A和B是笛卡尔坐标x, y, z机器人的关节电机只认角度。你得把每个笛卡尔点翻译成四个关节各自该转多少度。直线是最简单的路径——两点之间线段最短。在笛卡尔空间里均匀取点每5 mm一个点从起点一路排到终点。每个点做一次逆运动学计算得到一组关节角。连起来就是一条从起点到终点的关节空间轨迹。逆运动学不是每次都能成功的。如果某个采样点超出了机械臂的工作空间太远、太近、或者关节角度超出限位逆解就会失败。离线仿真里必须把这些点标记出来告诉工程师这个位置够不到而不是硬算一个错误结果。业务逻辑 → 代码映射输入P_start(x,y,z), P_end(x,y,z), 采样步长 step0.005 m, 机械臂几何参数↓【步骤1直线路径生成】方向向量 d P_end - P_start总距离 L ‖d‖采样点数 N ceil(L / step) 1对每个 i ∈ [0, N-1]t i / (N-1)P_i P_start t · d↓【步骤2逐点逆运动学】对每个 P_i调用逆解器输入 (x, y, z)θ₁ atan2(y, x)剩余三自由度通过几何法求解θ₂, θ₃θ₄ ≡ 0工具竖直向下不参与位置解算如果逆解失败无解或超出限位→ 记录失败点索引立即中断并报告↓【步骤3正逆解往返校验】对每个成功的关节角组合调用正运动学算回笛卡尔位置 P_back计算误差 ‖P_back - P_i‖应 1e-4 m↓【步骤4关节限位校验】检查每个关节角是否在 [θ_min, θ_max] 范围内超限则记录↓输出关节角度序列 (N×4)、正逆解误差、限位报告关键公式文字翻译直线插值P(t) P_start t·(P_end - P_start)t从0到1均匀变化。等间距采样意味着t的步长是常数1/(N-1)。这保证了笛卡尔空间中点间距恒定。基座偏航角θ₁θ₁ atan2(y, x)。基座旋转让大臂对准目标点在水平面的投影方向。atan2保证全象限正确。TCP偏移统一口径本篇统一采用工具竖直向下假设——TCP偏移仅在Z轴方向竖直向下不改变水平投影。因此逆解时腕部水平坐标 TCP水平坐标腕部Z坐标 TCP的Z坐标 - tcp_offset。正运动学同样仅在Z方向减去偏移。FK与IK使用完全相同的几何模型保证往返校验误差在数值精度范围内。剩余三关节逆解将3D问题降维到2D平面。已知腕部位置(x_w, y_w, z_w)求θ₂和θ₃。水平距离 r √(x_w² y_w²)有效高度 z_eff z_w - d₁基座高度。腕部到肩关节距离 d √(r² z_eff²)。余弦定理求θ₃cos(θ₃) (d² - a₂² - a₃²) / (2·a₂·a₃)。肩部角 atan2(z_eff, r)肘部角γ由正弦定理求得θ₂ 肩部角 - γ。正逆解往返校验逆解得到的关节角经过正运动学算回笛卡尔坐标与原始输入比较。FK与IK偏移口径统一后误差仅来自浮点舍入容差1e-4 m0.1 mm对码垛应用足够。4. OOP代码实现技术栈严格限定numpy, scipy, matplotlib。不引入pybullet、roboticstoolbox等任何第三方机器人库。项目结构树palletizing_station_sim/├── run_palletizing_station_sim.py├── requirements.txt├── robot/│ ├── __init__.py│ ├── models.py│ ├── kinematics.py│ ├── trajectory_generator.py│ ├── limit_checker.py│ └── visualize_palletizing.py├── tests/│ ├── __init__.py│ └── test_palletizing_station_sim.py└── results/核心源码robot/models.py — 数据模型机器人数据模型连杆几何参数、关节限位、TCP位姿4-DOF码垛机器人构型修正dataclass从dataclasses导入非dataclassfrom dataclasses import dataclass, fieldfrom typing import Listimport numpy as npdataclassclass JointLimits:关节限位name: strmin: float # 最小角度 (rad)max: float # 最大角度 (rad)max_velocity: float # 最大角速度 (rad/s)dataclassclass RobotConfig:4-DOF码垛机器人配置a2: float 0.65 # 大臂长度 (m)a3: float 0.55 # 小臂长度 (m)d1: float 0.40 # 基座高度 (m)tcp_offset: float 0.10 # TCP距法兰距离 (m)竖直向下joint_limits: List[JointLimits] field(default_factorylist)def num_joints(self) - int:return 4dataclassclass CartesianPose:笛卡尔位姿位置(x,y,z)x: floaty: floatz: floatdef position(self) - np.ndarray:return np.array([self.x, self.y, self.z], dtypenp.float64)dataclassclass JointAngles:关节角度序列θ₄不参与位置解算恒为0theta1: float # 基座偏航 (rad)theta2: float # 大臂俯仰 (rad)theta3: float # 小臂俯仰 (rad)theta4: float # 末端偏航 (rad)恒为0def array(self) - np.ndarray:return np.array([self.theta1, self.theta2, self.theta3, self.theta4],dtypenp.float64)robot/kinematics.py — 正逆运动学FK/IK偏移口径统一4-DOF码垛机器人正逆运动学FK与IK统一采用工具竖直向下偏移口径TCP 腕部位置 (0, 0, -tcp_offset)腕部水平坐标 TCP水平坐标腕部Z坐标 TCP的Z tcp_offset这样FK(IK(P)) P往返误差仅来自浮点舍入。import numpy as npfrom robot.models import RobotConfig, CartesianPose, JointAnglesclass PalletizingKinematics:4-DOF码垛机器人运动学构型基座偏航θ₁ 大臂俯仰θ₂ 小臂俯仰θ₃ 末端偏航θ₄(≡0)def __init__(self, config: RobotConfig):self.config configself.a2 config.a2self.a3 config.a3self.d1 config.d1self.tcp_offset config.tcp_offsetdef forward(self, joints: JointAngles) - CartesianPose:正运动学关节角 → TCP笛卡尔位置偏移口径工具竖直向下TCP在腕部正下方tcp_offset处t1, t2, t3, _ joints.theta1, joints.theta2, joints.theta3, joints.theta4# 腕部位置肩关节为原点# 大臂末端肘关节位置elbow_x self.a2 * np.cos(t2) * np.cos(t1)elbow_y self.a2 * np.cos(t2) * np.sin(t1)elbow_z self.d1 self.a2 * np.sin(t2)# 腕部位置小臂方向由t2t3决定wrist_x elbow_x self.a3 * np.cos(t2 t3) * np.cos(t1)wrist_y elbow_y self.a3 * np.cos(t2 t3) * np.sin(t1)wrist_z elbow_z self.a3 * np.sin(t2 t3)# TCP 腕部正下方竖直偏移tcp_x wrist_xtcp_y wrist_ytcp_z wrist_z - self.tcp_offsetreturn CartesianPose(xfloat(tcp_x), yfloat(tcp_y), zfloat(tcp_z))def inverse(self, target: CartesianPose) - JointAngles:逆运动学TCP笛卡尔位置 → 关节角偏移口径与FK统一腕部在TCP正上方tcp_offset处工具竖直向下θ₄ ≡ 0x, y, z target.x, target.y, target.z# θ₁基座偏航theta1 np.arctan2(y, x)# 腕部位置 TCP正上方tcp_offsetwrist_x xwrist_y ywrist_z z self.tcp_offset# 腕部到基座的水平距离去掉基座旋转r np.sqrt(wrist_x**2 wrist_y**2)# 有效高度减去基座高度z_eff wrist_z - self.d1# 腕部到肩关节的距离d_sq r**2 z_eff**2d np.sqrt(d_sq)# 工作空间可达性检查max_reach self.a2 self.a3min_reach abs(self.a2 - self.a3)if d max_reach 1e-9:raise ValueError(f目标点 ({x:.3f}, {y:.3f}, {z:.3f}) 超出最大工作空间。f距离d{d:.3f} m 最大臂展{max_reach:.3f} m)if d min_reach - 1e-9:raise ValueError(f目标点 ({x:.3f}, {y:.3f}, {z:.3f}) 处于奇异内圈。f距离d{d:.3f} m 最小内圈{min_reach:.3f} m)# 余弦定理求θ₃小臂俯仰cos_theta3 (d_sq - self.a2**2 - self.a3**2) / (2 * self.a2 * self.a3)cos_theta3 np.clip(cos_theta3, -1.0, 1.0)# elbow up: θ₃ 取正theta3 np.arccos(cos_theta3)# θ₂大臂俯仰# 肩部角 atan2(z_eff, r)shoulder_angle np.arctan2(z_eff, r)# 肘部角正弦定理gamma np.arcsin(np.clip(self.a3 * np.sin(theta3) / d, -1.0, 1.0))theta2 shoulder_angle - gamma# θ₄末端偏航恒为0工具竖直向下不参与位置解算theta4 0.0return JointAngles(theta1float(theta1),theta2float(theta2),theta3float(theta3),theta4float(theta4))robot/trajectory_generator.py — 直线路径生成笛卡尔空间直线路径生成给定起点终点等间距采样逐点逆解逆解失败时立即中断并报告不可达点索引import numpy as npfrom robot.models import CartesianPose, JointAnglesfrom robot.kinematics import PalletizingKinematicsclass LinearTrajectoryGenerator:笛卡尔空间直线插值 逐点逆解def __init__(self, kinematics: PalletizingKinematics):self.kinematics kinematicsdef generate_path(self, start: CartesianPose, end: CartesianPose,step: float 0.005) - dict:if step 0:raise ValueError(采样步长必须大于0)if step 1.0:raise ValueError(采样步长过大1m请检查单位)start_pos start.position()end_pos end.position()# 直线方向向量和总长度direction end_pos - start_postotal_length np.linalg.norm(direction)if total_length 1e-10:raise ValueError(起点和终点距离过近0.1 mm无法生成路径)# 采样点数n_samples int(np.ceil(total_length / step)) 1# 存储结果cartesian_points []joint_angles_list []fk_errors []success_count 0for i in range(n_samples):t i / (n_samples - 1) if n_samples 1 else 0.0point_pos start_pos t * directionpose CartesianPose(xfloat(point_pos[0]),yfloat(point_pos[1]),zfloat(point_pos[2]))cartesian_points.append(pose)# 逆解try:joints self.kinematics.inverse(pose)except ValueError as e:# 逆解失败立即中断但先把已成功信息打包进异常消息error_msg (f逆解失败于采样点 {i}/{n_samples-1} f(t{t:.4f})位置 f({point_pos[0]:.3f}, {point_pos[1]:.3f}, f{point_pos[2]:.3f}) m。f已成功 {success_count} 个点。f原因{e})raise ValueError(error_msg) from ejoint_angles_list.append(joints)success_count 1# 正逆解往返校验try:back_pose self.kinematics.forward(joints)error np.linalg.norm(back_pose.position() - point_pos)fk_errors.append(float(error))except Exception:fk_errors.append(np.inf)return {cartesian_points: cartesian_points,joint_angles: joint_angles_list,n_samples: n_samples,total_length: total_length,step: step,fk_errors: fk_errors,start: start,end: end}robot/limit_checker.py — 关节限位校验关节限位校验模块输出各关节峰值/裕度对比表 超限记录修正n_joints直接从self.config获取不再有裸变量bugimport numpy as npfrom robot.models import RobotConfigclass LimitChecker:校验关节角度序列是否超出限位def __init__(self, config: RobotConfig):self.config configdef check(self, joint_angles_list: list) - dict:逐点校验关节限位Returns:dict: 限位校验报告n_joints min(4, len(self.config.joint_limits))violations []limit_table []for j in range(n_joints):angles [ja.array()[j] for ja in joint_angles_list]actual_min min(angles)actual_max max(angles)limit self.config.joint_limits[j]min_margin actual_min - limit.minmax_margin limit.max - actual_maxis_violated (actual_min limit.min - 1e-6 oractual_max limit.max 1e-6)if is_violated:violations.append({joint: j,name: limit.name,actual_min: actual_min,actual_max: actual_max,limit_min: limit.min,limit_max: limit.max})limit_table.append({joint: j,name: limit.name,limit_min: limit.min,limit_max: limit.max,actual_min: actual_min,actual_max: actual_max,min_margin: min_margin,max_margin: max_margin,violated: is_violated})return {limit_table: limit_table,violations: violations,all_passed: len(violations) 0}robot/visualize_palletizing.py — 可视化模块码垛工位轨迹可视化4张核心图表3D直线路径关节角变化正逆解误差限位裕度修正FK误差图过滤inf值import numpy as npimport matplotlib.pyplot as pltplt.rcParams[font.sans-serif] [SimHei, DejaVu Sans]plt.rcParams[axes.unicode_minus] Falseclass PalletizingVisualizer:def __init__(self, results_dirresults):self.results_dir results_dirimport osos.makedirs(results_dir, exist_okTrue)def plot_3d_path(self, cartesian_points, start, end):① 3D直线路径展示起点、终点、路径点fig plt.figure(figsize(10, 8))ax fig.add_subplot(111, projection3d)positions np.array([p.position() for p in cartesian_points])ax.plot(positions[:, 0], positions[:, 1], positions[:, 2],b-, linewidth1.5, labelLinear Path (TCP))ax.scatter([start.x], [start.y], [start.z],cgreen, markero, s100, labelStart, zorder5)ax.scatter([end.x], [end.y], [end.z],cred, marker^, s100, labelEnd, zorder5)ax.set_xlabel(X (m))ax.set_ylabel(Y (m))ax.set_zlabel(Z (m))ax.set_title(3D Linear Path for Palletizing)ax.legend(fontsize9)ax.grid(True, alpha0.3)plt.tight_layout()path f{self.results_dir}/01_linear_path_3d.pngplt.savefig(path, dpi150)plt.close()return pathdef plot_joint_angles(self, joint_angles_list):② 关节角度变化4个关节角随采样点变化n len(joint_angles_list)indices np.arange(n)angles np.array([ja.array() for ja in joint_angles_list])fig, axes plt.subplots(4, 1, figsize(12, 8), sharexTrue)joint_names [θ₁ Base Yaw, θ₂ Arm Pitch,θ₃ Forearm Pitch, θ₄ End Yaw (≡0)]for i in range(4):axes[i].plot(indices, angles[:, i], b-, linewidth1.0)axes[i].set_ylabel(Angle (rad))axes[i].set_title(f{joint_names[i]} vs Sample Index, fontsize9)axes[i].grid(True, alpha0.3)axes[3].set_xlabel(Sample Index)plt.tight_layout()path f{self.results_dir}/02_joint_angles.pngplt.savefig(path, dpi150)plt.close()return pathdef plot_fk_error(self, fk_errors):③ 正逆解往返精度每个点的FK误差过滤inf值逆解失败点fk_errors np.array(fk_errors)# 过滤infvalid_mask np.isfinite(fk_errors)valid_errors fk_errors[valid_mask]n len(fk_errors)indices np.arange(n)fig, ax plt.subplots(figsize(10, 5))ax.plot(indices, fk_errors * 1000, g-, linewidth1.0,labelFK Error (mm))if len(valid_errors) 0:ax.axhline(ynp.mean(valid_errors) * 1000, colorred,linestyle--,labelfMean {np.mean(valid_errors)*1000:.4f} mm)ax.axhline(ynp.max(valid_errors) * 1000, colororange,linestyle:,labelfMax Valid {np.max(valid_errors)*1000:.4f} mm)ax.set_xlabel(Sample Index)ax.set_ylabel(FK Error (mm))ax.set_title(Forward-Inverse Kinematics Round-Trip Error)ax.legend(fontsize9)ax.grid(True, alpha0.3)plt.tight_layout()path f{self.results_dir}/03_fk_error.pngplt.savefig(path, dpi150)plt.close()return pathdef plot_limit_margins(self, limit_check_result):④ 关节限位裕度各关节实际范围 vs 限位limit_table limit_check_result[limit_table]fig, ax plt.subplots(figsize(10, 6))joint_names [row[name] for row in limit_table]x_pos np.arange(len(joint_names))actual_min [row[actual_min] for row in limit_table]actual_max [row[actual_max] for row in limit_table]limit_min [row[limit_min] for row in limit_table]limit_max [row[limit_max] for row in limit_table]ax.barh(x_pos, np.array(actual_max) - np.array(actual_min),leftactual_min, height0.4, labelActual Range,colorblue, alpha0.6)ax.barh(x_pos 0.5, np.array(limit_max) - np.array(limit_min),leftlimit_min, height0.4, labelLimit Range,colorred, alpha0.3)ax.set_yticks(x_pos 0.25)ax.set_yticklabels(joint_names)ax.set_xlabel(Angle (rad))ax.set_title(Joint Limit Margins)ax.legend(fontsize9)ax.grid(True, alpha0.3)plt.tight_layout()path f{self.results_dir}/04_limit_margins.pngplt.savefig(path, dpi150)plt.close()return path5. 可视化要求序号 图表文件 服务结论①01_linear_path_3d.png TCP三维直线路径验证起点→终点直线轨迹正确生成直观展示路径点分布②02_joint_angles.png 关节角度变化4个关节角随采样点变化展示逆解结果的连续性和单调性③03_fk_error.png 正逆解往返精度验证逆解可靠性FK/IK偏移口径统一后误差应在亚毫米级0.1 mm④04_limit_margins.png 关节限位裕度各关节实际角度范围 vs 限位识别哪些关节接近极限6. README使用说明 核心知识点卡片快速开始pip install -r requirements.txtpython -m pytest tests/test_palletizing_station_sim.py -v # 3/3 测试通过python run_palletizing_station_sim.py # 运行码垛工位仿真数据格式说明参数 修改位置 说明起点/终点run_...py中start/end CartesianPose(x,y,z)单位m采样步长generator.generate_path(step0.005) 默认5 mm连杆参数RobotConfig初始化 大臂a2、小臂a3、基座高d1关节限位JointLimits列表 各关节min/max/velocity 知识点卡片笛卡尔空间直线轨迹逆运动学对应课本第3、6章- 直线插值P(t) P_start t·(P_end - P_start)t∈[0,1]均匀采样- 逆运动学几何解析法θ₁atan2(y,x)余弦定理求θ₃肩部角-γ求θ₂- TCP偏移口径统一FK与IK统一采用工具竖直向下假设腕部TCP正上方保证FK(IK(P))P- 工作空间校验d √(r²z²)可达条件 |a₂-a₃| ≤ d ≤ a₂a₃- 正逆解往返校验逆解→正解→比较误差验证运动学模型正确性- θ₄不参与位置解算码垛场景吸盘竖直向下θ₄≡0仅预留接口7. 总结与工程师思考工业落地难处一、传送带同步带来的动态起点。 本篇假设起点是静止已知的。但真实产线上传送带以0.5 m/s运行纸箱在基座坐标系下的位置随时间变化。需要在每个抓取周期实时计算当前纸箱位姿动态生成直线路径。离线仿真中的静态逆解需要升级为带时间戳的动态逆解序列。二、关节速度约束与轨迹时间参数化。 本篇只输出关节角度序列没有时间信息。真实控制器需要知道每个关节角在什么时候到达。从笛卡尔直线速度反推各关节角速度需要求导——逆解本身是非线性的角速度可能在路径端点附近出现尖峰。工业系统用梯形速度剖面或S曲线规划来平滑加减速。三、逆解多解选择与碰撞风险。 4-DOF构型在可达空间内通常有elbow up和elbow down两个解。本篇固定选elbow up。但码垛场景中如果箱体堆叠高度变化elbow up可能撞到已码好的箱子。需要加入碰撞检测或动态解选择策略。工程师心得小赵看着图③——正逆解误差曲线平稳地趴在0.02 mm以下。误差不到半根头发丝逆解精度没问题。数值精度是一回事现场工况是另一回事。小李把咖啡杯放下你这个仿真里传送带是停着的。实际上箱子在动你逆解的起点每一秒都在变。还有——他指了图②中θ₂的曲线大臂俯仰斜率沿路径不均匀峰值位置需要结合差分曲线精确定位。如果传送带速度快关节角速度可能在某个位置飙上去。所以下一步要加时间参数化对。先把关节角速度算出来看有没有超限。再下一步才是实时逆解。一步一步来。8. 写作与风格限制本篇全部代码为课程离线仿真运行在普通PC上用于离线码垛轨迹规划校验。不可直接用于真实机器人控制器或产线设备。 真实工业现场需结合实时逆解、传送带同步跟踪、真空吸盘控制、碰撞检测。代码作者不对直接使用本代码于真实设备造成的任何后果负责。完全去营销中立客观不推荐任何硬件或商业品牌。禁止首先、其次、最后等八股连接词。拒绝论文式生硬开头保持故事叙事感。全部物理量标注完整单位。9. 文末附录环境配置pip install numpy1.24.0 scipy1.10.0 matplotlib3.7.0运行说明cd palletizing_station_simpip install -r requirements.txtpython -m pytest tests/ -vpython run_palletizing_station_sim.py参数修改指南想改什么 改哪里 示例起点/终点run_...py中start/end的x,y,z 位置(m)采样步长generator.generate_path(step0.005) 默认5 mm臂长RobotConfig中a2/a3 大臂/小臂长度(m)基座高度RobotConfig中d1 基座高度(m)关节限位JointLimits的min/max 弧度(rad)输出文件清单文件 含义results/01_linear_path_3d.png TCP三维直线路径起点终点results/02_join利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛