基于ROSWEB的WAM简单功能开发

平台

  操作系统:Ubuntu18.04Windows 10
  操作平台:Barrett七自由度机械臂
  Web前端框架:Node.js + Vue.js + Element UI
  ROSWeb开发工具:Robot Web Tools(roslibjs+ros2djs+ros3djs)
  开发工具:Visual Studio Code
  服务器:Tomcat8.5

环境搭建

基础环境搭建

  参照基于Vue.js的ROSWeb环境搭建文档。

ROSWeb工具简介

  ros-webros官方提供的从rosweb端的一个开源接口。其主要的功能库有3个,即roslibjs基本库,ros2djs2D库和ros3djs3D库。本项目主要用的是roslibjsros3djs

roslibjs库文件

  roslibjs库是ros-web中的基础库,可以实现创建ROS,订阅消息、发布话题和调用服务等功能。其中比较重要的类有ROSTopicService

ROS

  从ROS类的源码中可以看出,其底层也是基于socket协议实现的rosweb连接。ROS类在定义时,只需要提供ros端的IP和端口号即可(需要和rosbridge部分对应),如:

1
2
3
var ros = new ROSLIB.Ros({
url: 'ws://192.168.*.*:9090'
})

  定义完该类后,可以使用ros.on()函数实时监听ros的系统状态。Ros类一共提供了3种状态,即connectionerrorclose,分别对应连接成功、连接错误和关闭连接状态。在对应的回调函数里面可以编写相应的逻辑功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
this.ros.on('connection', () => {
this.$message({
message: '连接成功!',
type: 'success'
})
})

this.ros.on('error', (e) => {
this.$message.error('连接失败!')
})

this.ros.on('close', () => {
this.$message.error('关闭连接!')
})
```

### `Topic`类
```js
var wamPost = new ROSLIB.Topic({
ros: ros,
name: '/wam/pose',
messageType: 'geometry_msgs/PoseStamped'
})

wamPost.subscribe((message) => {
console.log(message)
wamPost.unsubscribe()
})

  Topic类的构造函数需要给定3个参数,即ros类实例,话题名称name和话题的消息格式messageType。实例化类后,即可调用subscribe()函数订阅话题,并通过回调函数里面的messgage值获取话题信息,在回调函数中可以使用unsubscribe()函数取消订阅。

1
2
3
4
5
6
7
8
var cmdVel = new ROSLIB.Topic({
ros: ros,
name: '/turtle1/cmd_vel',
messageType: 'geometry_msgs/Twist'
})

geometryMsgsTwist.linear.x = 1
cmdVel.publish(geometryMsgsTwist)

  除了可以订阅话题并获取信息,还可以发布话题。实例化话题类的方法和订阅一样,但需要提前定义好需要发布的话题消息格式和内容。

1
2
3
4
5
6
7
8
9
10
11
12
var geometryMsgsTwist = new ROSLIB.Message({
linear: {
x: 0.0,
y: 0.0,
z: 0.0
},
angular: {
x: 0.0,
y: 0.0,
z: 0.0
}
})

  消息的定义为对象类型的键值对数据,需要根据实际话题的消息类型来具体定义。
  定义好要发送的话题消息数据后,使用publich()发布话题。这里不需要订阅话题。

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var setPenClient = new ROSLIB.Service({
ros: ros,
name: '/turtle1/set_pen',
serviceType: 'turtlesim/SetPen'
})

var request = new ROSLIB.ServiceRequest({
r: 255,
g: 0,
b: 0,
width: 2,
off: 0
})

setPenClient.callService(request, (result) => {
console.log(result)
})

  Service类的构造函数也需要给定3个参数,即ros类实例,服务名称name和服务的消息格式serviceType。这里同样需要先定义好服务的请求信息,格式为对象类型的键值对数据。实例化服务类后,可以通过callService()函数调用服务。

ros3djs库文件

  ros3djs用于可视化3D模型相关的功能。其中比较重要的类有ViewerTFClientUrdfClient等。阅读底层代码可以发现,ros3djs是基于three.js 3D库实现的。

Viewer

1
2
3
4
5
6
7
8
9
var viewer = new ROS3D.Viewer({
// 对应<div>元素
divID: divID,
width: width,
height: height,
// 平滑 抗锯齿
antialias: true,
background: color
})

  Viewer用于创建一块用于显示3D模型的可视化区域。需要的参数为htmldiv区域ID divID,区域的宽度width,区域的高度heigh,是否抗锯齿antialias和背景颜色background等信息。实例化类后,即可在网页中看到一块空白区域。如需添加网格,调用ddObject(new ROS3D.Grid())函数即可。

TFClient

1
2
3
4
5
6
7
var tfClient = new ROSLIB.TFClient({
ros: ros,
angularThres: 0.01,
transThres: 0.01,
rate: 10.0,
fixedFrame: fixedFrame
})

  TFClient类是一个基础类,后续的一些类的参数经常需要使用它,构造函数的参数值按照官网提供的参数值即可,其中比较重要的一个参数是fixedFrame参数,这里需要和urdf模型中的基础坐标系相对应,否则会出现模型加载不完整,无法控制移动等问题。

UrdfClient

1
2
3
4
5
6
7
8
9
var urdfClient = new ROS3D.UrdfClient({
ros: ros,
tfClient: tfClient,
// path: 'http://resources.robotwebtools.org/',
path: 'http://localhost:8080/static',
rootObject: viewer.scene,
loader: ROS3D.COLLADA_LOADER_2
// loader: ROS3D.COLLADA_LOADER
})

  UrdfClient类可以可视化ros中的urdf模型,其主要的参数有实例化的ros,实例化的tfClient,模型存放的路径path,根对象rootObject(即urdf放在哪个区域)和模型加载器loader。这里比较重要的参数为path路径参数,这里存放的是网络或者本地的模型链接地址,本项目使用的vue.js前端框架,http://localhost:8080网址对应的即为整个项目文件夹的根目录,因此实际的模型地址位于http://localhost:8080/static中(需要将模型放在该文件夹下)。

设计思想

界面设计

主界面
  整体界面分为3个部分,上面为logo显示和一些常用的功能选项。下面左侧为WAM 3D模型的可视化显示,右侧为功能逻辑部分,主要测试一些简单的基础功能。
  其中WAM模型在连接成功会实时显示当前的位姿,数据也会实时更新在右下方的表格中。拖动右侧的滑动也会实时更新WAM的位姿状态。

逻辑功能

  ROS中常见的功能主要有订阅消息发布话题调用服务。另外还需要将3D模型能够可视化显示在web网页中。因此本项目主要实现的逻辑功能包括:

  1. 订阅消息(Subscribing message)
  2. 发布话题(publishing topic)
  3. 调用服务(Calling service)
  4. 针对urdfxacro文件的3D模型可视化

代码模块

添加依赖文件

  在vue.js的静态文件夹static中新建文件夹rosweb,用于存放所需要的第三方JavaScript文件。
rosweb文件夹
  其中roslib.jsros3d.jsros-web提供的库文件,剩余JavaScript文件为用于绘制3D可视化模型的库文件(主要为three.js框架及其依赖库)。
  然后在index.html入口文件中,将这些文件添加<head>标签中。

1
2
3
4
5
6
<script src="./static/rosweb/three.js"></script>
<script src="./static/rosweb/ColladaLoader.js"></script>
<script src="./static/rosweb/STLLoader.js"></script>
<script src="./static/rosweb/eventemitter2.min.js"></script>
<script src="./static/rosweb/roslib.js"></script>
<script src="./static/rosweb/ros3d.js"></script>

  由于ros-webros3d.js库文件在加载3D模型时,需要本地或者网络的模型链接,因此需要将ros中的模型部分放在static文件夹中。这里将barrett_ros中的barrett_model部分添加至static文件夹中:
barrett_model

主界面功能实现

  代码文件位于src->components->HelloWorld.vue。基本按照ROSWeb工具简介解释的部分,更改为实际的话题/服务和消息的数据类型即可。

常见问题整理

  1. index.html中导入完js文件,需要在.eslintrc.js中的rules.globals声明全局变量:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // add your custom rules here
    rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-unused-vars': 'off'
    },
    "globals":{
    "THREE": true,
    "ROSLIB": true,
    "ROS3D": true,
    }

  否则会出现无法找到该变量的定义的问题。

  1. 回调函数要改用为箭头函数,这样就可以使用使用this对象了。

  2. 解析订阅话题的接收到的值时,需要根据实际的格式对其解析。

  3. 如果遇到ctrl+c关闭节点后,节点仍然存在的问题:在环境变量中添加export ROS_HOSTNAME=hostname(输入指令hostname即可查看自己的),且在关闭节点时不要使用ctrl+c,直接关闭窗口。

  4. 连续发布多个话题或调用多个服务时,只会执行最后一个。这是ros系统的问题,话题是异步通讯机制,发布相同消息时,只会处理最后一个,服务是同步通讯机制,可以通过判断返回的信息来实现依次调用相同的多个服务。

谢谢老板!
-------------本文结束感谢您的阅读给个五星好评吧~~-------------