Android 与ROS机器人交互APP开发笔记-连接机器人

前言

随着人工智能和机器人技术的不断发展,智能机器人已经逐渐融入我们的生活和工作中。而在众多技术的融合中,Android系统和ROS(Robot Operating System)相结合,为智能机器人的发展开辟了一条崭新的道路。本人也是正在开发Android 与ROS机器人交互的APP,分享一些开发笔记。

Android 与Ros通信

Android 与Ros通信采用的是ROSBridge通信包,也是参照了开源项目:https://github.com/djilk/ROSBridgeClient

连接机器人

ROSBridge通信包底层其实是基于WebSocket协议来与Ros机器人通信,Android 设备需要和机器人处于同一局域网内。

     /**
     * 连接地址,根据Ros机器人实际ip和端口组成
     */
    private final String uriString = "ws://192.168.0.200:9090";
    private ROSBridgeClient client;
   /**
     * 连接
     * @param uri 地址
     * @param listener 回调事件
     * @param isDebug
     * @return
     */
    public boolean connect(String uri, final ROSClient.ConnectionStatusListener listener, boolean isDebug) {
        boolean isConnect;
        if (TextUtils.isEmpty(uri)) {
            uri = uriString;
        }
        client = new ROSBridgeClient(uri);
        isConnect = client.connect(listener);
        if (isConnect) {
            if (isDebug) {
                client.setDebug(true);
            }
        }
        return isConnect;
    }
public abstract class ROSClient {

    public ROSClient() {}
    
    public static ROSClient create(String uriString) {
        // if we ever implement other ROSClient types, we'll key off the URI protocol (e.g., ws://)
        // we'd also have to abstract out Topic and Service since they depend on the ROSBridge operations
        return new ROSBridgeClient(uriString);
    }
    
    public abstract boolean connect();
    public abstract boolean connect(ConnectionStatusListener listener);
    public abstract void disconnect();
    public abstract void send(Operation operation);
    public abstract void register(Class<? extends Operation> c,
            String s,
            Class<? extends Message> m,
            FullMessageHandler h);
    public abstract void unregister(Class<? extends Operation> c, String s);
    public abstract void setDebug(boolean debug);
    public abstract String[] getNodes() throws InterruptedException;
    public abstract String[] getTopics() throws InterruptedException;
    public abstract String[] getServices() throws InterruptedException;
    public abstract TypeDef getTopicMessageDetails(String topic) throws InterruptedException;
    public abstract TypeDef getServiceRequestDetails(String service) throws InterruptedException;
    public abstract TypeDef getServiceResponseDetails(String service) throws InterruptedException;
    public abstract TypeDef getTypeDetails(String type) throws InterruptedException;
    public abstract void typeMatch(TypeDef t, Class<? extends Message> c) throws InterruptedException; 
    public abstract Object getUnderlyingClient(); // for debugging
    
    public interface ConnectionStatusListener {
        public void onConnect();
        public void onDisconnect(boolean normal, String reason, int code);
        public void onError(Exception ex);
    }
public class ROSBridgeClient extends ROSClient {
    private String uriString;
    private ROSBridgeWebSocketClient client;
    
    public ROSBridgeClient(String uriString) {
        this.uriString = uriString;
    }
    
    @Override
    public boolean connect() {
        return connect(null);
    }
    
    @Override
    public boolean connect(ConnectionStatusListener listener) {
        boolean result = false;
        client = ROSBridgeWebSocketClient.create(uriString);
        if (client != null) {
            client.setListener(listener);
            try {
                result = client.connectBlocking();
            }
            catch (InterruptedException ex) {}
        }
        return result;
    }
    
    @Override
    public void disconnect() {
        try {
            client.closeBlocking();
        }catch (InterruptedException ex) {}
    }
    
    @Override
    public void send(Operation operation) {
        client.send(operation);
    }

    @Override
    public void setDebug(boolean debug) {
        client.setDebug(debug);
    }
}
public class ROSBridgeWebSocketClient extends WebSocketClient {

    private Registry<Class> classes;
    private Registry<FullMessageHandler> handlers;
    private boolean debug;
    private ROSClient.ConnectionStatusListener listener;
    public static final String DEFAULT_GZIP_CHARSET = "UTF-8";

    ROSBridgeWebSocketClient(URI serverURI) {
        super(serverURI);
        classes = new Registry<Class>();
        handlers = new Registry<FullMessageHandler>();
        Operation.initialize(classes);  // note, this ensures that the Message Map is initialized too
        listener = null;
    }

    public static ROSBridgeWebSocketClient create(String URIString) {
        ROSBridgeWebSocketClient client = null;
        try {
            URI uri = new URI(URIString);
            client = new ROSBridgeWebSocketClient(uri);
        }
        catch (URISyntaxException ex) {
            ex.printStackTrace();
        }
        return client;
    }

    public void setListener(ROSClient.ConnectionStatusListener listener) {
        this.listener = listener;
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        if (listener != null) {
            listener.onConnect();
        }
    }

    public static String unCompress(byte[] bytes, String charset) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream byteArrayOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        GZIPInputStream gzipInputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            byteArrayInputStream = new ByteArrayInputStream(bytes);
            gzipInputStream = new GZIPInputStream(byteArrayInputStream);
            byte[] buffer = new byte[256];
            int n;
            while ((n = gzipInputStream.read(buffer)) >= 0) {
                byteArrayOutputStream.write(buffer, 0, n);
            }
            //gzipInputStream.transferTo(byteArrayOutputStream);
            closeQuietly(byteArrayInputStream);
            closeQuietly(gzipInputStream);
            closeQuietly(byteArrayOutputStream);
            return byteArrayOutputStream.toString(charset);
        } catch (IOException e) {
            e.printStackTrace();
        }
        closeQuietly(byteArrayInputStream);
        closeQuietly(gzipInputStream);
        closeQuietly(byteArrayOutputStream);
        return null;
    }
    public static String unCompress(byte[] bytes) {
        return unCompress(bytes, DEFAULT_GZIP_CHARSET);
    }
    public static Boolean isCompressed(byte[] bytes) {
        return bytes.length > 2 && (((bytes[0] & 0xFF) == 0x78 &&
                ((bytes[1] & 0xFF) == 0x9C || (bytes[1] & 0xFF) == 0x01 ||
                        (bytes[1] & 0xFF) == 0xDA || (bytes[1] & 0xFF) == 0x5E)) ||
                ((bytes[0] & 0xFF) == 0x1F && (bytes[1] & 0xFF) == 0x8B));
    }
    public static void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception unused) {
            }
        }
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        byte[] raw_message_bytes = new byte[bytes.remaining()];;
        bytes.get(raw_message_bytes);
        String decompressed_data = null;
        if(isCompressed(raw_message_bytes)) {
            decompressed_data = unCompress(raw_message_bytes);
            if (decompressed_data == null) {
                return;
            }
        } else {
            decompressed_data = new String(raw_message_bytes);
        }
        onMessage(decompressed_data);
    }


    @Override
    public void onMessage(String message) {
        if (debug) {
            System.out.println("<ROS " + message);
        }
        //System.out.println("ROSBridgeWebSocketClient.onMessage (message): " + message);
        Operation operation = Operation.toOperation(message, classes);
        //System.out.println("ROSBridgeWebSocketClient.onMessage (operation): ");
        //operation.print();

        FullMessageHandler handler = null;
        Message msg = null;
        if (operation instanceof Publish) {
            Publish p = (Publish) operation;
            handler = handlers.lookup(Publish.class, p.topic);
            msg = p.msg;
        }
        else if (operation instanceof ServiceResponse) {
            ServiceResponse r = ((ServiceResponse) operation);
            handler = handlers.lookup(ServiceResponse.class, r.service);
            msg = r.values;
        }
        // later we will add clauses for Fragment, PNG, and Status. When rosbridge has it, we'll have one for service requests.

        // need to handle "result: null" possibility for ROSBridge service responses
        // this is probably some sort of call to the operation for "validation." Do it
        // as part of error handling.


        if (handler != null) {
            handler.onMessage(operation.id, msg);
        } else if (debug) {
            System.out.print("No handler: id# " + operation.id + ", ");
            if (operation instanceof Publish) {
                System.out.println("Publish " + ((Publish) operation).topic);
            } else if (operation instanceof ServiceResponse) {
                System.out.println("Service Response " + ((ServiceResponse) operation).service);
            }
            //operation.print();
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        if (listener != null) {
            boolean normal = (remote || (code == CloseFrame.NORMAL));
            listener.onDisconnect(normal, reason, code);
        }
    }

    @Override
    public void onError(Exception ex) {
        if (listener != null) {
            listener.onError(ex);
        } else {
            ex.printStackTrace();
        }
    }

    // There is a bug in V1.2 of java_websockets that seems to appear only in Android, specifically,
    //    it does not shut down the thread and starts using gobs of RAM (symptom is frequent garbage collection).
    //    This method goes into the WebSocketClient object and hard-closes the socket, which causes the thread
    //    to exit (note, just interrupting the thread does not work).
    @Override
    public void closeBlocking() throws InterruptedException {
        super.closeBlocking();
        try {
            Field channelField = this.getClass().getSuperclass().getDeclaredField("channel");
            channelField.setAccessible(true);
            SocketChannel channel = (SocketChannel) channelField.get(this);
            if (channel != null && channel.isOpen()) {
                Socket socket = channel.socket();
                if (socket != null) {
                    socket.close();
                }
            }
        }
        catch (Exception ex) {
            System.out.println("Exception in Websocket close hack.");
            ex.printStackTrace();
        }
    }


    public void send(Operation operation) {
        String json = operation.toJSON();
        if (debug) {
            System.out.println("ROS> " + json);
        }
        send(json);
    }

    public void register(Class<? extends Operation> c,
                         String s,
                         Class<? extends Message> m,
                         FullMessageHandler h) {
        Message.register(m, classes.get(Message.class));
        classes.register(c, s, m);
        if (h != null) {
            handlers.register(c, s, h);
        }
    }

    public void unregister(Class<? extends Operation> c, String s) {
        handlers.unregister(c, s);
        // Note that there is no concept of unregistering a class - it can get replaced is all
    }

    public Class<? extends Message> getRegisteredMessage(String messageString) {
        return classes.lookup(Message.class, messageString);
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }
}

下一篇是关于slam地图数据订阅及如何绘制slam地图

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值