深度解析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的仿真为例进行介绍。
V-REP

介绍之前,我想多说一句,无论什么时候,官方手册都是最好的参考资料,实践才是最好的老师。本文针对想要快速入门的初学者,以尽量言简意赅的方式引领读者入门。读完后你可以自己搭建一个可用的仿真平台,并对仿真的过程有初步的了解。部分介绍中存在不严谨的措辞还请谅解。

准备MATLAB环境

对应于Windows系统,你只需要准备三个文件,而且都可以在V-REP的安装目录下找到:

  1. remoteApiProto.m
  2. remApi.m
  3. 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服务器服务提供两种模式:

  1. Continuous(连续)远程API服务器服务
  2. 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
start
simRemoteApi.stop
stop
配图是手册给出的说明,已经十分清楚了。V-REP还提供了两个函数检测服务器状态和重置服务:

simRemoteApi.status
gather
simRemoteApi.reset
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)端的执行情况。如果说我们的仿真有同步需求,我们可以使用同步模式。该模式需要事先被使能。

几种工作模式的关系如下图所示:
modus operandi

你的系统中也可以同时又多个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
每一个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函数调用模式的示例。
simxGetObjects

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函数调用的示例,接收一系列数据流。
simxGetIntegerParameter

vrep.simxAddStatusbarMessage(clientID,'Hello V-REP!',vrep.simx_opmode_oneshot);

这一段代码向V-REP发送数据(non-block)
simxAddStatusbarMessage

vrep.simxGetPingTime(clientID);

这一段代码确保连接关闭前所有的指令已经发出。
simxGetPinTime
可以看出,这里用到了很多特定的常量,官方手册中给出了这些常量的列表

准备好MATLAB程序之后,我们打开V-REP,系统自动新建了一个scene。此时从模型浏览器中找到UR5的仿真模型(其它你喜欢的机器人也行)拖动到scene中。打开UR5的脚本,在最上方第一行处添加一行:

simRemoteApi.start(19999)

保持scene,先启动V-REP 仿真,然后运行MATLAB程序(可见此时是临时服务)。观测MATLAB的命令窗口输出以及V-REP的输出。图文教程可参考这篇文章

猜你喜欢

转载自blog.csdn.net/philthinker/article/details/79848287
今日推荐