[转]使用rosbridge协议实现安卓跟ros的解耦
安卓与ROS通信的现状
因为ROS官方支持的语言绑定只有C++和Python,所以目前安卓想与ROS通信,必须借助半官方的rosjava包,而Rosjava太重了,因为它跟C++/Python一样,是一个全功能的ROS绑定,意即你可以在Java(android)平台上创建Master Node,然后其他Node(C++/Python)可以连上这个Master,进行分布式通信!这对于桌面Java或许还能接受,但对于android实在是过于复杂了。
另外,rosjava的gradle脚本太复杂,需要很深的gradle知识才能将其集成到自己的android工程,很多公司(比如我们-_-!)嫌麻烦直接导入rosjava的demo工程,然后将自己的代码添加进去,团队里如果有新人加入,则还要重新搭建一个rosjava环境,太麻烦了。
rosbridge协议
很多人都觉得移动平台or嵌入式系统要实现跟ROS进行分布式通信成本太高昂,大家寻思用C/S架构可能更符合移动平台的需求,于是提出了rosbridge协议,该协议的基本思想是将节点间的分布式通信,改成client节点与一个代理节点进行C/S通信,然后代理节点再将请求转发给server节点,这样移动端就不需要实现整个ROS平台,只需要跟代理节点通信即可。
这是一篇比较rosjava跟rosbridge优劣的pdf,简单来说,就是移动平台以牺牲做server节点的代价,换来了轻量级ROS交互的能力。不过特殊情况下rosjava还是有用的,比如机器人的底盘调用机械臂的service,如果机械臂只支持rosbridge,则调用不可行——不过这种情况通过pub/sub应该也能解决。
ROSBridgeClient库
要让android能收发rosbridge消息,首先要支持WebSocket这种特殊的传输通道才能实现android接收ROS端publish(推送)过来的消息,目前实现WebSocket的大部分集中在桌面Java,比如Jetty、Netty等,其中Jetty因为用到了某些Dalvik VM不支持的java类而导致不能在android上使用。另外android自身的webview对WebSocket的支持较晚,不能保证全机型覆盖,所以Java-WebSocket这个用java.nio包里的类实现的WebSocket就脱颖而出。
有了传输通道,剩下的就是怎么组包发送了,这个库不仅要将发送的Java类型转换成语言无关的ROS消息类型(反之亦然),还要将ROS操作(订阅、发布、调用service、广播topic等)转换成rosbridge里规定的json串,在评估了java_rosbridge库和ROSBridgeClient库后,我选择了后者,因为前者虽然更小巧,但是用的jetty来实现WebSocket通信,没法跑在android上。
添加std_msgs包里的消息类型
在试用ROSBridgeClient库的过程中,我发现作者连std_msgs里的消息类型——例如String——都没有实现,却而代之的,是一个精巧的注解加反射机制实现的meta message类型,要扩展很简单,见我fork出来的repo 。
在摸索std_msgs的过程中,我弄明白一个机制:ROS的内置类型其实并不是实际存在的,它必须对应到具体语言的内置类型,所以为了跨语言通信,所有Message只能使用Wrapper类型,这就解释了为什么std_msgs包里一堆wrapper了,因为每个msg文件里的内置类型(比如string),都是不存在的,必须对应到Java的String,或Python的str、或C++的std::string,唯独不能对应ROS的string,因为ROS不是一门语言。