@Project : Coppeliasim-Threading-Learning
@Author :Lucio.YipengLi@outlook.com
@Date :2025/02/14
@CoppeliaSim Version: 4.9.0
学习配合Threading库同步使用ZeroMQ
1.问题背景
在coppeliasim软件中,我们可以很轻松地按照使用规则运用内置脚本编辑器来敲代码,但内置的编辑器并不好用。而在使用外部编辑器例如Pycharm执行远程脚本时,就得自己写多线程脚本来达到与内置脚本一样的效果。
Threading库已经能很好地满足我们的需求,在本片文章中只涉及直接运用的方法,类继承法其实一样,自行摸索就好。
2.问题分析
在这个问题下我们搭建这样一个场景,如下图所示的一个篮球和一个红球,我们需要同时完成两件事情:
1.让红球按照X轴走一个直线
2.让篮球跟随红球运动
看起来比较简单不需要多线程也能完成,但是我们能学到基本的操作和初步理解概念。为了方便理解,我们直接采用直角坐标系,测量每一次红球移动的差值并赋值给篮球。
一、场景搭建
注:需要完整项目学习的同学可以邮件联系我或者去我github里自取(觉得好帮忙点点STAR,谢谢!)在coppeliasim中搭建这样的场景并不难,我们直接创建两个球体,分别命名为“Sphere1”和“Spehre2”,在属性中改变两个球体的颜色。这里需要注意的是球与地板之间的距离最好直接贴在一起。将球体的运动属性设置为仅动态即可(要加碰撞也行吧)。
二、代码编写
打开pycharm,构建一个基本的仿真远程脚本需要几个步骤:
1.导入 RemoteAPIClient
2.创建客户端client
3.获取sim api
4.设置Stepping(非必要)
5.startSimulation
6.任务结束后stopSimulation
from coppeliasim_zmqremoteapi_client import RemoteAPIClient
# 引入 RemoteAPI
client = RemoteAPIClient()
mainApp = client.require('sim')
mainApp.setStepping(True)
mainApp.startSimulation()
#工作流程 ......
mainApp.stopSimulation()
接下来是Threading库的运用
整体的工作思路是创建两个任务函数,将不同物体需要做的事情写好,注意在不同的任务函数可以重复获取sim api。尽量一个函数包含好一个物体所有任务。
下面这段代码展示了红色球的任务,非常简单的获取当前位置,修改x坐标+0.01,而后再设置回去
def RedBall():
client = RemoteAPIClient()
sim: coppeliaSimType.SimType = client.require('sim')
sim.setStepping(True)
r_ball = sim.getObject("/Sphere2")
while (t := sim.getSimulationTime()) < 5:
r_position = sim.getObjectPosition(r_ball, sim.handle_world)
r_position[0] += 0.001
sim.setObjectPosition(r_ball, r_position, sim.handle_world)
sim.step()
蓝色球的任务稍微复杂一些,因为需要处理红色球的坐标。
def BlueBall():
client = RemoteAPIClient()
sim:coppeliaSimType.SimType = client.require('sim')
sim.setStepping(True)
# 获取红蓝球对象
b_ball = sim.getObject("/Sphere1")
r_ball = sim.getObject("/Sphere2")
# 分别获取物体的初始位置
r_parent_postion = sim.getObjectPosition(r_ball,sim.handle_world)
b_parent_position = sim.getObjectPosition(b_ball,sim.handle_world)
while (t := sim.getSimulationTime()) < 5:
# 获取红色的当前位置并与上一次位置做差
r_position = sim.getObjectPosition(r_ball,sim.handle_world)
r_minus_position = list_minus(r_position, r_parent_postion)
# 将差值坐标赋值给蓝色球
b_add_position = list_add(r_minus_position,b_parent_position)
b_parent_position = sim.getObjectPosition(b_ball, sim.handle_world)
# 必须深拷贝!
r_parent_postion = copy.deepcopy(r_position)
# 设置蓝色小球位置
sim.setObjectPosition(b_ball,b_add_position,sim.handle_world)
sim.step()
两个都写好之后,我们需要注意在这类远程脚本尽量不要写无限循环任务,我们需要给整体脚本退出的出口,要不然很容易导致仿真环境出问题(例如明明结束仿真了但是物体却还在场景里蹦跶)
client = RemoteAPIClient()
sim:coppeliaSimType.SimType = client.require('sim')
sim.startSimulation()
# 创建两个任务线程
blueball_thread = threading.Thread(target=BlueBall)
redball_thread = threading.Thread(target=RedBall)
# 线程!启动!
blueball_thread.start()
redball_thread.start()
# 等候两个任务都完成(但其实在任务中我设定了仿真五秒)
blueball_thread.join()
redball_thread.join()
# 结束仿真
sim.stopSimulation()
最后启动后结果如预想的一样,红色球往X轴走,而蓝色球跟随红色球