黄弢,汪迪,万晨晖,马瑞启,许 洋
(华中科技大学机械科学与工程学院,武汉 430074)
实验教学是高校创新实践人才培养的重要手段[1],机器人教学是工程类实验教学中重要的组成部分[2],在实验教学中,受限于现场条件和传统工业机器人的价格,可用的实验资源往往有限,学习成本较高。借助计算机仿真技术,可在软件中集成虚拟仿真模块,自定义仿真场景[3]。
目前,国内外比较通用的机器人仿真软件有VREP、Gazebo和Webots 等[4]。V-REP是一款灵活的通用机器人仿真器,支持嵌入式脚本,插件和ROS 节点[6],自2019 年后改名为CoppeliaSim,V-REP已经停止更新。Gazebo可构建机器人运动仿真模型,支持传感器数据的仿真[7],主要配合ROS 仿真在Linux 平台使用。Webots自带众多不同种类传感器和驱动器[8],近年来在四足类机器人仿真上应用广泛[9]。现有平台的不足之处在于,它们只是一个仿真工具,而非一个实验工具,为完成实验往往需要多个软件相互配合,学习成本高,开发一款具备机器人仿真功能与实验设计的平台则具有较大意义。
工业机器人中应用较为广泛的是示教编程,即通过示教器控制机器人移动,在运动过程中记录相应的关节位置,完成示教操作。这种示教方式过于简单和低效,并不适用于机器人学科教学。一种较好的方式是类似于Matlab 脚本化、交互式编程,既可通过类的方式封装简易调用函数,也可以赋予用户自己实现算法的能力。
为使机器人仿真实验平台更具灵活性和可扩展性,采用以Bullet为仿真引擎,以Lua 为交互式操作语言、Qt为图形框架和节点图和数据流驱动为实验系统的技术方案。体架构设计如图1 所示。
图1 基于Bullet引擎的机器人仿真实验平台整体架构
其中,仿真层为机器人仿真的核心实现,由OpenGL提供场景管理与视图管理,Bullet 提供物理运算,如碰撞检测、运动学运算和动力学运算等。操纵层面向用户,以函数和功能节点的方式向用户提供对仿真层的功能访问。
Bullet是一种应用广泛的物理引擎,支持多个平台[10]。作为一款物理引擎,它并没有提供可视化对象的方法,为可视化Bullet对象,还需要使用OpenGL,它的接口与图形卡硬件非常接近,处理底层命令,尤其是图形应用程序,是一项烦琐的任务,例如Glut、FreeGlut、Glfw库可以用来简化工作,这类库通常可称为包装器,另外它还负责应用程序的启动、控制和输入处理[13]。
物理计算方面由Bullet 提供的相关接口实现,主要涉及的核心对象有世界对象、碰撞配置、碰撞检测、约束解算器和碰撞调度器等多种。物理和图形部分需要进行运动状态绑定和几何更新,以便根据物理引擎计算的位姿变换矩阵更新几何显示。采用物理和图形分离的架构能降低程序耦合,便于功能扩展[14]。
图2 渲染场景的一般流程
在创建OpenGL上下文之后,将解析OpenGL函数指针,即调用硬件平台的函数实现,所有OpenGL命令都会用来改变当前上下文的状态信息,OpenGL 根据上下文的状态信息进行渲染工作。
Bullet保持其核心组件的相同模块化设计,甚至包括单个物理对象。这使得可通过随意互换或替换物理对象的组件来定制它们。在Bullet中构建物理对象需要3 个组件:
(1)碰撞形状(Collision Shape)。定义物体的体积和形状,可以是长方体、球体、圆柱体或其他更为复杂的形状,决定了物体与其他物体在碰撞时的响应方式。
(2)运动状态(Motion State)。跟踪物体的运动状态,运动状态的工作是记录对象的当前位置和方向。
(3)碰撞对象(Collision Object)。作为对象的主控制器,管理前面提到的组件和对象的物理属性。碰撞物体是物理物体的基本组成部分,因为它们保持了物体的物理属性。
文献[19]显示,当模型的水平范围为8~10倍隧道直径时,即可获得较高的计算精度。本文建立了二维弹塑性动力有限元模型,模型水平方向为80 m,竖直方向为60 m,盾构隧道直径为6 m。
从创建Bullet对象,配置相应的物理参数,到步进模拟,场景渲染基本流程如图3 所示。
图3 场景渲染、基本流程
在创建Bullet对象之后,在OpenGL中同步创建几何形状,其关键在于运动状态的绑定,这一步骤是通过指针方式设定运动状态。既当物理模型中的运动状态更新时,用于描述运动状态的变量值会同步更新,当渲染场景时,通过指针来访问描述运动状态的变量,此时即可实现状态的同步更新。
Lua 是一种嵌入式脚本语言,扩展性好,容易与C/C++语言结合。Lua的目标是仅提供一些很有限但却很强大的语言机制[11]。Lua 是基于关联数组和扩展性语法结构设计的语言,支持动态定义类型,内存自动回收机制,常被用于嵌入其他宿主语言[12]。
将Bullet相关功能封装为C ++接口,即可实现基于C++语言的机器人仿真,但C ++程序需要编译,无法脱离开发环境存在,不便于用在实际的教学环境。Lua语言提供了一种在Lua中调用C++函数的机制。
Lua与C/C++语言之间的通信,主要是通过一个虚拟堆栈进行。Lua 给C ++语言提供了API 来对堆栈进行操作,这些API 就好比两者之间通信的协议。C++与Lua的常规绑定方法如下:
(1)在C++中提供具体的函数
(2)在C++中按照指定格式提供回调函数,调用具体的函数
(3)在C++中注册回调函数
可见,对于每一个需要在Lua 中调用的C ++函数,都需要一系列较为繁琐的绑定过程。对于Qt环境而言,使用QtLua 库可以大大简化这一过程。它提供了有用的C++包装器类,以使Lua 和C ++都可访问C++和Lua 对象。利用Qt 元对象系统的优势将QObject成员公开给Lua脚本。其特点如下:
(1)不公开基于Lua 堆栈的CAPI,仅从C ++代码操纵QtLua::State和QtLua::Value 之类的C ++对象。
(2)通过继承QtLua::UserData类,可将C++对象作为用户数据值提供给Lua。可通过覆盖虚拟函数来重新定义所有Lua 元操作,包括对来自Lua 的userdata对象的迭代。
(3)可以将QtLua::Function 类子类化,以轻松定义可从Lua 调用的C ++函数对象。使用QtLua 绑定Lua与C++的基本流程如图4 所示。
图4 在C++中绑定Lua的基本流程
创造LuaState,用于储存Lua 变量值的虚拟堆栈,启用Lua的标准库,注册自定义对象。如自定义变量(属性)、自定义函数对象和自定义类对象。然后就可以通过state执行Lua语句,Lua中注册的C ++对象均可通过Lua语句来调用或赋值。
基于节点图的控制方式是对基于Lua控制方式的一层封装,实现了以事件驱动的方式来调用Lua,并将设备和功能块抽象为节点,节点之间的数据传递方式通过连线来定义。
在现有基于Lua 脚本的控制模式下,已可进行构建机器人仿真场景,分析仿真结果等操作。在复杂的机器人场景中,往往需要多台机器人相互协作共同完成实验任务,为实现灵活的机器人运动控制,可根据不同实验场景进行机器人组态[16]。本平台设计了一种基于事件驱动的图形节点编程工具。
节点编辑工具在建模类工具中应用广泛,如Blender的材质编辑和模型渲染节点图。其基本流程为事先定义数据模型并将其注册到数据模型注册表,构造节点并与对应的数据模型进行绑定,用户操作信号或者定时器信号、通信信号等将触发模型计算,计算结果将传播到输出连接,每个新连接都会获取可用的数据并进一步传播,数据节点的组成如图5 所示。
图5 数据节点的组成
数据节点采用数据模型和图形节点分离的设计模式,数据模型负责定义端口、数据输入、输出和数据运算等,而图形节点则定义了图形的外观样式、几何形状和连接状态等。
本平台将图形节点和Lua 脚本融合,实现一种形如Simulink中Matlab Function的效果。在节点从构造到数据输入、输出的不同阶段,将执行不同的函数。Lua脚本节点生命周期中所执行的函数如图6 所示。
图6 Lua脚本节点生命周期中所执行的函数
其中构造函数和初始化函数只执行一遍,而数据输入触发的运算类函数将在每次输入数据时执行。在Lua函数中通过调用获取节点数据函数,将数据从C++环境中拷贝到Lua 环境,编写相应的处理逻辑,调用输出节点数据函数,将数据节点数据传播到下个节点。
节点图的典型优势在于基于数据流驱动的控制逻辑,避免了循环扫描指令的CPU 负荷,同时图形化交互和事件驱动方式符合人的控制直觉。可将典型的功能块封装为图形节点,用户只需要考虑输入和输出,而不用关注内部的实现细节。节点之间具有低耦合的特性,可根据实际的逻辑将不同的节点进行连接和组合。
图7 是一个G代码解析并控制Scara机器人绘图的一个案例,Gcoder 模块负责G 代码的解析,将解析得到的点位通过输出端口输出,连接到一个散点图模块和机器人“反解驱动”模块,“反解驱动”模块的输出端口输出机器人实际末端轨迹。
图7 Scara机器人绘图案例
Scara机器人绘图实验流程如图8 所示。
图8 Scara机器人绘图流程
通过Gcoder模块导入G代码文件并解析,将开启一段定时循环,不断输出点位数据,虚线框中的内容在“反解驱动”节点中实现。G代码解析并执行完毕后,运行效果如图9 所示。
图9 Scara机器人绘图效果
发现最终运动得到的路径与期望路径基本一致,达到预期的实验目的,如图10 所示。
图10 机器人末端理论轨迹与实际轨迹
本实验平台支持多种实验场景,通过导入不同机器人模型可进行不同的实验。得益于Bullet的碰撞检测功能,能在机器人场景中模拟抓取实验。Scara机器人抓取案例脚本实验界面如图11 所示。
图11 Scara机器人抓取案例脚本
游戏手柄是一种典型的输入设备,通过操纵按钮和摇杆可以向计算机发送相应的触发信号和键值,是一种典型的事件发生装置。在实验平台中,游戏手柄作为输入设备被定义为功能节点,它的按键信号被映射到节点的输出端口,并与后续的逻辑处理节点相连接。当按下或松开手柄的某一个键时,对应的端口将发射一个数据更新信号,并将数据传递到下一个节点。
手柄的前后左右摇杆来控制Scara 机器人末端的平面移动方向,上下键来控制机器人升降。而B、A功能键用于控制末端执行器——卡爪的开合,Scara机器人抓取效果如图12 所示。
图12 Scara机器人抓取效果
由Scara机器人解析G代码绘图和抓取物体实验中可见,本平台能够快速搭建仿真实验,并对结果进行实时可视化分析。机器人功能的节点化使得实验平台具有较高的可重构性和可扩展性,能适应机器人实验的多样化。
基于Bullet 设计了一种机器人仿真实验平台,具备运动学解算、碰撞检测与轨迹可视化功能。通过使用数据流节点图作为逻辑手段,将计算模块和通信模块封装为功能节点,大大降低了实验搭建的难度。通过图形连接定义运算逻辑的方式,便于用户快速理解实验架构。应用实例表明,该平台功能完善,可扩展性强,兼具脚本语言的灵活性与图形化语言的便捷性,适用机器人的多种实验场景。