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

参考官方文档链接(可能需要梯子):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在默认情况下是异步运行的。但有时候,我们需要客户端与仿真过程同步。这可以用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

...

下图阐明了同步操作模式:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章