用android怎么做一个机器人,怎样写一个类似ROS的易用的android机器人框架(2)

怎样写一个类似ROS的易用的android机器人框架(2)

接下来,我们一步步来实现这个几个目标

ROS式节点通讯的Android实现

相关代码实现位于 ai.easy.robot.framework包内

首先,我们需要开启一个常驻运行的 MasterService,在机器人运行的这个生命周期内保持活动,每个节点(不管进程内还是进程外)都可以连接此Service,向其发送消息,MasterService负责将这些消息转发给已经订阅了该消息主题的节点。

我们选用 Messenger:

Messenger可跨进程传递数据

相比socket通讯方式,没有管理和建立socket的麻烦,没有必要单独再定义普通数据到socket包的序列化。

底层实现了AIDL,不需要在每个节点定义数据接口,所有数据统一为 Message

Messenger发送的消息数据是放在一个消息循环中统一分发处理的,不存在多线程的数据不同步问题

实现过程

MasterService 类在 onCreate() 之后开启一个线程运行消息循环

thread(start=true,name = "master"){

try {

Looper.prepare()

//TODO

h = MyHandler(this, Looper.myLooper())

//

messenger = Messenger(h)

mtx.release()

//

logd("start loop")

Looper.loop()

logd("stop loop")

}catch (e:Exception){

e.printStackTrace()

}

}

这里,MyHandler负责所有消息的接收,解析,转发,messenger则通过 onBind()暴露给连接的外界节点

override fun onBind(intent: Intent?): IBinder = synchronized(this){

if(!this::messenger.isInitialized){

//mtx.wait()

mtx.acquire()

}

messenger.binder

}

MasterConnector 是负责普通节点和 MasterService 连接的中介,其connect()函数通过bindService方式获取 MasterService 的 messenger,从而可以想MasterService发送 Message

private val conn =

object : ServiceConnection {

override fun onServiceDisconnected(name: ComponentName?) {

logd("service disconnected")

isConnected = false

messenger = null

}

override fun onServiceConnected(name: ComponentName?, service: IBinder?) {

logd("service connected")

messenger = Messenger(service)

isConnected = true

onConnected?.invoke()

}

}

fun connect(servClass:Class) {

if (!isConnected && !isBounded) {

val i = Intent()

i.setClass(ctx, servClass)

ctx.bindService(i, conn, Context.BIND_AUTO_CREATE)

isBounded = true

}

}

MasterService的消息的处理过程

每个Message的 what 属性说明消息的作用,obj属性指明节点名称或者消息主题,data属性封装消息数据

what的取值定义为

const val NODE_MGR_REG = 1//节点注册指令消息

const val NODE_MGR_UN_REG = 2//节点注销指令消息

const val NODE_MGR_LIST = 3//查询节点列表指令消息

const val NODE_MGR_CLR = 4//清除所有节点指令

const val NODE_MSG_PUB = 5//节点发布消息

const val NODE_MSG_SUB = 6//节点订阅消息

const val NODE_MSG_UN_SUB = 7 //取消订阅

const val NODE_MSG_ON_REG = 8//注册成功事件

const val NODE_MSG_ON_NODE_CHANGED = 9 //节点列表发生变化事件

const val NODE_MSG = 16 //MasterService转发的消息

节点注册指令消息

表示有外部节点要加入整个节点消息通讯体系,该消息中有个 replyTo属性,传入的是该节点接收 MasterService发送数据的Messenger对象,MaserService就是通过他再给目标节点发送消息的。这个Messenger对象需要放入nodeMap中保存。

节点注销指令消息

删除nodeMap中存放的Messenger对象,释放其内存。

节点订阅消息

表示节点想接收某个主题的消息。将节点名称和订阅主题存入nodeSubMap

节点发布消息

拥有消息主题和消息数据,通过nodeSubMap查询订阅了该消息的节点的Messenger对象,再通过该Messenger对象发送该消息。实现消息转发。

查询节点列表指令消息

查询已经注册的节点列表

清除所有节点指令

强迫所有节点注销

普通节点的消息处理过程

BaseNode是所有普通节点的基类,实现消息的收发

注册节点

fun register() {

val m = Message.obtain()

m.what = MasterService.NODE_MGR_REG

m.obj = name

m.replyTo = receiver

connector.messenger?.send(m)

}

注册需要将自身的Messenger对象 receiver传递给 MasterService,其中

private val receiver = Messenger(

object : Handler() {

override fun handleMessage(msg: Message?) {

logd("msg>>")

when (msg?.what) {

MasterService.NODE_MSG -> onMessageReceive("${msg.obj}", msg?.data)

MasterService.NODE_MSG_ON_REG -> onNodeRegisted()

else -> {

}

}

}

})

receiver接收到 MasterService 的消息后调用响应的回调函数

注销节点

fun unregister()

订阅消息

fun subscribe(topic: String)

发布消息

fun publish(topic: String, msg: Bundle)

所有节点的数据消息封装在Bundle对象中。

至此,android版的类似ROS通讯框架就实现了

多进程机器人系统的通讯

系统中只有一个进程运行MasterService 跟MasterService不在同一个进程的节点的消息收发不需要改变,只是其connect方法做些改动

fun connect(masterPackageName:String,masterServiceName:String) {

if (!isConnected && !isBounded) {

val i = Intent()

i.setComponent(ComponentName(masterPackageName,masterServiceName))

ctx.bindService(i, conn, Context.BIND_AUTO_CREATE)

isBounded = true

}

}

多主板机器人系统的通讯

多主板分布的节点需要通过网络进行通讯了。考虑到这种应用场景不多。所以MasterService暂时还不支持这样的操作,因此还需要实现一个适配器,以便能够注册和管理网络上的节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值