之前博客《ROS学习笔记之——基于ROS的Android开发》和《ROS学习笔记之——ROSAndroid 与 rosjava》已经介绍过ROS-Android的一些学习资料,本博文再深入的学习一下
目录
rosjava
rosjava是ROS纯java的实现,它提供了一个客户端库,使Java程序员可以快速与ROS主题,服务和参数进行交互。甚至可以通过java实现roscore.
关于rosjava更多细节可以参考:http://rosjava.github.io/rosjava_core/latest/javadoc/index.html
由于ROS严重依赖于网络通信,因此rosjava是异步的。
一般而言,java包需要采用反向域名的形式,如下所示:
- org.ros.rosjava
- org.ros.rosjava_geometry
创建一个节点
通常,ROS节点与进程同步,然而,在rosjava中,节点更像nodelet,因为许多节点可以在单个进程Java VM中运行。
用户不会创建节点。 而是将程序定义为NodeMain的实现,由适当命名的NodeMainExecutor执行。如下所示
import org.ros.namespace.GraphName;
import org.ros.node.Node;
import org.ros.node.NodeMain;
public class MyNode implements NodeMain {
@Override
public GraphName getDefaultNodeName() {//返回节点的默认名字。该名字会一直被使用,除非在NodeConfiguration中被重新设置
return new GraphName("my_node");//当rosjava指向node,topic或者parameters的时候,会采用GraphName
}
@Override
public void onStart(ConnectedNode node) {//此处是输入代码的,就是开始节点了。参数ConnectedNode是用于构建发布者与订阅者
}
@Override
public void onShutdown(Node node) {//离开代码,应该用于关闭所有的发布者订阅者
}
@Override
public void onShutdownComplete(Node node) {//最后的退出程序的点
}
@Override
public void onError(Node node, Throwable throwable) {//报错的时候调用
}
}
NodeConfiguration
http://rosjava.github.io/rosjava_core/latest/javadoc/org/ros/node/NodeConfiguration.html#
Java NodeConfiguration.setNodeName方法代碼示例:https://vimsky.com/zh-tw/examples/detail/java-method-org.ros.node.NodeConfiguration.setNodeName.html
GraphName
http://rosjava.github.io/rosjava_core/latest/javadoc/org/ros/namespace/GraphName.html#
ConnectedNode
http://rosjava.github.io/rosjava_core/latest/javadoc/org/ros/node/ConnectedNode.html#
发布者与订阅者
关于ROS中,通过C++创建发布者与订阅者,可以参考博文《ROS复习笔记之——系统框架及其编程规范》
下面例子中,创建一个发布者来发布chatter topic。发布者将消息std_msgs.String发布到/chatter话题上。部分理解写代码的注释中了~
package org.ros.rosjava_tutorial_pubsub;
import org.ros.concurrent.CancellableLoop;
import org.ros.namespace.GraphName;
import org.ros.node.AbstractNodeMain;
import org.ros.node.ConnectedNode;
import org.ros.node.NodeMain;
import org.ros.node.topic.Publisher;
/**
* A simple {@link Publisher} {@link NodeMain}.
*
* @author damonkohler@google.com (Damon Kohler)
*/
public class Talker extends AbstractNodeMain {
@Override
public GraphName getDefaultNodeName() {//获取名字
return GraphName.of("rosjava_tutorial_pubsub/talker");
}
@Override
public void onStart(final ConnectedNode connectedNode) {
final Publisher<std_msgs.String> publisher =
connectedNode.newPublisher("chatter", std_msgs.String._TYPE);//定义发布的话题的名字以及其消息类型
// This CancellableLoop will be canceled automatically when the node shutsdown.
// 这个Talker节点的目的是每秒发布一次hello world消息,因此是通过循环的发布与休眠来实现的
//故此创建一个CancellableLoop来实现
connectedNode.executeCancellableLoop(new CancellableLoop() {
private int sequenceNumber;
@Override
protected void setup() {
sequenceNumber = 0;
}
@Override
protected void loop() throws InterruptedException {
std_msgs.String str = publisher.newMessage();//创建消息
str.setData("Hello world! " + sequenceNumber);//设置发布的消息的内容
publisher.publish(str);//发布者发布消息
sequenceNumber++;//记录第几次发布
Thread.sleep(1000);//休眠1000ms
}
});
}
}
接下来创建一个订阅者
package org.ros.rosjava_tutorial_pubsub;
import org.apache.commons.logging.Log;
import org.ros.message.MessageListener;
import org.ros.namespace.GraphName;
import org.ros.node.AbstractNodeMain;
import org.ros.node.ConnectedNode;
import org.ros.node.NodeMain;
import org.ros.node.topic.Subscriber;
/**
* A simple {@link Subscriber} {@link NodeMain}.
*
* @author damonkohler@google.com (Damon Kohler)
*/
public class Listener extends AbstractNodeMain {
@Override
public GraphName getDefaultNodeName() {
return GraphName.of("rosjava_tutorial_pubsub/listener");//定义发布者节点的名字
}
@Override
public void onStart(ConnectedNode connectedNode) {
final Log log = connectedNode.getLog();
Subscriber<std_msgs.String> subscriber = connectedNode.newSubscriber("chatter", std_msgs.String._TYPE);//创建一个订阅者,对应的要填入话题的名称以及消息的类型
//对于订阅者节点,可以加多个MessageListener
//当一个新的消息接收到后,所有的MessageListener将会被回调,以传入的消息作为参数调用 MessageListener.onNewMessage
subscriber.addMessageListener(new MessageListener<std_msgs.String>() {
@Override
public void onNewMessage(std_msgs.String message) {
log.info("I heard: \"" + message.getData() + "\"");
}
});
}
}
而至于服务与参数,可以参考:http://rosjava.github.io/rosjava_core/latest/getting_started.html
rosandroid
android_core提供了安卓的库给编写ROS app。故此从某种程度上而已,rosandroid就说一个让ros可以使用一些Android应用的库
RosActivity
RosActivity是所以基于ROS的安卓应用的基类。
对于继承RosActivity的activity,在其开始时,其super class将会
- 在前台开启NodeMainExecutorService服务
- 登录MasterChooser活动,提示用户配置主URI
- 显示正在进行的通知,通知用户ROS节点正在后台运行
如下所示
package org.ros.android.android_tutorial_pubsub;
import android.os.Bundle;
import org.ros.android.MessageCallable;
import org.ros.android.RosActivity;
import org.ros.android.view.RosTextView;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeMainExecutor;
import org.ros.rosjava_tutorial_pubsub.Talker;
public class MainActivity extends RosActivity {
private RosTextView<std_msgs.String> rosTextView;
private Talker talker;
public MainActivity() {
// The RosActivity constructor configures the notification title and ticker
// messages.
super("Pubsub Tutorial", "Pubsub Tutorial");//回调超级构造函数
}
@SuppressWarnings("unchecked")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);//获取layout
rosTextView = (RosTextView<std_msgs.String>) findViewById(R.id.text);//id
rosTextView.setTopicName("chatter");//话题名
rosTextView.setMessageType(std_msgs.String._TYPE);//消息类型
rosTextView.setMessageToStringCallable(new MessageCallable<String, std_msgs.String>() {
@Override
public String call(std_msgs.String message) {
return message.getData();
}
});
}
//父类定义了RosActivity.init的抽象方法,故此需要重写
@Override
protected void init(NodeMainExecutor nodeMainExecutor) {//这是启动所编写的节点及其他逻辑的地方
talker = new Talker();//前面定义过了talker
// 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(talker, nodeConfiguration);//执行talker节点
// The RosTextView is also a NodeMain that must be executed in order to
// start displaying incoming messages.
nodeMainExecutor.execute(rosTextView, nodeConfiguration);
}
}
需要添加的权限
在AndroidManifest.xml中添加
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
在<application>添加
<service android:name="org.ros.android.NodeMainExecutorService" >
<intent-filter>
<action android:name="org.ros.android.NodeMainExecutorService" />
</intent-filter>
</service>
参考资料
https://github.com/rosjava/rosjava_core