【CoppeliaSim打怪升级之路】3. Python下利用Threading库同步运行ZeroMQ Remote Api

150 阅读3分钟

@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”,在属性中改变两个球体的颜色。这里需要注意的是球与地板之间的距离最好直接贴在一起。将球体的运动属性设置为仅动态即可(要加碰撞也行吧)。

image.png

二、代码编写

打开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轴走,而蓝色球跟随红色球

二、相关链接

我的Github

CoppeliaSim用户手册 --- CoppeliaSim User Manual