怎樣寫一個相似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)

相關文章
相關標籤/搜索