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 会先回零位, 再运动到目标位置