深度解析V-REP Remote API (MATLAB) 的应用
OS: Win10 x64
V-REP: V-REP PRO EDU 3.5.0
MATLAB: 2016b
下面我们来聊一聊V-REP中MATLAB远程API的应用。如果你只对V-REP有基本了解,对V-REP的远程API不熟悉,强烈建议你先阅读此文。V-REP是机器人仿真界的“瑞士军刀”,其功能之丰富可以满足绝大多数仿真需求。这里仅以UR5的仿真为例进行介绍。
介绍之前,我想多说一句,无论什么时候,官方手册都是最好的参考资料,实践才是最好的老师。本文针对想要快速入门的初学者,以尽量言简意赅的方式引领读者入门。读完后你可以自己搭建一个可用的仿真平台,并对仿真的过程有初步的了解。部分介绍中存在不严谨的措辞还请谅解。
准备MATLAB环境
对应于Windows系统,你只需要准备三个文件,而且都可以在V-REP的安装目录下找到:
- remoteApiProto.m
- remApi.m
- remoteApi.dll
你只需要将其复制到你的Matlab工作目录下即可。至此Matlab环境准备完成,很简单对吧?准备完成后,你只需要调用:
vrep=remApi('remoteApi')
来建立一个vrep对象并且加载库文件。调用
vrep.simxStart
即可使能client端(Matlab)的应用。具体使用方法参考后续内容。
官方手册列出了所有支持的Matlab远程API。
V-REP的两种服务器服务模式
server端(V-REP)的操作稍微复杂。远程API服务是基于V-REP插件的。因此,使能服务器端的前提是相应的插件都被成功加载(v_repExtRemoteApi.dll, libv_repExtRemoteApi.dylib 或 libv_repExtRemoteApi.so)。你可以通过终端查看。其实一般情况下这个问题无需多虑。
V-REP服务器服务提供两种模式:
- Continuous(连续)远程API服务器服务
- Temporary (临时)远程API服务器服务
下面分别介绍。
连续远程API服务器服务
连续服务随V-REP的启动而启动。远程API插件会从一个名为 remoteApiConnections.txt 的文件中读取配置信息,并启用相关服务。此模式下,即便V-REP仿真没有开始,远程API仍然可以工作。你也可以通过命令行的形式启动连续服务。
对于Matlab机器人的仿真而言,这种模式其实非常方便,因为我们每一次仿真不需要先运行V-REP仿真再运行Matlab仿真,仅在Clent端(Matlab)一步到位。缺点是V-REP端的仿真行为是“失控”的,不太适合有bug时的调试。
临时远程API服务器服务
临时服务在仿真脚本中被启动。V-REP的仿真行为始终处于用户可控的状态下,比较适合调试。当然仿真结束时服务也会一起结束。总之这种方式更加受欢迎。
临时服务器服务可以通过以下两个Lua函数(对Lua不熟悉者参考后续内容)控制开始/终止:
simRemoteApi.start
simRemoteApi.stop
配图是手册给出的说明,已经十分清楚了。V-REP还提供了两个函数检测服务器状态和重置服务:
simRemoteApi.status
simRemoteApi.reset
Remote API的工作模式
V-REP远程API提供四种工作模式,四种模式各有特色,下面逐一介绍。
Blocking 函数调用
Blocking函数调用时调用远程API最简单的方式,这种模式适用于我们必须等待V-REP服务器反馈信息的情况。例如我们想要获取当前机械臂每个关节的角度时。
Non-Blocking 函数调用
如果我们不需要V-REP服务器反馈任何信息,采用Non-Blocking函数会大幅度提高效率。例如给机械臂的各个轴设定一个目标值,我们不需要等待设定完成再进行下一步操作。
还有一种情况,为了保证信号的完整性,我们可能需要向V-REP服务器发送多个需要同时处理的数据。这样可防止由于时序问题造成的混乱。此时我们可以暂停通信线程,这样发送出去的数据不会立即被接收,而是等待通信线程启动后一起接收。
Data streaming
数据流机制类似于ROS系统中的Topic机制。V-REP服务器会按照指定的频率连续发送数据,Client应用随时可获取。
Synchronous operation
上述工作模式均是异步的,即V-REP仿真的执行并没有考虑到Client应用(Matlab)端的执行情况。如果说我们的仿真有同步需求,我们可以使用同步模式。该模式需要事先被使能。
几种工作模式的关系如下图所示:
你的系统中也可以同时又多个Client同时运行,但是操作方法稍微复杂,因此不作介绍。有兴趣可以参考官方手册。
常用的Remote API (MATLAB)
所有支持的Remote API函数 (Matlab)均可以在这里找到。在这里你可以找到所有可能会用到的Remote API常量。
时间原因,此处暂时不列举常用的API函数及其用法,你可以后续的例子中体会。
Lua语言基础
这里仅介绍下操作V-REP服务器时可能需要用到的Lua语言基础,Lua语言博大精深,想深入学习的或者想完全利用Lua方式操作V-REP仿真的请参考专业教材。
Lua语言是一门以C语言为基础的动态类型的脚本语言,这意味着一个变量可以在不同时刻指向不同类型的数据。Lua的语法与Python非常相似,后续介绍的过程中我们假定你有相当完善的C语言基础。
Lua数据类型
在Lua中,我们使用一个通用的数据结构 lua_TValue 来统一表示所有在Lua虚拟机中需要保存的数据类型。这里拆开来介绍。
宏 | 类型 | 对应的数据结构 |
---|---|---|
LUA_TNONE | 无类型 | 无 |
LUA_TNIL | 空类型 | 无 |
LUA_TBOOLEAN | 布尔型 | 无 |
LUA_TLIGHTUSERDATA | 指针 | void* |
LUA_TNUMBER | 数据 | lua_Number |
LUA_TSTRING | 字符串 | TString |
LUA_TTABLE | 表 | Table |
LUA_TFUNCTION | 函数 | CClosure、LClosure |
LUA_TUSERDATA | 指针 | void* |
LUA_TTHREAD | Lua虚拟机、协程 | lua_State |
下面我们针对几种类型具体说一下:
字符串:Lua中字符串是被内化的一种数据,简单来说,就是存放的不是一份字符串的数据副本,而是这份字符串数据的引用。每当创建一个新的字符串时,系统会查找是否已存在相同的字符串数据,如果已存在,就直接复用。
a = "1"
a = a.."2"
“..”是字符串连接符,上述代码执行完后,系统中存在一个字符串“1”和一个字符串“12”。当然为了效率起价你,操作字符串时请尽可能少使用连接符,因为每次都会新创建一个字符串。
表:使用表来同意表示Lua中的一切数据是Lua语言的一大特色。Lua语言将表中的数据存放在两种类型的数据结构中,一种是数组,一种是散列表。表从 1 开始索引。支持查找、新增元素、迭代、切片操作。
local t = {}
t[1] = 0
t[100] = 0
local a = {}
for i=1,3 do
a[i] = true
end
Lua词法与函数
局部变量赋值:
local a = 10
local a,b = 10
表赋值:
local p = {}
local q = {1,2}
local p = {["a"]=1}
查询表:
local p = {["a"]=1}
local b = p["a"]
全局变量赋值:
a = 10
函数:一个函数的信息保存在FuncState结构体中
function test()
end
function f(a,b,c)
end
function test()
return 1,2,3
end
a,b = test()
local g = 2
function fun()
local a = 1
function test()
a = g
end
end
Lua流程控制
for 循环指令:
local a = 0
for i = 1,100,5 do
a = a + i
end
for k,v in pairs(t) do
print(k,v)
end
除了for循环外,还有使用while以及repeat关键字实现的循环。
if 条件判断:
a = 100
if( a == 10 )
then
print("a 的值为 10" )
elseif( a == 20 )
then
print("a 的值为 20" )
elseif( a == 30 )
then
print("a 的值为 30" )
else
print("没有匹配 a 的值" )
end
print("a 的真实值为: ", a )
一个例子
这是一个官方给出的例子,我们对其简要说明一下。
新建一个文件夹按照前文中提到的方法准备好Matlab环境。新建一个simpleTest.m文件,或者直接从V-REP的安装目录中找到这个同名文件拷贝过来。
在simpleTest.m中输入如下Matlab代码:
function simpleTest()
disp('Sim started');
vrep=remApi('remoteApi');
vrep.simxFinish(-1);
clientID=vrep.simxStart('127.0.0.1',19999,true,true,5000,5);
if (clientID>-1)
disp('Connected to remote API server');
[res,objs]=vrep.simxGetObjects(clientID,vrep.sim_handle_all,vrep.simx_opmode_blocking);
if (res==vrep.simx_return_ok)
fprintf('Number of objects in the scene: %d\n',length(objs));
else
fprintf('Remote API function call returned with error code: %d\n',res);
end
pause(2);
t=clock;
startTime=t(6);
currentTime=t(6);
vrep.simxGetIntegerParameter(clientID,vrep.sim_intparam_mouse_x,vrep.simx_opmode_streaming);
while (currentTime-startTime < 5)
[returnCode,data]=vrep.simxGetIntegerParameter(clientID,vrep.sim_intparam_mouse_x,vrep.simx_opmode_buffer);
if (returnCode==vrep.simx_return_ok)
fprintf('Mouse position x: %d\n',data);
end
t=clock;
currentTime=t(6);
end
vrep.simxAddStatusbarMessage(clientID,'Hello V-REP!',vrep.simx_opmode_oneshot);
vrep.simxGetPingTime(clientID);
vrep.simxFinish(clientID);
else
disp('Failed connecting to remote API server');
end
vrep.delete();
disp('Program ended');
end
下面从头开始对上述代码稍加说明。我们假设你对MATLAB编程非常熟悉。
vrep = remApi('remoteAPi');
每次你都会用到这一句,目的是建立一个vrep对象并加载library。最后别忘了加个vrep.delete()销毁这个对象,节省内存。
vrep.simxFinish(-1);
这一句用来关闭其它可能的服务连接,当然你若是第一次用这个大可省略,保险起见还是加上这一句。
clientID=vrep.simxStart('127.0.0.1',19999,true,true,5000,5);
这一句用来启动服务。官方说明如下。第一个参数是V-REP端的IP地址;第二个参数是端口名称;第三个参数表示此时等待连接成功或超时(block函数调用);第四个参数表示一旦连接失败,不再重复尝试连接;第五个参数是超时时间设定(毫秒);第六个参数指的是数据包通信的频率,默认为5(毫秒)。返回值是当前Client的ID,如果是-1,那么表示未能连接成功。
每一个simxStart最后都务必加一个simxFisnish。
[res,objs]=vrep.simxGetObjects(clientID,vrep.sim_handle_all,vrep.simx_opmode_blocking);
if (res==vrep.simx_return_ok)
fprintf('Number of objects in the scene: %d\n',length(objs));
else
fprintf('Remote API function call returned with error code: %d\n',res);
end
这一段代码是一个block函数调用模式的示例。
t=clock;
startTime=t(6);
currentTime=t(6);
vrep.simxGetIntegerParameter(clientID,vrep.sim_intparam_mouse_x,vrep.simx_opmode_streaming);
while (currentTime-startTime < 5)
[returnCode,data]=vrep.simxGetIntegerParameter(clientID,vrep.sim_intparam_mouse_x,vrep.simx_opmode_buffer);
if (returnCode==vrep.simx_return_ok)
fprintf('Mouse position x: %d\n',data);
end
t=clock;
currentTime=t(6);
end
这一段代码时non-block函数调用的示例,接收一系列数据流。
vrep.simxAddStatusbarMessage(clientID,'Hello V-REP!',vrep.simx_opmode_oneshot);
这一段代码向V-REP发送数据(non-block)
vrep.simxGetPingTime(clientID);
这一段代码确保连接关闭前所有的指令已经发出。
可以看出,这里用到了很多特定的常量,官方手册中给出了这些常量的列表。
准备好MATLAB程序之后,我们打开V-REP,系统自动新建了一个scene。此时从模型浏览器中找到UR5的仿真模型(其它你喜欢的机器人也行)拖动到scene中。打开UR5的脚本,在最上方第一行处添加一行:
simRemoteApi.start(19999)
保持scene,先启动V-REP 仿真,然后运行MATLAB程序(可见此时是临时服务)。观测MATLAB的命令窗口输出以及V-REP的输出。图文教程可参考这篇文章。
下一篇: apidoc 使用说明