欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深度解析V-REP Remote API (MATLAB) 的应用

程序员文章站 2024-03-02 16:22:34
...

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 Remote API (MATLAB) 的应用

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

准备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
深度解析V-REP Remote API (MATLAB) 的应用
simRemoteApi.stop
深度解析V-REP Remote API (MATLAB) 的应用
配图是手册给出的说明,已经十分清楚了。V-REP还提供了两个函数检测服务器状态和重置服务:

simRemoteApi.status
深度解析V-REP Remote API (MATLAB) 的应用
simRemoteApi.reset
深度解析V-REP Remote API (MATLAB) 的应用

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

几种工作模式的关系如下图所示:
深度解析V-REP Remote API (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,那么表示未能连接成功。
深度解析V-REP Remote API (MATLAB) 的应用
每一个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函数调用模式的示例。
深度解析V-REP Remote API (MATLAB) 的应用

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函数调用的示例,接收一系列数据流。
深度解析V-REP Remote API (MATLAB) 的应用

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

这一段代码向V-REP发送数据(non-block)
深度解析V-REP Remote API (MATLAB) 的应用

vrep.simxGetPingTime(clientID);

这一段代码确保连接关闭前所有的指令已经发出。
深度解析V-REP Remote API (MATLAB) 的应用
可以看出,这里用到了很多特定的常量,官方手册中给出了这些常量的列表

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

simRemoteApi.start(19999)

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