VREP Remote API工作模式详解(未写完,完成度90%)

参考官方文档链接(可能需要梯子):http://www.coppeliarobotics.com/helpFiles/index.html

概述

remote API的使用方式与regularAPI类似,但是有2点不同:

  • 大多remote API都会返回一个位编码值:return code。因为return code是bit-coded的,所以需要测试每一个位来确定正确的含义。
  • 大多remote API都需要两个额外的参数:operation modeclientID(simxStart返回的标识符)。

需要operation modereturn 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在默认情况下是异步运行的。但有时候,我们需要客户端与仿真过程同步——通过远程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

...

下图阐明了同步操作模式:
在这里插入图片描述
当调用同步触发器(simxSynchronousTrigger)时,下一个仿真步开始计算。这并不意味着当函数调用返回时,下一个模拟步骤将完成计算。因此,您必须确保读取正确的数据。如果没有采取特殊措施,则可能从之前的仿真步骤或当前仿真步骤读取数据,如下图所示:
在这里插入图片描述
有几种方式来克服以上的问题。
最简单的方法是在调用同步触发器(simxSynchronousTrigger)后直接以阻塞方式调用函数(其实这里官网想表达的意思是直接调用一个阻塞方式的函数,函数任意,比如simxGetPintTime,原因嘛,就是让这个阻塞过程来强制占用(这里不好解释了)):

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

simxGetPingTime(clientID); // After this call, the first simulation step is finished (Blocking function call)

// Now we can safely read all streamed values

下图说明了上述过程
在这里插入图片描述
下图说明了如何在服务器端(即在V-REP远程API插件端)处理初始化的远程API命令:
在这里插入图片描述

附加内容

在客户端(即,你的IDE),最少会运行两个线程:①主线程(从中调用Remote API);②通信线程(从中传送数据)。在客户端,可以有任意多的通信线程(通信线):可用simxStart来启动每一个。在服务器端使用V-REP插件实现,以类似的方式运行。下图说明了Remote API的工作方式:
在这里插入图片描述

  • simx_opmode_oneshot:非阻塞模式(non-blocking mode)。命令送去服务端执行(1)-(b)-(3)。从本地缓冲区返回对先前执行的同一命令的响应(如果有的话(i)-(2))。函数不等待从服务端(7)-(i)的响应。
    在服务端,指令被暂存在(4)-(d),沿着(d)-(9)-(g)执行一次,并沿着(g)-(6)传送响应结果。这个模式常常被“设置类函数(set-functions)”(如simxSetJointPosition)使用,用户并不关心返回值。
  • simx_opmode_blocking:阻塞模式(blocking mode)。命令送去服务端执行(1)-(b)-(3),并等待从服务端返回的响应(7)-(i)-(2)。然后接收到的响应被从输入箱缓存中删除(i),这操作是阻塞模式独有的。
    在服务端,指令被暂存在(4)-(d),沿着(d)-(9)-(g)执行一次,并沿着(g)-(6)传送响应结果。这个模式常常被“得到类函数(get-functions)”(如simxGetObjectHandle)使用,用户需要得到响应。
  • simx_opmode_streaming:非阻塞模式(non-blocking mode)。命令送去服务端执行(1)-(b)-(3)。从本地缓冲区返回对先前执行的同一命令的响应(如果有的话(i)-(2))。函数不等待从服务端(7)-(i)的响应。
    simx_opmode_oneshot类似,但是在服务端指令被暂存在(4)-(e)(而非(4)-(d)),连续执行(e)-(9)-(g),并持续传送回客户端(g)-(6)。这个模式常常被“得到类函数(set-functions)”(如simxGetJointPosition)使用,用户经常需要一个特定的值。
  • simx_opmode_oneshot_split
  • simx_opmode_streaming_split
  • simx_opmode_discontinue
  • simx_opmode_buffer:非阻塞模式(non-blocking mode)。不向服务端传送指令,但是如果(i)-(2)可用,则从本地缓冲区返回对先前执行的相同命令的响应。此模式通常与simx_opmode_streamingsimx_opmode_streaming_split操作模式一起使用:首先,用一个streaming指令启动,然后提取数据。
  • simx_opmode_remove

猜你喜欢

转载自blog.csdn.net/lllxxq141592654/article/details/85128856