參考官方文檔鏈接(可能需要梯子):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
...
下圖闡明瞭同步操作模式: