参考官方文档链接(可能需要梯子):http://www.coppeliarobotics.com/helpFiles/index.html
文章目录
概述
remote API的使用方式与regularAPI类似,但是有2点不同:
- 大多remote API都会返回一个位编码值:
return code
。因为return code
是bit-coded的,所以需要测试每一个位来确定正确的含义。 - 大多remote API都需要两个额外的参数:
operation mode
和clientID
(simxStart
返回的标识符)。
需要operation mode
和return code
的原因是remote API函数需要通过socket通信机制从客户端到服务端(VREP),执行任务,返回客户端。一种简单的(或常规的)方法是让客户机发送请求,然后等待服务器处理请求并作出响应:在大多数情况下,这会花费太多的时间,而且延迟会损害客户机应用程序。实际上,remote API通过提供四种机制来执行函数或控制仿真过程,让用户选择operation mode和仿真进行的方式。
- 阻塞式函数调用模式(Blocking function calls)
- 非阻塞时函数调用模式(Blocking function calls)
- 数据流模式(Data streaming)
- 同步模式(Synchronous operation)
阻塞式函数调用模式(Blocking function calls)
阻塞式函数调用模式是一种简单常规的方式,适用于必须等待从服务端(VREP)返回信息的情形,比如如下情况:
// Following function (blocking mode) will retrieve an object handle:
if (simxGetObjectHandle(clientID,"myJoint",&jointHandle,simx_opmode_blocking)==simx_return_ok)
{
// here we have the joint handle in variable jointHandle!
}
下图阐明了阻塞式函数调用模式:
例1:读取UR5机械臂转轴句柄
详情:在场景中有一个UR5机械臂,以下代码读取机械臂各个轴的句柄值。
配套scene文件:
链接:https://pan.baidu.com/s/1cAUe15T7FMDWrGgUjLm9aw
提取码:2w75
与下面例3场景一致。另外这个场景非常简单,就只是在空场景里拖拽一个UR5过去。自己操作就好,实在不放心可以再下载。
代码:
import vrep
vrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish
'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.1
19997, # 端口号
True, # True:程序等待服务端开启(或连接超时)
True, # True:连接丢失时,通信线程不会尝试第二次连接
2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)
5) # 数据传输间隔,越小越快,默认5 # Connect to V-REP
print('Connected to remote API server')
vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)
RC1, UR5_joint1_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint1', vrep.simx_opmode_blocking)
RC2, UR5_joint2_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint2', vrep.simx_opmode_blocking)
RC3, UR5_joint3_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint3', vrep.simx_opmode_blocking)
RC4, UR5_joint4_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint4', vrep.simx_opmode_blocking)
RC5, UR5_joint5_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint5', vrep.simx_opmode_blocking)
RC6, UR5_joint6_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint6', vrep.simx_opmode_blocking)
print("RC1:",RC1)
print("UR5_joint1_Handle",UR5_joint1_Handle)
print("RC2:",RC1)
print("UR5_joint2_Handle",UR5_joint2_Handle)
print("RC3:",RC1)
print("UR5_joint3_Handle",UR5_joint3_Handle)
print("RC4:",RC1)
print("UR5_joint4_Handle",UR5_joint4_Handle)
print("RC5:",RC1)
print("UR5_joint5_Handle",UR5_joint5_Handle)
print("RC1:",RC1)
print("UR6_joint6_Handle",UR5_joint6_Handle)
非阻塞式函数调用模式(Non-blocking function calls)
非阻塞函数调用模式用在仅仅想给服务端(VREP)发送指令,而无需等待服务端返回信息的情况,例如如下情形:
// Following function (non-blocking mode) will set the position of a joint:
simxSetJointPosition(clientID,jointHandle,jointPosition,simx_opmode_oneshot);
下图阐明了非阻塞式函数调用模式:
例2:置位转轴角度
详情:在场景中有一个转轴(passive mode)A,连接着另一个转轴B。要把转轴A旋转1rad。
配套scene文件:
链接:https://pan.baidu.com/s/1S5JPmIl_fz5qiovHEncINw
提取码:4scy
代码:
import vrep
vrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish
'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.1
19997, # 端口号
True, # True:程序等待服务端开启(或连接超时)
True, # True:连接丢失时,通信线程不会尝试第二次连接
2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)
5) # 数据传输间隔,越小越快,默认5 # Connect to V-REP
print('Connected to remote API server')
vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)
RC0, h= vrep.simxGetObjectHandle(clientID, 'j', vrep.simx_opmode_blocking)
vrep.simxSetJointPosition(clientID,h,1,vrep.simx_opmode_oneshot)
vrep.simxGetPingTime(clientID) # 不可少,否则很可能不执行,后面会解释为什么
有些情形下,用一条指令传送多条信息是很重要的——这些信息会在服务端同时执行(例如让机器人的3个关节同时运动,即,在同一个仿真步中)。在这种情况下,用户可以暂停通信进程来实现,如下所示:
simxPauseCommunication(clientID,1);
simxSetJointPosition(clientID,joint1Handle,joint1Value,simx_opmode_oneshot);
simxSetJointPosition(clientID,joint2Handle,joint2Value,simx_opmode_oneshot);
simxSetJointPosition(clientID,joint3Handle,joint3Value,simx_opmode_oneshot);
simxPauseCommunication(clientID,0);
// Above's 3 joints will be received and set on the V-REP side at the same time
下图阐明了暂停通信进程的效果:
数据流模式(Data streaming)
服务端可以预测客户端需求的数据类型。要实现这一点,客户端必须用流(streaming)
或者连续(continuous)
操作模式flag向服务端发出此请求(即:函数被存放在服务端,在不需要客户端发出请求的情况下,定期执行并发送数据)。这可以看做是从客户端到服务端的命令(command)/信息(message)
订阅,其中服务端像客户端提供数据流。在客户端这种数据流操作请求和读取流数据如下所示:
// Streaming operation request (subscription) (function returns immediately (non-blocking)):
simxGetJointPosition(clientID,jointHandle,&jointPosition,simx_opmode_streaming);
// The control loop:
while (simxGetConnectionId(clientID)!=-1) // while we are connected to the server..
{
// Fetch the newest joint value from the inbox (func. returns immediately (non-blocking)):
if (simxGetJointPosition(clientID,jointHandle,&jointPosition,simx_opmode_buffer)==simx_return_ok)
{
// here we have the newest joint position in variable jointPosition!
}
else
{
// once you have enabled data streaming, it will take a few ms until the first value has arrived. So if
// we landed in this code section, this does not always mean we have an error!!!
}
}
// Streaming operation is enabled/disabled individually for each command and
// object(s) the command applies to. In above case, only the joint position of
// the joint with handle jointHandle will be streamed.
下图阐明了数据流操作模式:
数据流提取完后,要通知服务端停止数据流传输,否则服务端将一直传送无用数据,并导致速度下降。用simx_opmode_discontinue来实现停止传输。
例3:读取UR5机械臂转轴角度
详情:在场景中有一个UR5机械臂,以下代码读取机械臂各个轴的角度值。
配套scene文件:
链接:https://pan.baidu.com/s/1cAUe15T7FMDWrGgUjLm9aw
提取码:2w75
与上面例1场景一致。另外这个场景非常简单,就只是在空场景里拖拽一个UR5过去。自己操作就好,实在不放心可以再下载。
代码:
import vrep
import time
vrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish
'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.1
19997, # 端口号
True, # True:程序等待服务端开启(或连接超时)
True, # True:连接丢失时,通信线程不会尝试第二次连接
2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)!不太理解!
5) # 数据传输间隔,越小越快,默认5 # Connect to V-REP
print('Connected to remote API server')
vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)
RC1, UR5_joint1_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint1', vrep.simx_opmode_blocking)
RC2, UR5_joint2_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint2', vrep.simx_opmode_blocking)
RC3, UR5_joint3_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint3', vrep.simx_opmode_blocking)
RC4, UR5_joint4_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint4', vrep.simx_opmode_blocking)
RC5, UR5_joint5_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint5', vrep.simx_opmode_blocking)
RC6, UR5_joint6_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint6', vrep.simx_opmode_blocking)
vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint3_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint4_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint5_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint6_Handle, vrep.simx_opmode_streaming)
while(True):
if (vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0] # 判断vrep是否开始回传数据
& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0] # [0]是指return中的第0位,也即return code
& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]
& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]
& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]
& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0])==vrep.simx_return_ok:
for i in range(3): # 提取3次关节角度
rc1, j1_pos=vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)
rc2, j2_pos=vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)
rc3, j3_pos=vrep.simxGetJointPosition(clientID, UR5_joint3_Handle, vrep.simx_opmode_buffer)
rc4, j4_pos=vrep.simxGetJointPosition(clientID, UR5_joint4_Handle, vrep.simx_opmode_buffer)
rc5, j5_pos=vrep.simxGetJointPosition(clientID, UR5_joint5_Handle, vrep.simx_opmode_buffer)
rc6, j6_pos=vrep.simxGetJointPosition(clientID, UR5_joint6_Handle, vrep.simx_opmode_buffer)
print("j1_pos:", j1_pos)
print("j2_pos:", j2_pos)
print("j3_pos:", j3_pos)
print("j4_pos:", j4_pos)
print("j5_pos:", j5_pos)
print("j6_pos:", j6_pos)
print("-----------------------")
time.sleep(0.2)
break
else:
print("waiting for server response...")
time.sleep(0.001) # 0.001是我手调出来的,便于演示而已
# 测试服务端是否继续在发送数据给客户端,以第一个关节为例
time.sleep(3)
if vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_ok:
print("客户端待机3秒后,服务端依然在发送数据。")
print("关节1的角度为",vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[1])
elif vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_novalue_flag:
print("客户端待机3秒后,服务端已停止发送数据。")
# 强制擦除存放在服务端的指令,再测试服务端是否还在发送数据
print('擦除存放在服务端的指令...')
while True:
# 因为客户端到服务端的指令是有延迟的,所以需要这个While循环来确保确实已经擦除服务端的命令,实际使用时不必这样测试。
# 另外这里面的逻辑需要注意一下,第一次检测到vrep.simx_return_novalue_flag时,应该是While循环第一个指令造成的,而不是
# 当前的那个
rc1, j1_pos=vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_discontinue)
if rc1==vrep.simx_return_ok:
print("waiting for server response...")
time.sleep(0.001) # 0.001是我手调出来的,便于演示而已
elif rc1==vrep.simx_return_novalue_flag:
print("server responds!")
break
if vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_ok:
print("强制擦除后,服务端依然在发送数据。")
print("关节1的角度为",vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[1])
elif vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_novalue_flag:
print("强制擦除后,服务端已停止发送数据。")
Connected to remote API server
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
j1_pos: 0.04933595657348633
j2_pos: 0.04936075210571289
j3_pos: -0.049343109130859375
j4_pos: 0.04935884475708008
j5_pos: 0.04934239387512207
j6_pos: 0.049343109130859375
-----------------------
j1_pos: 0.9543983936309814
j2_pos: 0.9548768997192383
j3_pos: -0.955643892288208
j4_pos: 0.9558093547821045
j5_pos: 0.9556386470794678
j6_pos: 0.9556429386138916
-----------------------
j1_pos: 1.5655755996704102
j2_pos: 1.565735101699829
j3_pos: -1.5655508041381836
j4_pos: 1.5706815719604492
j5_pos: 1.5680694580078125
j6_pos: 1.568070411682129
-----------------------
客户端待机3秒后,服务端依然在发送数据。
关节1的角度为 -9.5367431640625e-07
擦除存放在服务端的指令...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
server responds!
强制擦除后,服务端已停止发送数据。
同步模式(Synchronous operation)
以上3种模式在仿真进行时并不考虑客户端。Remote API在默认情况下是异步运行的。但有时候,我们需要客户端与仿真过程同步。这可以用Remote API的同步模式实现。此时服务端需要提前设置为同步模式。
服务端设置同步模式可以通过以下几种方式实现
simRemoteApi.start
函数- 连续remote API服务端服务配置文件
remoteApiConnections.txt
以下是同步模式的例子
simxSynchronous(clientID,true); // Enable the synchronous mode (Blocking function call)
simxStartSimulation(clientID,simx_opmode_oneshot);
// The first simulation step waits for a trigger before being executed
simxSynchronousTrigger(clientID); // Trigger next simulation step (Blocking function call)
// The first simulation step is now being executed
simxSynchronousTrigger(clientID); // Trigger next simulation step (Blocking function call)
// The second simulation step is now being executed
...
下图阐明了同步操作模式: