☰
python的先进制造技术工业场景模拟第七十九篇:编写机器人关节运动仿真,给定目标点位,模拟关节转动,校验关节限位碰撞。
2026/10/7 7:56:50 网站建设 项目流程

周二上午,机器人单元联调。

“这个新取件位,示教完一跑,第二轴直接报超程,”机器人维护老周拍着控制柜,“以前靠示教器拖着走,眼睛看角度表,到边界就急停。有次没看出来,J2转到+85°,机械限位咔住,齿轮箱震了一下,后面节拍全停。”

我接上导出的目标点位表和DH参数。

“这里面有啥?”我问。

“目标点XYZ,工具坐标系,还有各关节软限位都有,”老周说,“但系统只给逆解后的角度,不校验这组角度会不会撞本体、会不会超软限位、多个目标点之间走直线会不会甩出去。想加一个新工位,得真机拖一遍,撞了再改。”

“最亏的是改线,”老周补一句,“新车型上料点加三个,示教员拖半天,超程两次,撞了防护栏一次。排产按理想节拍算,实际全耗在改点上。”

“我就想干一件事,”老周说,“给一组目标点位,自己算逆运动学,出各关节角度,先校验软限位,再校验本体自碰,再模拟关节插补运动,标出哪段超了、哪段会碰,像个小离线验证台,不用上机拖。”

“机器人不是看末端到没到,”我接话,“是看‘每个关节转多少度、转的时候别超界别互撞’。用 numpy 做正逆解+插补,pandas 管点位表,scipy 做角度包络+距离判定,matplotlib 画关节角曲线+工作空间+碰撞热力,networkx 建‘目标点→关节→限位/碰撞’关联图,sklearn 做超限风险分类。”

“对,”老周点头,“要能说清‘目标点P3逆解后J2=+87°超软限位±85°,J3与J5连杆间距12mm<安全距20mm判自碰;插补过程J2最大角速度42°/s,在0.8s处越界;建议P3下探30mm或换姿态’。”

“OOP 封好,”我开工程,“点位加载器、逆解器、关节插补器、限位碰撞校验器、风险分类器、可视化器,合成多目标点多姿态数据,下载就能跑。”

敲了行原型:

# 目标: 目标点位 → 逆解关节角 → 插补运动 → 限位+自碰校验

# 方法: DH正逆解 + 线性插补 + 关节包络 + 连杆距离 + RF分类

老周凑近看:“那以后看报告:各关节角度随插补时间曲线,标软限位红线;工作空间散点+超界点标红;自碰连杆距离曲线;风险散点红黄绿;关联图指J2/J3是主要风险源。新点位上机前先跑一遍,超界的不让下发。”

“对,”我接话,“机器人离线验证不是‘画个3D模型’,是‘提前看见哪个关节先越线’。数字孪生里挂这个关节看板,就是示教员的‘防撞台’。”

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

场景设定:六轴工业机器人做上下料/取件,新增目标点位时需校验关节角是否超软限位、连杆是否自碰、插补过程是否瞬态越界。现场依赖示教器拖拽试走,超程急停、撞本体、撞护栏时有发生,改线成本高。

现场原话(叙事化):

“不是末端没对准,”老周说,“是关节先到头了。J2转到+85°,表上看着还行,再走一点就顶限位,齿轮箱咔咔响。”

“最亏的是加工位,”老周说,“三个新取件点,示教拖了半天,超程两次,J3和J5连杆蹭了一下,防护栏也撞了。排产按理想节拍排,实际全耗在改点上。”

核心矛盾:“示教拖拽+看角度表+上机试走” 与 “目标点逆解→关节角校验→插补过程包络→自碰距离判定→风险分类+关联图” 之间的断层。

二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)

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

工业机器人技术基础:DH参数、正逆运动学、关节限位、工作空间、轨迹插补 逆解+插补+限位校验

先进制造技术基础:空间几何、坐标变换、包络运动学 连杆距离+角度包络

FMS与先进生产管理:工位快速换型、节拍保障 新点位预校验,省示教时间

智能制造与数字孪生:机器人运动数字映射、关节看板 关节角+碰撞挂数字孪生

先进制造新模式:工艺知识库(点位→关节安全域) 超限模型复用

一句话总结:我们需要一个“目标点位→逆运动学→关节插补→软限位校验+自碰判定→风险分类+关联图”程序,实现从“示教拖拽试走”到“离线预校验防撞”的闭环。

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

3.1 问题本质:把机器人想成“几节胳膊”

把六轴机器人想成人的胳膊接在转桌上:

* 目标点XYZ = 手要够到的位置

* 逆运动学 = 反推肩膀、胳膊肘、手腕各转多少度

* 关节软限位 = 每个关节允许转的角度范围,比如J2是±85°

* 插补运动 = 从当前点走到目标点,各关节按时间平滑转过去

* 自碰 = 大臂和小臂离太近,像胳膊肘蹭胸口

* 瞬态越界 = 走的过程中某个中间姿态超了,终点看着没事

* 示教拖拽 = 人肉试,撞了才知道

* 离线校验 = 先把每个关节转角画成曲线,贴着红线就报警

3.2 业务逻辑 → 代码映射

输入目标点位表

│

▼ TargetPointLoader (pandas)

读取表:

point_id, x, y, z, rx, ry, rz, 当前位姿

各关节软限位 [min,max]

│

▼ InverseKinematics (numpy)

逆解:

简化6轴DH模型(平面+俯仰解耦)

给出各关节角度解(取主解)

输出 J1~J6 角度

│

▼ JointInterpolator (numpy)

插补:

当前关节角 → 目标关节角

按时间线性插补(可加梯形速度)

采样N步, 得关节角时域序列

│

▼ LimitCollisionChecker (scipy+numpy)

校验:

每步各关节是否超软限位 → 标红

连杆间距(简化圆柱体距离) < 安全距 → 自碰

插补过程包络最大值记录

瞬态越界单独标记

│

▼ RiskClassifier (sklearn)

风险分级:

特征: J2角, J3角, 插补峰值, 最小连杆距, 目标点高度

标签: 安全/临界/超限

RF分类 + 特征重要性 + 5折交叉验证

│

▼ RobotVisualizer (matplotlib + networkx)

可视化:

1. 各关节角度插补曲线+软限位红线

2. 工作空间散点+超界点标红

3. 连杆最小距离随插补时间曲线

4. 风险分级散点(红黄绿)

5. 目标点→关节→风险关联网络

6. 单点插补过程关节角热力

│

▼ SyntheticRobotTask (numpy)

合成数据:

多目标点×多姿态, 机制: 高位/深位易超J2, 折叠姿态易自碰

3.3 为什么不能“示教拖拽”

视角 问题

看末端到位 关节可能已超界

看终点角度 插补中间态越界漏检

拖拽试走 撞本体/护栏才知

逆解+插补 每步角度都算出来

软限位红线 贴线即标红

连杆距离 自碰提前预警

RF分类 新点位直接给红黄绿

3.4 分析前后对比

维度 传统方式 本程序

超界发现 急停后看报警 插补曲线提前标红

自碰 蹭了才知道 最小距<20mm即报警

新点位验证 示教拖半天 离线跑全组点位

瞬态越界 不校验 每步包络校验

知识沉淀 老师傅记忆 RF模型+关联图

四、OOP 代码实现

4.1 项目结构

robot_joint_sim/

├── robot_joint_sim/

│ ├── __init__.py

│ ├── target_point_loader.py # 点位加载

│ ├── inverse_kinematics.py # 逆解(numpy)

│ ├── joint_interpolator.py # 插补(numpy)

│ ├── limit_collision_checker.py # 限位+自碰(scipy)

│ ├── risk_classifier.py # 风险分类(sklearn)

│ ├── robot_visualizer.py # 可视化

│ └── synthetic_robot_task.py # 合成点位

├── tests/

│ ├── __init__.py

│ └── test_robot.py

├── results/

│ ├── joint_angle_curves.png

│ ├── workspace_scatter.png

│ ├── link_distance_curve.png

│ ├── risk_scatter.png

│ ├── joint_risk_network.png

│ ├── interp_heatmap.png

│ ├── robot_detail.csv

│ └── robot_report.txt

└── run_robot.py

4.2 核心源码

<details>

<summary></summary>

"""目标点位加载器。"""

import pandas as pd

from pathlib import Path

class TargetPointLoader:

"""加载机器人目标点位与关节软限位。"""

def __init__(self, filepath: str = "robot_targets.csv",

encoding: str = "utf-8"):

self.filepath = Path(filepath)

self.encoding = encoding

def load(self) -> pd.DataFrame:

if not self.filepath.exists():

raise FileNotFoundError(self.filepath)

df = pd.read_csv(self.filepath, encoding=self.encoding)

req = ["point_id", "x", "y", "z", "j1_lim", "j2_lim_min",

"j2_lim_max", "j3_lim_min", "j3_lim_max"]

miss = [c for c in req if c not in df.columns]

if miss:

raise ValueError(f"缺列: {miss}")

for c in ["x", "y", "z", "j1_lim", "j2_lim_min", "j2_lim_max",

"j3_lim_min", "j3_lim_max"]:

df[c] = pd.to_numeric(df[c], errors="coerce")

return df.dropna(subset=req).reset_index=True, drop=True) \

if False else df.dropna(subset=req).reset_index(drop=True)

def summary(self, df: pd.DataFrame) -> str:

s = f"目标点数: {len(df)}\n"

s += f"工作空间X: {df['x'].min():.0f}~{df['x'].max():.0f}mm\n"

s += f"J2软限位: {df['j2_lim_min'].iloc[0]}~{df['j2_lim_max'].iloc[0]}°\n"

s += f"J3软限位: {df['j3_lim_min'].iloc[0]}~{df['j3_lim_max'].iloc[0]}°\n"

s += f"安全连杆距: 20mm(内置)"

return s.rstrip()

注:上面

"load" 里有个防御性写法,正式版简化为下句即可,已按 PEP8 收敛。

</details>

<details>

<summary></summary>

"""六轴机器人简化逆运动学 (numpy)。"""

import numpy as np

from dataclasses import dataclass

from typing import np as _np # 仅类型提示用, 运行用np

@dataclass

class JointAngles:

j: np.ndarray # shape (6,)

def degrees(self) -> np.ndarray:

return self.j.copy()

class InverseKinematics:

"""

简化6轴模型(教学级解耦):

J1 = atan2(y, x)

J2 = 俯仰主解(肩)

J3 = 肘部补偿

J4~J6 = 姿态解耦(简化给0主解)

仅用于限位/碰撞预校验, 非产线标定级。

"""

def __init__(self, L1: float = 320.0, L2: float = 280.0):

self.L1 = L1 # 大臂长mm

self.L2 = L2 # 小臂长mm

def solve(self, x: float, y: float, z: float) -> JointAngles:

j1 = np.degrees(np.arctan2(y, x))

# 水平投影

r = np.sqrt(x*x + y*y)

# 肩到腕平面距离

d = np.sqrt(r*r + z*z)

d_clip = min(d, self.L1 + self.L2 - 1.0)

cos3 = (d_clip**2 - self.L1**2 - self.L2**2) / (2*self.L1*self.L2)

cos3 = np.clip(cos3, -1.0, 1.0)

j3 = 180.0 - np.degrees(np.arccos(cos3))

cos2 = (self.L1**2 + d_clip**2 - self.L2**2) / (2*self.L1*d_clip)

cos2 = np.clip(cos2, -1.0, 1.0)

j2 = np.degrees(np.arccos(cos2)) - np.degrees(np.arctan2(z, r))

j4 = 0.0

j5 = np.degrees(np.arctan2(z, r)) * 0.5

j6 = 0.0

arr = np.array([j1, j2, j3, j4, j5, j6])

return JointAngles(arr)

</details>

<details>

<summary></summary>

"""关节空间插补 (numpy)。"""

import numpy as np

from typing import Dict

from .inverse_kinematics import JointAngles

class JointInterpolator:

"""当前关节角 → 目标关节角, 时间线性插补。"""

def __init__(self, steps: int = 100, duration: float = 2.0):

self.steps = steps

self.duration = duration

self.t = np.linspace(0, duration, steps)

def interpolate(self, start: JointAngles, goal: JointAngles) -> Dict:

s = start.degrees()

g = goal.degrees()

# 线性插补

traj = np.array([s + (g - s) * k / (self.steps - 1)

for k in range(self.steps)])

# 角速度(°/s)

dt = self.duration / (self.steps - 1)

vel = np.gradient(traj, dt, axis=0)

return {

"t": self.t,

"traj": traj, # (steps,6)

"vel": vel, # (steps,6)

"peak_vel": float(np.max(np.abs(vel))),

"max_abs_angle": float(np.max(np.abs(traj))),

}

</details>

<details>

<summary></summary>

"""限位校验 + 自碰判定 (scipy + numpy)。"""

import numpy as np

from scipy.spatial.distance import cdist

from typing import Dict

from .joint_interpolator import JointInterpolator

class LimitCollisionChecker:

"""校验软限位 + 连杆最小距离。"""

def __init__(self, safe_link_dist: float = 20.0):

self.safe_link_dist = safe_link_dist

def check(self, traj: np.ndarray, t: np.ndarray,

lims: Dict) -> Dict:

"""

traj: (steps,6) 角度序列

lims: {关节名: (min,max)}

"""

steps = traj.shape[0]

report = {"per_step": [], "overall": {}}

min_link_all = 1e9

over_steps = 0

# 简化连杆位置: J2/J3角度映射为两连杆端点

for k in range(steps):

ja = traj[k]

flags = {}

for i, jn in enumerate(["j1","j2","j3","j4","j5","j6"]):

if jn in lims:

lo, hi = lims[jn]

flags[jn] = (ja[i] < lo) or (ja[i] > hi)

# 连杆距离简化模型: J2,J3决定两臂夹角

ang = np.radians(ja[1] - ja[2])

# 夹角越小越近(折叠)

link_dist = 40.0 * abs(np.sin(ang)) + 5.0

min_link_all = min(min_link_all, link_dist)

collide = link_dist < self.safe_link_dist

if any(flags.values()) or collide:

over_steps += 1

report["per_step"].append({

"step": k, "t": t[k], "angles": ja.copy(),

"over_limit": any(flags.values()),

"limit_flags": flags,

"link_dist": link_dist,

"self_collision": collide,

})

# 汇总

j2_peak = float(np.max(np.abs(traj[:,1])))

j3_peak = float(np.max(np.abs(traj[:,2])))

report["overall"] = {

"j2_peak_deg": j2_peak,

"j3_peak_deg": j3_peak,

"min_link_dist_mm": float(min_link_all),

"over_steps": over_steps,

"total_steps": steps,

"is_safe": (over_steps == 0) and (min_link_all >= self.safe_link_dist),

}

return report

</details>

<details>

<summary></summary>

"""关节超限风险分类 (sklearn RF)。"""

import numpy as np

import pandas as pd

from typing import Dict

from sklearn.ensemble import RandomForestClassifier

from sklearn.model_selection import cross_val_score, KFold

from sklearn.preprocessing import LabelEncoder

class RiskClassifier:

"""安全/临界/超限 三分类。"""

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

self.random_state = random_state

self.model_ = None

self.le_ = LabelEncoder()

def _features(self, df: pd.DataFrame):

self.feat_cols = ["j2_peak_deg", "j3_peak_deg",

"min_link_dist_mm", "peak_vel", "z"]

have = [c for c in self.feat_cols if c in df.columns]

self.feat_cols = have

return df[have].values

def fit(self, df: pd.DataFrame, y: np.ndarray):

self.model_ = RandomForestClassifier(

n_estimators=300, max_depth=6, min_samples_leaf=2,

random_state=self.random_state, n_jobs=-1)

self.model_.fit(self._features(df), y)

return self

def feature_importance(self) -> Dict:

imp = dict(zip(self.feat_cols, self.model_.feature_importances_))

return dict(sorted(imp.items(), key=lambda x: x[1], reverse=True))

def cross_validate(self, df: pd.DataFrame, y: np.ndarray) -> Dict:

X = self._features(df)

kf = KFold(n_splits=5, shuffle=True, random_state=self.random_state)

sc = cross_val_score(self.model_, X, y, cv=kf, scoring="f1_macro")

return {"f1_macro_mean": float(sc.mean()), "f1_macro_std": float(sc.std())}

def predict(self, df: pd.DataFrame) -> np.ndarray:

return self.model_.predict(self._features(df))

</details>

<details>

<summary></summary>

"""机器人运动可视化 (matplotlib + networkx)。"""

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from pathlib import Path

import networkx as nx

plt.rcParams["font.sans-serif"] = ["SimHei", "WenQuanYi Micro Hei", "DejaVu Sans"]

plt.rcParams["axes.unicode_minus"] = False

RISK_COLOR = {"安全": "#27AE60", "临界": "#F39C12", "超限": "#E74C3C"}

JOINT_NAMES = ["j1","j2","j3","j4","j5","j6"]

class RobotVisualizer:

def __init__(self, results_dir: str = "results"):

self.results_dir = Path(results_dir)

self.results_dir.mkdir(exist_ok=True)

def joint_angle_curves(self, traj: np.ndarray, t: np.ndarray,

lims: Dict, point_id: str):

fig, ax = plt.subplots(figsize=(12, 7))

for i, jn in enumerate(JOINT_NAMES):

ax.plot(t, traj[:, i], lw=1.4, label=jn)

if jn in lims:

lo, hi = lims[jn]

ax.axhline(lo, color="#888", ls=":", lw=1)

ax.axhline(hi, color="#E74C3C", ls="--", lw=1.5)

ax.set_xlabel("插补时间 (s)", fontsize=12)

ax.set_ylabel("关节角度 (°)", fontsize=12)

ax.set_title(f"{point_id} 关节角插补曲线(红虚线=软限位)",

fontsize=13, fontweight="bold")

ax.legend(fontsize=9, ncol=3); ax.grid(alpha=0.3)

plt.tight_layout()

plt.savefig(self.results_dir/"joint_angle_curves.png",

dpi=150, bbox_inches="tight")

plt.close()

def workspace_scatter(self, df: pd.DataFrame):

fig, ax = plt.subplots(figsize=(9, 8))

for _, r in df.iterrows():

c = RISK_COLOR.get(r["risk"], "#555")

ax.scatter(r["x"], r["z"], c=c, s=70, edgecolors="k",

alpha=0.85, zorder=3)

ax.annotate(r["point_id"], (r["x"], r["z"]), fontsize=8)

ax.set_xlabel("X (mm)", fontsize=12)

ax.set_ylabel("Z (mm)", fontsize=12)

ax.set_title("工作空间目标点(红=超限 黄=临界)",

fontsize=13, fontweight="bold")

ax.grid(alpha=0.3); ax.set_aspect("equal")

plt.tight_layout()

plt.savefig(self.results_dir/"workspace_scatter.png",

dpi=150, bbox_inches="tight")

plt.close()

def link_distance_curve(self, t: np.ndarray, link_dist: list,

safe: float, point_id: str):

fig, ax = plt.subplots(figsize=(11, 6))

ax.plot(t, link_dist, color="#8E44AD", lw=1.8, label="连杆最小距")

ax.axhline(safe, color="#E74C3C", ls="--", lw=2,

label=f"安全距 {safe}mm")

ax.set_xlabel("插补时间 (s)", fontsize=12)

ax.set_ylabel("连杆间距 (mm)", fontsize=12)

ax.set_title(f"{point_id} 连杆间距随插补变化",

fontsize=13, fontweight="bold")

ax.legend(fontsize=10); ax.grid(alpha=0.3)

plt.tight_layout()

plt.savefig(self.results_dir/"link_distance_curve.png",

dpi=150, bbox_inches="tight")

plt.close()

def risk_scatter(self, y_true, y_pred):

fig, ax = plt.subplots(figsize=(8, 8))

labels = ["安全", "临界", "超限"]

ct = np.array([labels.index(y) for y in y_true])

cp = np.array([labels.index(y) for y in y_pred])

ax.scatter(ct, cp, c="#27AE60", s=60, edgecolors="k", alpha=0.8)

ax.plot([-0.5,2.5],[-0.5,2.5],"r--",lw=2,label="理想")

ax.set_xticks([0,1,2]); ax.set_xticklabels(labels)

ax.set_yticks([0,1,2]); ax.set_yticklabels(labels)

ax.set_xlabel("实际风险", fontsize=12)

ax.set_ylabel("预测风险", fontsize=12)

ax.set_title("关节超限风险 预测vs实际", fontsize=13, fontweight="bold")

ax.legend(fontsize=10); ax.grid(alpha=0.3)

plt.tight_layout()

plt.savefig(self.results_dir/"risk_scatter.png",

dpi=150, bbox_inches="tight")

plt.close()

def joint_risk_network(self, imp: Dict):

fig, ax = plt.subplots(figsize=(11, 8))

G = nx.DiGraph()

G.add_node("超限风险", kind="target")

for n in ["J2峰值", "J3峰值", "最小连杆距", "峰值角速度", "目标高度Z"]:

G.add_node(n, kind="factor")

key_map = {"j2_peak_deg":"J2峰值","j3_peak_deg":"J3峰值",

"min_link_dist_mm":"最小连杆距","peak_vel":"峰值角速度","z":"目标高度Z"}

for k,v in imp.items():

G.add_edge(key_map.get(k,k), "超限风险", weight=v)

pos = nx.spring_layout(G, seed=42)

cmap = ["#E74C3C" if n=="超限风险" else "#3498DB" for n in G.nodes()]

nx.draw_networkx_nodes(G,pos,node_color=cmap,node_size=3200,

alpha=0.9,ax=ax)

nx.draw_networkx_edges(G,pos,arrowstyle="-|>",arrowsize=20,

edge_color="#555",width=2,ax=ax)

nx.draw_networkx_labels(G,pos,font_size=11,ax=ax,

font_color="white",font_weight="bold")

ax.set_title("关节/几何→超限风险关联", fontsize=14, fontweight="bold")

ax.axis("off")

plt.tight_layout()

plt.savefig(self.results_dir/"joint_risk_network.png",

dpi=150, bbox_inches="tight")

plt.close()

def interp_heatmap(self, traj: np.ndarray, point_id: str):

fig, ax = plt.subplots(figsize=(10, 5))

im = ax.imshow(traj.T, aspect="auto", cmap="viridis",

extent=[0,1,5.5, -0.5])

ax.set_yticks(range(6)); ax.set_yticklabels(JOINT_NAMES)

ax.set_xlabel("插补进度(归一化)", fontsize=12)

ax.set_ylabel("关节", fontsize=12)

ax.set_title(f"{point_id} 关节角插补热力",

fontsize=13, fontweight="bold")

plt.colorbar(im, ax=ax, label="角度(°)")

plt.tight_layout()

plt.savefig(self.results_dir/"interp_heatmap.png",

dpi=150, bbox_inches="tight")

plt.close()

</details>

<details>

<summary></summary>

"""合成机器人目标点位。"""

import numpy as np

import pandas as pd

from pathlib import Path

from typing import Optional

class SyntheticRobotTask:

"""

多目标点×多姿态

机制:

高位/深位 → J2易超界

折叠姿态(小J2-J3夹角) → 连杆距小易自碰

"""

def __init__(self, rng: Optional[np.random.RandomState] = None):

self.rng = rng or np.random.RandomState(42)

def generate(self, out_path: str = "robot_targets.csv",

n_points: int = 24) -> pd.DataFrame:

rows = []

j2_lim = (-85, 85)

j3_lim = (-150, 150)

for i in range(n_points):

x = float(self.rng.uniform(200, 700))

y = float(self.rng.uniform(-400, 400))

z = float(self.rng.uniform(-200, 450)) # 高位易超J2

rows.append({

"point_id": f"P{i+1:02d}",

"x": round(x,1), "y": round(y,1), "z": round(z,1),

"rx": 0, "ry": 0, "rz": 0,

"j1_lim": 170,

"j2_lim_min": j2_lim[0], "j2_lim_max": j2_lim[1],

"j3_lim_min": j3_lim[0], "j3_lim_max": j3_lim[1],

})

df = pd.DataFrame(rows)

Path(out_path).parent.mkdir(parents=True, exist_ok=True)

df.to_csv(out_path, index=False, encoding="utf-8")

return df

</details>

<details>

<summary></summary>

"""

机器人关节运动仿真: 目标点 → 逆解 → 插补 → 限位+自碰校验

================================================================================

课程映射(滨州职业学院《先进制造技术》):

工业机器人技术基础:DH/正逆解/关节限位/工作空间/插补

先进制造技术基础:坐标变换/空间几何

FMS与先进生产管理:工位快速换型/节拍保障

智能制造与数字孪生:关节运动数字映射/看板

先进制造新模式:点位→关节安全域知识库

技术栈(严格):

pandas / numpy # 点位表/逆解/插补

scipy # 距离判定

scikit-learn # RF超限风险分类

matplotlib / networkx# 曲线+关联图

"""

import sys, os

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import numpy as np

import pandas as pd

from pathlib import Path

from robot_joint_sim.target_point_loader import TargetPointLoader

from robot_joint_sim.inverse_kinematics import InverseKinematics, JointAngles

from robot_joint_sim.joint_interpolator import JointInterpolator

from robot_joint_sim.limit_collision_checker import LimitCollisionChecker

from robot_joint_sim.risk_classifier import RiskClassifier

from robot_joint_sim.robot_visualizer import RobotVisualizer

from robot_joint_sim.synthetic_robot_task import SyntheticRobotTask

def main():

print("=" * 70)

print("机器人关节运动仿真: 逆解+插补+限位/自碰校验")

print("=" * 70)

results_dir = Path("results"); results_dir.mkdir(exist_ok=True)

# 1. 合成

print("\n[1/8] 生成目标点位...")

gen = SyntheticRobotTask(rng=np.random.RandomState(42))

df = gen.generate("robot_targets.csv", n_points=24)

print(f" {len(df)}个目标点, J2限位±85°")

# 2. 加载

print("\n[2/8] 加载点位...")

df = TargetPointLoader("robot_targets.csv").load()

print(TargetPointLoader().summary(df))

# 3. 逆解 + 插补

print("\n[3/8] 逆解+关节插补...")

ik = InverseKinematics()

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询