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

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

怎样写一个相似ROS的易用的android机器人框架(1)node

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

参考项目源码git

ROS式节点通信的Android实现

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

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

咱们选用 Messenger框架

  1. Messenger可跨进程传递数据
  2. 相比socket通信方式,没有管理和创建socket的麻烦,没有必要单独再定义普通数据到socket包的序列化。
  3. 底层实现了AIDL,不须要在每一个节点定义数据接口,全部数据统一为 Message
  4. Messenger发送的消息数据是放在一个消息循环中统一分发处理的,不存在多线程的数据不一样步问题

实现过程

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

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()暴露给链接的外界节点ide

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属性封装消息数据oop

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暂时还不支持这样的操做,所以还须要实现一个适配器,以便可以注册和管理网络上的节点。

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

相关文章
相关标签/搜索