在正式介绍怎么用CoppeliaSim进行无人机的仿真和外部控制之前,我们先看看Python连接CoppeliaSim需要作什么操作。
Python连接CoppeliaSim
CoppeliaSim在“C:\Program Files\CoppeliaRobotics\CoppeliaSimEdu\programming”文件夹下放置了一系列与其连接所需要的库。
针对python语言的连接库在“C:\Program Files\CoppeliaRobotics\CoppeliaSimEdu\programming\remoteApiBindings\python\python”和“C:\Program Files\CoppeliaRobotics\CoppeliaSimEdu\programming\remoteApiBindings\lib\lib\Windows\”路径下可以找到。
实现python与CoppeliaSim连接的具体操作如下:
- 从第一个文件夹中找到sim.py和simConst.py两个文件,从第二个文件夹中找到remoteApi.dll文件,然后复制到一个新的文件夹中。
- 在创建的文件夹中新建一个.py文件,写入如下内容:
import sim
sim.simxFinish(-1) # 断开一切可能的连接
clientID=sim.simxStart('127.0.0.1',19997,True,True,5000,5) # 连接到CoppeliaSim场景中
if clientID !=-1:
print ('Connected to remote API server')
else:
print ('Failed connecting to remote API server')
sim.simxGetPingTime(clientID) # 在断开之前连接确保之前的命令已经被执行完成
sim.simxFinish(clientID) # 断开与CoppeliaSim场景的连接
-
点击CoppeliaSim的运行按钮,进入仿真:
-
运行第2步的文件,如无意外将输出“Connected to remote API server”。
无人机模型加入
完成python和CoppeliaSim的简单连接之后,可以往仿真器中加入无人机模型。(如果之前的仿真没关,记得把它关了)
在左边的模型中,选择“robots”→“mobile”可以看到里面有名为“Quadcoper.ttm”的无人机
选中该无人机并将其往右边拉,便成功添加了一台无人机到仿真平台中:
Python控制CoppeliaSim中的无人机
点开左边的Quadcoper无人机对象,可以看到其组成部分。其中,target是中央的那个小绿球,无人机会跟着该球移动,因此可以通过移动该球的位置来控制无人机的移动。代码如下:
import sim
import time
sim.simxFinish(-1) # 断开一切可能的连接
# 开启一个与服务器(V-REP)的一个通信线程。返回一个ClientID,这个ClientID后续的API基本都需要用到,可以理解为用这个ID来进行通信。
clientID=sim.simxStart('127.0.0.1',19997,True,True,5000,5) # 连接到CoppeliaSim场景中
if clientID !=-1:
print ('Connected to remote API server')
# 用名字来检索一个对象句柄。仿真环境里任何添加的object都会有一个自己的句柄(一个int,比如85,102),然后这个句柄跟object的名字对应,也就是我们有了名字就能对应到句柄。
ret1, targetObj = sim.simxGetObjectHandle(clientID, './target', sim.simx_opmode_oneshot_wait)
# 获取无人机对象信息
ret2, arr = sim.simxGetObjectPosition(clientID, targetObj, -1, sim.simx_opmode_oneshot_wait)
if ret2 == sim.simx_return_ok:
print(targetObj)
for i in range(20):
arr[1]=arr[1]+0.1
sim.simxSetObjectPosition(clientID, targetObj, -1, (arr[0], arr[1], arr[2]), sim.simx_opmode_oneshot_wait)
else:
print ('Failed connecting to remote API server')
sim.simxGetPingTime(clientID) # 在断开之前连接确保之前的命令已经被执行完成
sim.simxFinish(clientID) # 断开与CoppeliaSim场景的连接
需要注意,CoppeliaSim V4.3.0版本在控制对像的时候需要加上’./‘如’./target’,这个与老版本有区别。本人之前看着老版本的教程一直控制不了无人机,差点直接放弃了CoppeliaSim 。。。
Python控制多台无人机
和上面的操作一样,往仿真器中拖入多台无人机如下:
修改双击无人机的target对象,对其重命名:
PS:注意这里也可以不改,如果有多个对象时可以用target[x]这种格式进行索引,本人一开始不知道才写了后面那些。。。x的值和Quadcopter[x]中的x相对应。这里迷惑的地方在于点开Quadcopter[x]子项列表的时候里面的子项名称都是一样的,只有在运行仿真时才可以看到target[x]这种形式。—20220521补充。
然后修改无人机的对象描述代码(双击):
将里面的“targetObj=sim.getObject(‘./target’)”改为“targetObj=sim.getObject(‘./target1’)”或者自己命名的其他名称。
通过如下的命令就可以实现对无人机的分别控制了:
import sim
import time
sim.simxFinish(-1) # 断开一切可能的连接
# 开启一个与服务器(V-REP)的一个通信线程。返回一个ClientID,这个ClientID后续的API基本都需要用到,可以理解为用这个ID来进行通信。
clientID=sim.simxStart('127.0.0.1',19997,True,True,5000,5) # 连接到CoppeliaSim场景中
if clientID !=-1:
print ('Connected to remote API server')
# 用名字来检索一个对象句柄。仿真环境里任何添加的object都会有一个自己的句柄(一个int,比如85,102),然后这个句柄跟object的名字对应,也就是我们有了名字就能对应到句柄。
_, targetObj1 = sim.simxGetObjectHandle(clientID, './target1', sim.simx_opmode_oneshot_wait)
_, targetObj2 = sim.simxGetObjectHandle(clientID, './target2', sim.simx_opmode_oneshot_wait)
# 获取无人机对象信息
_, arr1 = sim.simxGetObjectPosition(clientID, targetObj1, -1, sim.simx_opmode_oneshot_wait)
_, arr2 = sim.simxGetObjectPosition(clientID, targetObj2, -1, sim.simx_opmode_oneshot_wait)
for i in range(20):
arr1[1] = arr1[1] + 0.1
arr2[1] = arr2[1] + 0.1
sim.simxSetObjectPosition(clientID, targetObj1, -1, (arr1[0], arr1[1], arr1[2]), sim.simx_opmode_oneshot_wait)
sim.simxSetObjectPosition(clientID, targetObj2, -1, (arr2[0], arr2[1], arr2[2]), sim.simx_opmode_oneshot_wait)
else:
print ('Failed connecting to remote API server')
sim.simxGetPingTime(clientID) # 在断开之前连接确保之前的命令已经被执行完成
sim.simxFinish(clientID) # 断开与CoppeliaSim场景的连接
Python多进程控制多台无人机
在上面的操作中,如果控制的无人机数量很多,仿真的速度将变得非常卡,原因在于程序总是要等待执行完一台无人机的控制任务之后才继续执行另外一台。为了解决这个问题,可以使用Python的多进程接口对程序进行控制(这里不使用多线程是因为Python的多线程是伪多线程,实现上用的是一个CPU线程切片进行模拟的)。
首先,在模拟器的无人机target对象上右键,然后选择Add→Associated child script→Threaded→Lua
然后双击代码对象:
在sysCall_init()函数中添加如下代码:
repeat until (simRemoteApi.start(19901,1300,false,true)~=-1)
--注意19901为端口号,每一个对象都要设置不同的端口号,可以设置的端口号为19901~19999
随后,便可以通过如下的代码对无人机进行控制:
import sim
import multiprocessing
def worker_1(interval):
client_id = sim.simxStart("127.0.0.1", 19900+interval, True, False, 10000, 5)
while True:
_, UAV = sim.simxGetObjectHandle(client_id,"./target" + str(interval), sim.simx_opmode_oneshot_wait)
sim.simxSetObjectPosition(client_id, UAV, UAV,[-0.1,0.1,0.1], sim.simx_opmode_oneshot_wait)
if __name__ == '__main__':
p1 = multiprocessing.Process(target = worker_1, args = (1,))
p2 = multiprocessing.Process(target = worker_1, args = (2,))
p3 = multiprocessing.Process(target = worker_1, args = (3,))
p4 = multiprocessing.Process(target = worker_1, args = (4,))
p1.start()
p2.start()
p3.start()
p4.start()
print("The number of CPU is:" + str(multiprocessing.cpu_count()))
for p in multiprocessing.active_children():
print("child p.name:" + p.name + "\tp.id" + str(p.pid))