安卓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(); }}
- 安卓UI线程向ROS节点发送消息的方法
- 安卓:handler向子线程发送消息
- 向线程发送消息
- 向线程发送消息
- 向线程发送消息
- Android:子线程向UI主线程发送消息
- 不同线程之间向对方的窗口发送消息
- 使用PostThreadMessage向发送线程消息失败的解决方法
- 安卓UI线程与异步消息处理机制
- ROS节点、消息、服务、主题的关系
- ROS节点,消息,话题,服务的介绍
- 使用PostThreadMessage向工作线程发送消息
- 使用PostThreadMessage向工作线程发送消息
- 线程中向主对话框发送消息
- 使用PostThreadMessage向工作线程发送消息
- 使用PostThreadMessage向工作线程发送消息
- 使用PostThreadMessage向工作线程发送消息
- 使用PostThreadMessage向工作线程发送消息
- 程序员的六个准则
- 在 iOS 上开始创建你的虚拟现实应用
- C#委托与事件的本质区别
- 最近在处理Cocoa NSString时, 遇到一些字符编码的问题
- ActionInvocation的理解
- 安卓UI线程向ROS节点发送消息的方法
- jQuery 事件 - ready() 方法
- JS实现图片推拉门效果
- 手动编写一个Servlet程序
- 已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭
- Android MediaController Api译文
- GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流
- STL stack/queue/sort/vector/set/map 的使用方法
- iOS开发之JSPatch的基础用法