当前位置:首页>python>Copperliasim UR5 Python 关节控制仿真实战

Copperliasim UR5 Python 关节控制仿真实战

  • 2026-10-11 06:11:36
Copperliasim UR5 Python 关节控制仿真实战

Copperliasim UR5 Python 关节控制仿真实战

作者:机器人算法调试记录

日期:2026-08-07

关键词:CoppeliaSim、UR5、Python、ZMQ Remote API、位置伺服、无轨迹规划、stepping 模式

适用读者:使用 CoppeliaSim 做机器人点位运动控制的初学者/工程师

目录

1. 项目概述

2. 软件环境配置

3. 代码接口调用

4. 仿真结果

5. 附录:完整代码

1. 项目概述

1.1 本次仿真做什么

使用 Python 通过 CoppeliaSim 的 ZMQ Remote API 控制 UR5 六轴机械臂:

• 用户交互输入 6 个关节的目标角度

• UR5 各关节先回零位,再运动至目标位置

• 不使用轨迹规划——采用位置伺服逼近,每帧计算差值并以限速逼近

1.2 UR5 机械臂简介

• 自由度:6(基座旋转、肩部、肘部、腕部 1/2/3)

• 工作半径:850 mm

• 额定有效载荷:5 kg

• 关节速度上限:约 2.16 rad/s(本仿真保守取 0.5 rad/s)

2. 软件环境配置

本项目的完整执行链路为:配置环境 → 代码调用仿真接口 → 得到仿真结果。<br>本章是第一步:把 CoppeliaSim 和 Python 环境准备好。

2.1 软件清单

📋 以下为表格数据:

· 组件: CoppeliaSim;版本: 4.x;作用: 仿真平台(含 UR5 模型库)

· 组件: Python;版本: 3.13;作用: 控制脚本运行环境

· 组件: coppeliasim-zmqremoteapi-client;版本: 最新;作用: ZMQ 远程 API 客户端(新版官方推荐)

· 组件: numpy;版本: 2.x;作用: 数值计算

2.2 安装 CoppeliaSim

下载

1. 打开官网下载页:https://www.coppeliarobotics.com/downloads

2. 选择版本:

• CoppeliaSim Edu(教育版):免费,学生/教师/科研使用,功能完整

• CoppeliaSim(专业版):付费,商业用途

1. 选择平台(本教程为 Windows):

• Windows:zip 压缩包(免安装,解压即用)

• Linux:deb / rpm / AppImage / tar.gz

• macOS:dmg 安装包

Windows 安装步骤(免安装绿色版)

1. 下载 Windows 版 zip 压缩包(约 600MB)2. 解压到任意目录,如 C:\Program Files\CoppeliaRobotics\CoppeliaSim3. 双击运行 CoppeliaSim.exe(绿色版无需安装向导)4. 首次启动若提示防火墙,允许访问(ZMQ 通信需要)5. 安装完成——看到 3D 场景主界面即成功

提示:CoppeliaSim 是绿色软件,解压即用,不需要注册表/安装向导;<br>换电脑或换目录时,把整个文件夹复制走即可。

验证安装

启动后按以下步骤确认环境就绪:

1. 菜单 Help → About,确认版本号 ≥ 4.3(ZMQ Remote API 需要 4.x)2. 场景中拖入一个机器人模型,确认模型库正常3. 菜单 Add-on → ZMQ remote API,确认插件可用(状态显示 running 或可启动)

版本注意:本教程使用 ZMQ Remote API,需要 CoppeliaSim 4.3 及以上。<br>旧版本(V-REP 3.x)用的是已废弃的 legacy Remote API,接口完全不同,请务必下载新版。

2.3 安装 Python 依赖

# 方式一:直接安装(国内网络可能超时)pip install coppeliasim-zmqremoteapi-client numpy# 方式二:清华镜像(国内推荐)pip install coppeliasim-zmqremoteapi-client numpy -i https://pypi.tuna.tsinghua.edu.cn/simple# 方式三:零安装,直接用 CoppeliaSim 自带文件# 将 <CoppeliaSim>\programming\zmqRemoteApi\clients\python\src\coppeliasim_zmqremoteapi_client# 文件夹复制到项目目录即可

2.4 场景搭建步骤

1. 启动 CoppeliaSim,新建场景(File → New Scene)

2. 打开 Model Browser → robots → non-mobile → 拖入 UR5 模型

3. 确认场景树中关节名为 joint1 ~ joint6(若不同需修改代码中的 JOINT_NAMES)

4. 启动 ZMQ Remote API:菜单 Add-on → ZMQ remote API(默认端口 23000)

5. 设置仿真步长:Simulation Settings → Time step = 0.05s(与代码中 sim_dt 一致)

关键:仿真步长必须与代码中的 sim_dt 一致,否则运动时序会错乱。

2.5 常见环境问题

📋 以下为表格数据:

· 问题: pip 安装超时;原因: 直连 PyPI 网络问题;解决: 用清华镜像(见 2.2)

· 问题: No module named 'coppeliasim_zmqremoteapi_client';原因: 依赖未装;解决: 三种安装方式任选

· 问题: VS Code 运行报 ModuleNotFoundError;原因: 解释器选错;解决: Ctrl+Shift+P → Python: Select Interpreter → 选装过依赖的解释器

3. 代码接口调用

环境就绪后,进入第二步:代码如何一步步调用仿真接口,从连接到最终运动。

3.1 接口调用时序(全链路)

步骤 Python 代码 仿真接口 作用───────────────────────────────────────────────────────────────────────── 1 RemoteAPIClient() 连接 建立 ZMQ 连接(端口23000) 2 client.getObject('sim') 获取对象 拿到 sim 远程对象 3 sim.getObject('/jointN') ×6 获取对象 拿到 6 个关节句柄 4 sim.stopSimulation() 仿真控制 清理运行状态(避免启动冲突) 5 sim.setBoolParam(..., False) 参数设置 关闭物理引擎(纯运动学) 6 sim.setStepping(True) 仿真控制 开启 stepping 模式 7 sim.startSimulation() 仿真控制 启动仿真(暂停在第一帧) 8 sim.getJointPosition(h) ×6 关节操作 读取当前关节角(伺服循环①) 9 sim.setJointPosition(h, q) ×6 关节操作 直驱设置关节位置(伺服循环④)10 client.step() 仿真控制 推进一个物理帧(伺服循环⑤)

3.2 三大底层控制机制

三个关键接口调用决策,对应三个真实的调试教训。

① stepping 模式(管时序)——为什么不用自由运行?

📋 以下为表格数据:

· 模式: 非 stepping;仿真推进方式: 仿真自由运行;问题: 与外部指令异步,配合 time.sleep() 时精度差(Windows 上误差 1~15ms),运动抖动

· 模式: stepping;仿真推进方式: 每次 client.step() 精确推进一帧;问题: 指令与物理帧严格对齐,运动丝滑

sim.setStepping(True) # 接口调用: 开启 steppingsim.startSimulation()# 循环内: 设置关节 → client.step() 推进一帧

stepping 下仿真会全速跑,需用 wall-clock 同步调速:

wait = t0 + sim_t - time.perf_counter()if wait > 0:    time.sleep(wait)

② 运动学直驱(管控制)——为什么用 setJointPosition 而不是 setJointTargetPosition?

📋 以下为表格数据:

· 接口: setJointPosition(h, q);机制: 直驱:直接把关节放到目标角度;可靠性: 高——设置什么就是什么

· 接口: setJointTargetPosition(h, q);机制: PID 追踪:给目标,关节内部控制器去追;可靠性: 依赖动态模式配置,配置不对会被静默忽略(机械臂不动但仿真时间照走,极难排查)

③ 关闭物理引擎(管物理)——为什么 setBoolParam 要传 False?

物理引擎开启: 关节是 dynamic, 直驱命令被物理残余力对抗 → "抬头"/回弹/抽动物理引擎关闭: 关节纯运动学, 直驱命令精确执行 → 实际位置与目标严格一致

# 接口调用: 关闭物理引擎 (注意: 正确常量名是 dynamics_handling_enabled,# 旧名 boolparam_physics_engine_enabled 在新版中不存在)sim.setBoolParam(sim.boolparam_dynamics_handling_enabled, False)

3.3 位置伺服核心逻辑(move_to)

while 未到达目标:    ① 读当前关节位置 sim.getJointPosition    ② 到达判断 所有关节误差 < tolerance    ③ 限幅 差值 clip 到 ±speed×sim_dt (每步位移有上限)    ④ 设置关节位置 sim.setJointPosition    ⑤ 推进一帧 client.step() + 节拍控制

限幅的作用:speed=0.5 rad/s, sim_dt=0.05s 时每步最多移动 0.025 rad(1.43°),等价于把关节速度钳制在 0.5 rad/s 以内。距离目标越近步长越小,最后平滑收敛。

3.4 主流程与交互输入

def main():    q_target = input_target() # ① 交互输入目标角度 (度)    q_zero = np.zeros(6) # ② 零位    robot = UR5Controller() # ③ 连接 (接口步骤 1~3)    robot.start_simulation() # ④ 启动 (接口步骤 4~7)    robot.move_to(q_zero, speed=0.5) # ⑤ 先回零位 (伺服循环)    robot.move_to(q_target, speed=0.5) # ⑥ 再到目标

交互输入三重校验:

def input_target():    while True:        parts = input("请输入 6 个关节目标角度(度), 空格分隔: ").strip().split()        if len(parts) != 6: # 校验 1: 数量            print(f"[提示] 需要 6 个角度, 当前输入了 {len(parts)} 个")            continue        try:            return np.radians([float(a) for a in parts]) # 校验 2/3: 数字 + 转弧度        except ValueError:            print("[提示] 输入包含非法字符")

4. 仿真结果

环境配置完成、接口调用执行后,得到以下结果。

4.0 仿真动画(录制视频)

运行 python ur5_move.py 时会通过 PrintWindow 自动录制 CoppeliaSim 窗口画面,

运动结束后保存为 ur5_motion.mp4(与脚本同目录)。

视频嵌入说明:<br>- 本地查看(VS Code 预览 / Typora / 有道云笔记桌面版):上方 <video> 标签可直接播放<br>- 发布到知乎 / 在线平台:Markdown 导入不支持本地视频,需先将 ur5_motion.mp4<br>上传到平台,再把 <video> 中的 src 替换为在线视频链接

4.1 实际仿真运行输出

==================================================  UR5 关节运动控制==================================================请输入 6 个关节目标角度(度), 空格分隔 (例: 45 -60 60 -45 -90 30):> 90 -90 90 -90 -90 0目标关节位置(deg): [ 90. -90. 90. -90. -90. 0.][第 1/2 步] 当前位姿 → 零位[第 2/2 步] 零位 → 目标位置

UR5 在 CoppeliaSim 场景中:先平滑收回到零位,再从零位伺服运动到目标位姿,最终停在目标位置。

4.2 收敛性数值验证(纯 Python 模拟,不连仿真器)

目标:零位 → [45,-60,60,-45,-90,30]°,speed=0.5 rad/s, sim_dt=0.05s

步数: 32, 等效时长: 1.6s每步最大位移: 0.0250 rad = 1.43° ← 限幅生效实际速度: 0.500 rad/s ← 严格等于设定值到达误差: 最大 0.000° ← 精确定位

4.3 调参指南

📋 以下为表格数据:

· 参数: speed;默认值: 0.5 rad/s;说明: 关节最大运动速度(想快调大,想慢调小)

· 参数: tolerance;默认值: 0.01 rad;说明: 到达判据(约 0.57°,越小定位越准)

· 参数: timeout;默认值: 60 s;说明: 超时保护(防止卡死)

· 参数: sim_dt;默认值: 0.05 s;说明: 仿真步长(必须与场景 Time step 一致)

5. 附录:完整代码

5.1 主程序(ur5_move.py,约 100 行)

"""UR5 关节运动至目标位置 - CoppeliaSim (不使用轨迹规划)用户输入 6 个关节目标角度, UR5 先回零位, 再运动至目标位置。采用位置伺服逼近: 每帧计算差值并以限速逼近, 不生成规划轨迹。用法: python ur5_move.py前置: CoppeliaSim 打开含 UR5(joint1~joint6)场景, ZMQ API 运行, Time step=0.05s"""import timeimport numpy as npfrom coppeliasim_zmqremoteapi_client import RemoteAPIClientclass UR5Controller:    """UR5 控制器: stepping 模式 + 运动学直驱 + 位置伺服"""    JOINT_NAMES = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6']    def __init__(self, sim_dt: float = 0.05):        """连接 CoppeliaSim, 获取 6 个关节句柄; sim_dt 需与场景 Time step 一致"""        self.sim_dt = sim_dt        # 仿真接口: 创建 ZMQ 远程客户端 (默认连接 localhost:23000)        self.client = RemoteAPIClient()        # 仿真接口: 获取 sim 远程对象, 之后所有仿真 API 都通过它调用        self.sim = self.client.getObject('sim')        # 仿真接口: 按场景路径 '/jointN' 逐个获取关节句柄        self.joint_handles = [self.sim.getObject('/' + n) for n in self.JOINT_NAMES]    def get_joint_positions(self) -> np.ndarray:        """读取当前各关节角 (rad)"""        # 仿真接口: 查询关节当前角度, 返回弧度值        return np.array([self.sim.getJointPosition(h) for h in self.joint_handles])    def set_joint_positions(self, q: np.ndarray):        """运动学直驱: 直接放置关节位置 (不受物理/PID 影响, 最可靠)"""        for h, angle in zip(self.joint_handles, q):            # 仿真接口: 直接设置关节位置 (单位 rad)            self.sim.setJointPosition(h, float(angle))    def start_simulation(self):        """启动 stepping 仿真; 关闭物理引擎使关节纯运动学, 直驱精确无回弹"""        try:            # 仿真接口: 若仿真已在运行则停止, 避免启动状态冲突            self.sim.stopSimulation(); time.sleep(0.1)        except Exception:            pass        # 仿真接口: 关闭物理引擎 (关节变纯运动学, 直驱命令不被物理对抗)        self.sim.setBoolParam(self.sim.boolparam_dynamics_handling_enabled, False)        # 仿真接口: 开启 stepping 模式, 仿真暂停等待 client.step() 逐帧推进        self.sim.setStepping(True)        # 仿真接口: 启动仿真 (stepping 下暂停在第一个物理帧)        self.sim.startSimulation()    def move_to(self, q_target: np.ndarray, speed: float = 0.5,                tolerance: float = 0.01, timeout: float = 60.0) -> np.ndarray:        """        位置伺服逼近目标 (无轨迹规划):          差值 = 目标 - 当前 -> 限幅 ±speed*sim_dt -> 设置关节 -> step() 推进一帧        到达判据: 所有关节误差 < tolerance; 超时强制结束。        Returns: 实际到达位置 (rad)        """        t_start = time.perf_counter()        while True:            q = self.get_joint_positions()            if np.all(np.abs(q_target - q) < tolerance):                break            # 限幅保证每步位移 ≤ speed*sim_dt, 关节速度不超限            step = np.clip(q_target - q, -speed * self.sim_dt, speed * self.sim_dt)            self.set_joint_positions(q + step)            # 仿真接口: 推进一个物理帧 (仿真时间前进 sim_dt)            self.client.step()            time.sleep(self.sim_dt) # 节拍控制 (实时速度)            if time.perf_counter() - t_start > timeout:                print("[WARN] 超时未到达, 强制结束")                break        return self.get_joint_positions()def input_target() -> np.ndarray:    """交互式输入 6 个关节角度(度), 校验数量与合法性, 返回弧度数组"""    while True:        parts = input("请输入 6 个关节目标角度(度), 空格分隔 (例: 45 -60 60 -45 -90 30):\n> ").strip().split()        if len(parts) != 6:            print(f"[提示] 需要 6 个角度, 当前输入了 {len(parts)} 个\n")            continue        try:            return np.radians([float(a) for a in parts])        except ValueError:            print("[提示] 输入包含非法字符\n")def main():    """主流程: 输入目标 -> 连接 -> 回零位 -> 运动到目标"""    print("=" * 50, "\n UR5 关节运动控制\n", "=" * 50)    q_target = input_target()    q_zero = np.zeros(6) # 零位    print(f"\n目标关节位置(deg): {np.degrees(q_target).round(1)}")    robot = UR5Controller()    robot.start_simulation()    print(f"\n[第 1/2 步] 当前位姿 → 零位")    robot.move_to(q_zero, speed=0.5)    print(f"[第 2/2 步] 零位 → 目标位置")    robot.move_to(q_target, speed=0.5)if __name__ == '__main__':    main()

5.2 运行指南

cd ur5_move_to_targetpython ur5_move.py# 按提示输入 6 个关节角度 (度), 例如: 90 -90 90 -90 -90 0# UR5 会先回零位, 再运动到目标位置

最新文章

随机文章