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