Android使用ROSBridge与ROS通信 简单使用
ROS官方只支持了C++和Python,想要在Android上与ROS通讯,我的选择是ROSBridge
环境
ROS kinetic
ROS 服务端
安装
1 | sudo apt-get install ros-<rosdistro>-rosbridge-suite |
启动
1 | roslaunch rosbridge_server rosbridge_websocket.launch |
在这之前不需要开启 roscore, 因为 rosbridge 会默认执行 roscore
Android客户端
要让 android 接收或者发送 ROS 消息的话,首先要在 Android上完成 websocket,然后按照协议解析,也很麻烦,不过又要站在巨人的肩膀上了,找到一个开源项目:ROSBridgeClient,这位同学使用 java-websocket 的包在Android上实现了 websocket 的应用,很棒。
直接把 src/com/jilk/ros
目录复制到 我的 Android 项目里,
当然会报错啦,这些代码依赖了第三方库,加在Android工程的libs 里面 引用
- eventbus.jar 用于发送从ROS接收到的消息
- java_websocket.jar 用于websocket 的实现
- json-simple-1.1.jar 用于json解析
复制到项目包里的 代码包含了一个 example .
完全可以使用
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | public class Example { public Example() {} public static void main(String[] args) { ROSBridgeClient client = new ROSBridgeClient( "ws://162.243.238.80:9090" ); client.connect(); //testTopic(client); try { testService(client); } catch (RuntimeException ex) { ex.printStackTrace(); } finally { client.disconnect(); } } public static void testService(ROSBridgeClient client) { try { Service<Empty, GetTime> timeService = new Service<Empty, GetTime>( "/rosapi/get_time" , Empty. class , GetTime. class , client); timeService.verify(); //System.out.println("Time (secs): " + timeService.callBlocking(new Empty()).time.sec); Service<com.jilk.ros.rosapi.message.Service, Type> serviceTypeService = new Service<com.jilk.ros.rosapi.message.Service, Type>( "/rosapi/service_type" , com.jilk.ros.rosapi.message.Service. class , Type. class , client); serviceTypeService.verify(); String type = serviceTypeService.callBlocking( new com.jilk.ros.rosapi.message.Service( "/rosapi/service_response_details" )).type; Service<Type, MessageDetails> serviceDetails = new Service<Type, MessageDetails>( "/rosapi/service_response_details" , Type. class , MessageDetails. class , client); serviceDetails.verify(); //serviceDetails.callBlocking(new Type(type)).print(); Topic<Log> logTopic = new Topic<Log>( "/rosout" , Log. class , client); logTopic.verify(); /* System.out.println("Nodes"); for (String s : client.getNodes()) System.out.println(" " + s); System.out.println("Topics"); for (String s : client.getTopics()) { System.out.println(s + ":"); client.getTopicMessageDetails(s).print(); } System.out.println("Services"); for (String s : client.getServices()) { System.out.println(s + ":"); client.getServiceRequestDetails(s).print(); System.out.println("-----------------"); client.getServiceResponseDetails(s).print(); } */ } catch (InterruptedException ex) { System.out.println( "Process was interrupted." ); } /* Service<Empty, Topics> topicService = new Service<Empty, Topics>("/rosapi/topics", Empty.class, Topics.class, client); Service<Topic, Type> typeService = new Service<Topic, Type>("/rosapi/topic_type", Topic.class, Type.class, client); Service<Type, MessageDetails> messageService = new Service<Type, MessageDetails>("/rosapi/message_details", Type.class, MessageDetails.class, client); try { Topics topics = topicService.callBlocking(new Empty()); for (String topicString : topics.topics) { Topic topic = new Topic(); topic.topic = topicString; Type type = typeService.callBlocking(topic); MessageDetails details = messageService.callBlocking(type); System.out.println("Topic: " + topic.topic + " Type: " + type.type); details.print(); System.out.println(); } Type type = new Type(); type.type = "time"; System.out.print("Single type check on \'time\': "); messageService.callBlocking(type).print(); } catch (InterruptedException ex) { System.out.println("testService: process was interrupted."); } */ } public static void testTopic(ROSBridgeClient client) { Topic<Clock> clockTopic = new Topic<Clock>( "/clock" , Clock. class , client); clockTopic.subscribe(); try {Thread.sleep( 20000 );} catch (InterruptedException ex) {} Clock cl = null ; try { cl = clockTopic.take(); // just gets one } catch (InterruptedException ex) {} cl.print(); cl.clock.nsecs++; clockTopic.unsubscribe(); clockTopic.advertise(); clockTopic.publish(cl); clockTopic.unadvertise(); } } |
example很好理解
就看了下 topic 相关的东西 testTopic,我觉得如果有很多topic就要用很多的testXXXTopic了,有点麻烦,所以我二次封装了一个 RosBridgeClientManager 来用
连接 ROS master
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 29 30 31 32 33 34 35 36 37 38 39 40 | /** * 连接 ROS master * @param url ROS master IP * @param port ROS master 端口 * @param listener 连接状态监听器 */ public void connect( final String url, int port, final ROSClient.ConnectionStatusListener listener) { if (url != null && url.equals(mCurUrl)) { // already connected } else { mRosBridgeClient = new ROSBridgeClient( "ws://" + url + ":" + port); mRosBridgeClient.connect( new ROSClient.ConnectionStatusListener() { @Override public void onConnect() { // connected successful mCurUrl = url; if (listener != null ) { listener.onConnect(); } } @Override public void onDisconnect( boolean normal, String reason, int code) { // client disconnected if (listener != null ) { listener.onDisconnect(normal, reason, code); } } @Override public void onError(Exception ex) { // connect error if (listener != null ) { listener.onError(ex); } } }); } } |
加了一个连接监听器,可以在业务层进行状态判断了。
注册topic 到 ROS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * 注册topic * @param topicName topic 名称 * @param data_type 消息类型 * @param <T> */ public <T> void advertiseTopic(String topicName, T data_type) { AdvertiseTopicObject<T> topic = new AdvertiseTopicObject<>(topicName, data_type, mRosBridgeClient); topic.setMessage_type(data_type); topic.advertise(); // 利用 反射获取泛型,主要是得到 T.class,我也没试 // Class <T> entityClass = (Class <T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; // Topic topic = new Topic(topicName, entityClass, client); // topic.advertise(); } |
原来的 advertise 已经很简单了,为什么我还要弄这个东西? 我也不知道啊
AdvertiseTopicObject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class AdvertiseTopicObject<T> { private T message_type; private String topicName; private ROSBridgeClient client; public AdvertiseTopicObject(String topicName, T type, ROSBridgeClient rosBridgeClient) { this .client = rosBridgeClient; this .topicName = topicName; this .message_type = type; } public void advertise() { Topic topic = new Topic(topicName, message_type.getClass(), client); topic.advertise(); } } |
发布topic 消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 发布 topic 消息 * @param topicName topic名称 * @param msg 消息 * @param <T> 消息类型 */ public <T> void publishTopic(String topicName, T msg) { PublishTopicObject<T> publishTopicObject = new PublishTopicObject<>(); publishTopicObject.setTopic(topicName); publishTopicObject.setMsg(msg); String msg_str = mGson.toJson(publishTopicObject); mRosBridgeClient.send(msg_str); } |
跟上面的 AdvertiseTopicObject 保持一致,所以有了
PublishTopicObject.java
1 2 3 4 5 6 7 8 | public class PublishTopicObject<T> { private String op = "publish" ; private String topic; private T msg; } |
订阅 topic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * 订阅topic * @param topicName topic 名称 * @param listener 消息监听器 */ public void subscribeTopic(String topicName, OnRosMessageListener listener { SubscribeTopicObject subscribeTopicObject = new SubscribeTopicObject(); subscribeTopicObject.setTopic(topicName); String msg_str = mGson.toJson(subscribeTopicObject); mRosBridgeClient.send(msg_str); addROSMessageListener(listener); } |
同理:跟上面的 PublishTopicObject 保持一致,所以有了
SubscribeTopicObject.java
1 2 3 4 5 6 7 8 9 10 | public class SubscribeTopicObject { private String op = "subscribe" ; private String topic; public String getOp() { return op; } } |
取消订阅 topic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * 取消订阅topic * @param topicName * @param listener */ public void unSubscribeTopic(String topicName, OnRosMessageListener listener) { UnSubscribeTopicObject unSubscribeTopicObject = new UnSubscribeTopicObject(); unSubscribeTopicObject.setTopic(topicName); String msg_str = mGson.toJson(unSubscribeTopicObject); mRosBridgeClient.send(msg_str); removeROSMessageListener(listener); } |
还有 UnSubscribeTopicObject.java
public class UnSubscribeTopicObject { private String op = "unsubscribe"; private String topic; }
接收ROS 消息
//Receive data from ROS server, send from ROSBridgeWebSocketClient onMessage() // using eventbus ?! public void onEvent(final PublishEvent event) { Log.d("TAG", event.msg); for (int index = 0 ; index < mROSListenerList.size(); index++) { mROSListenerList.get(curIndex).onStringMessageReceive(event.name, stringData); //mROSListenerList.get(curIndex).onImageMessageReceive(event.name, imageData); } }
在 ROSBridgeWebSocketClient.java 里面找到了接收消息后发出来的代码
EventBus.getDefault().post(new PublishEvent(operation,publish.topic,content));
Emmm... 用的是 EventBus,先用起来再说。
还有就是关于服务的封装了,没写。
完成代码在 Github gist
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决
2016-04-05 Fragment之我的解决方案:Fragmentation
2016-04-05 Fragment全解析系列(二):正确的使用姿势
2016-04-05 Fragment全解析系列(一):那些年踩过的坑
2016-04-05 他们都没告诉你适配 Android N 需要注意什么