安卓UI线程向ROS节点发送消息的方法

来源:互联网 发布:yii2 数据库查询缓存 编辑:程序博客网 时间:2024/06/08 07:47

想要在安卓上运行ROS节点,需要rosjava这个client library,以及构建在rosjava之上的RosActivity类

安卓上的每个ros节点,跟ubuntu上的常规节点不一样,不是进程而是线程,亦称作nodelet,所以在一个安卓app里可以运行多个ros node,构成一个node网络

node通过NodeMain接口定义,NodeMain定义了2个函数:setup和loop

前者让线程有机会做初始化操作(等价于常规节点的subscriber/publisher/server/client实例化),后者是线程的消息循环(等价于常规节点的spinOnce方法)处理

问题来了,如果UI线程要向ros节点发送消息,该怎么发呢?

我最初想到的是用安卓的MessageQueue来实现,但很快发现问题所在,Looper.loop方法 跟 NodeMain的loop方法 不能并存

        因为Looper.loop()一旦运行,则线程进入Looper消息循环,永远不会执行NodeMain的loop方法

后来想出了个方法,UI线程(通过调用相应方法)修改ros节点的成员变量,ros节点在消息循环里里读取该成员变量,完成消息传递,这有2个问题:①消息类型固定②如果还有其他线程发消息,则需要考虑同步(线程安全)

为了解决问题②,一番寻找,发现BlockingQueue可以达成效果,且该类一直是线程间通讯的标准方法,自己用MessageQueue反而显得孤陋寡闻了敲打


那怎么解决问题①呢?考虑到NodeMain的loop方法不像Looper.loop()那样是个不返回的死循环,所以,理论上应该是可以将ros的消息循环揉到android的消息循环里的

       比如,定义一个Tick Message,在Looper.prepare后,Looper.loop前,发送该Tick(boot自举),然后在Handler里调用NodeMain的loop,然后再向自身发送Tick,使得ros消息循环也运转起来,这样问题①和问题②都解决了,还实现了在一个线程里运行两个消息循环微笑

上代码,首先是这个运行在android上的ros节点

package org.ros.android.android_tutorial_pubsub;import android.content.Context;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.text.format.Time;import android.util.Log;import org.ros.concurrent.CancellableLoop;import org.ros.message.MessageListener;import org.ros.namespace.GraphName;import org.ros.node.AbstractNodeMain;import org.ros.node.ConnectedNode;import org.ros.node.Node;import org.ros.node.service.ServiceResponseBuilder;import org.ros.node.service.ServiceServer;import org.ros.node.topic.Subscriber;import java.util.logging.MemoryHandler;import rosjava_test_msgs.AddTwoIntsRequest;import rosjava_test_msgs.AddTwoIntsResponse;/** * Created by haipeng on 16-3-17. */public class SpeechSynNode extends AbstractNodeMain {    private String topicName;    private Context mUiCtx;    private Handler mUiHandler;    private Handler mHandler;    private static String TAG = "SpeechSynNode";    public SpeechSynNode(Context ctx, Handler handler, String topic)    {        mUiCtx = ctx;        topicName = topic;        mUiHandler = handler;    }    @Override    public GraphName getDefaultNodeName() {        return GraphName.of("speech_synthesizer");    }    @Override    public void onStart(final ConnectedNode connectedNode) {        //create subscriber        final Subscriber<std_msgs.String> subscriber =                connectedNode.newSubscriber(topicName, std_msgs.String._TYPE);        subscriber.addMessageListener(new MessageListener<std_msgs.String>() {            @Override            public void onNewMessage(std_msgs.String message) {                String text = message.getData();                Log.i(TAG, "I heard msg from ubuntu : \"" + text + "\"");                Message msg = mHandler.obtainMessage(CommonMsg.PLAY_MSG, text);                mHandler.sendMessage(msg);            }        });        connectedNode.executeCancellableLoop(new CancellableLoop() {            @Override            protected void setup() {                Looper.prepare();//allocate MQ, required by ifly                mHandler = new Handler(){                    public void handleMessage(Message msg) {                        super.handleMessage(msg);                        switch (msg.what) {                            case CommonMsg.TICK_NODE_SYN: {                                try {                                    //process ros msg                                    loop();                                    //enter next tick                                    Message tick = mHandler.obtainMessage(CommonMsg.TICK_NODE_SYN);                                    mHandler.sendMessage(tick);                                }catch(InterruptedException e){                                    Log.e(TAG, "node thread interrupted " + e.getMessage());                                }                                break;                            }                            case CommonMsg.PLAY_MSG:{                                String text = (String)msg.obj;                                playMsg(text);                                break;                            }                            default: {                                break;                            }                        }                    }                };                //trigger the ros msg loop                Message msg = mHandler.obtainMessage(CommonMsg.TICK_NODE_SYN);                mHandler.sendMessage(msg);                //tell UI thread node's handler                msg = mUiHandler.obtainMessage(CommonMsg.HANDLER_SYN, mHandler);                mUiHandler.sendMessage(msg);                //enter Looper loop, and never comeback                Looper.loop();            }            @Override            protected void loop() throws InterruptedException {                long time = System.currentTimeMillis();                if (time % 1000 == 0){                    Log.i(TAG, "ros_node run again after 1s");                }            }        });    }    private void playMsg(String text){        if(!bSpeaking) {            int code = mTts.startSpeaking(text, mTtsListener);            if (code != ErrorCode.SUCCESS) {                Log.e(TAG, "语音合成失败,错误码: " + code);            }        }else{            Log.w(TAG, "语音合成繁忙,丢弃!");        }    }    @Override    public void onShutdown(Node arg0) {        try {            // 释放连接            mTts.stopSpeaking();            mTts.destroy();            android.util.Log.i(TAG, "Synthesizer destroyed");        }catch (Throwable e) {            android.util.Log.e(TAG, "error when destroying synthesizer");        }    }}

再看UI线程怎么给node发消息

package org.ros.android.android_tutorial_pubsub;import android.content.pm.ActivityInfo;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import org.ros.android.RosActivity;import org.ros.node.NodeConfiguration;import org.ros.node.NodeMainExecutor;import com.hankcs.hanlp.HanLP;import java.util.List;import java.util.Map;/** * @author damonkohler@google.com (Damon Kohler) */public class MainActivity extends RosActivity {    private Button btnPlayMsg;    private static final String TAG = "MainRosActivity";    private Handler mSynHandler = null;    private SpeechSynNode speechSynthesizer;    private final Handler mHandler = new Handler(){        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case CommonMsg.HANDLER_SYN:{                    mSynHandler = (Handler)msg.obj;                    break;                }                default: {                    assert false;                    break;                }            }        };    };    public MainActivity() {        // The RosActivity constructor configures the notification title and ticker        // messages.        super("Robot", "Robot");    }    @SuppressWarnings("unchecked")    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        btnPlayMsg = (Button) findViewById(R.id.play_msg);        btnPlayMsg.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                if (null != mSynHandler){                    Message msg = mSynHandler.obtainMessage(CommonMsg.PLAY_MSG, "今天是个好日子");                    mSynHandler.sendMessage(msg);                }            }        });    }    @Override    protected void init(NodeMainExecutor nodeMainExecutor) {        speechSynthesizer = new SpeechSynNode(this, mHandler, "speech_syn");        // At this point, the user has already been prompted to either enter the URI        // of a master to use or to start a master locally.        // The user can easily use the selected ROS Hostname in the master chooser        // activity.        NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(getRosHostname());        nodeConfiguration.setMasterUri(getMasterUri());        nodeMainExecutor.execute(speechSynthesizer, nodeConfiguration);    }    @Override    protected void onDestroy(){        super.onDestroy();        Log.i(TAG, "onDestroy");        nodeMainExecutorService.shutdownNodeMain(speechSynthesizer);        nodeMainExecutorService.forceShutdown();    }}


0 0
原创粉丝点击