Android و Rx و Kotlin ، أو كيفية جعل مخلب Lego يتقلص. الجزء 1

مرحباً ، عشاق هبر! صدفة محظوظة ، في أغسطس 2018 ، كنت محظوظًا للبدء في العمل مع رفيقي ( kirillskiy ) في مشروع كان مذهلاً في مصلحته. وهكذا ، خلال اليوم كنا المبرمجين العاديين ، وفي الليل كنا المبرمجين مرة أخرى الذين يناضلون مع قضايا الاعتراف الحركات للأشخاص ذوي وظائف محدودة من أطرافهم ، يمكن للأشخاص الأصحاء بطبيعة الحال استخدام هذا ، وذلك باستخدام تقنية مماثلة في مجموعة متنوعة من الطرق.

في هذا المقال ، يتحدث Cyril بشكل عام عن المشروع ، لكنني سأقول بمزيد من التفصيل وأتطرق إلى موضوع android فيه.
أولاً ، سوف أخبركم عن المشروع بأكمله ، وما توصلنا إليه وكيف أردنا تنفيذ هذا:

1) تم اختيار EMG (Electromyography - تسجيل النشاط الكهربائي للعضلات) كوسيلة للحصول على البيانات (أوه ، نعم ، سيكون هناك الكثير من البيانات). لأول مرة تم تطبيق هذه الطريقة في عام 1907 ، لذلك كنا نسير على طول المسار المطروق.

2) لقد وجدنا مستشعر EMG ذي 8 قنوات يعمل على البلوتوث (حتى أنه يمتلك واجهة برمجة تطبيقات خاصة به ، والتي تحولت في النهاية إلى أنها غير مجدية ، لأنه كان علي الاتصال بشكل مستقل كجهاز BT. شكرًا على الأقل كتبنا مواصفة)

3) قررنا أن كل شيء سوف يعمل مثل هذا:

  • وضع التدريب. نلبس المستشعر على الساعد ، ونختار نوع الحركة التي سنقوم بتدريبها. على سبيل المثال ... "ثني الفرشاة." وبدء التدريب (كرر الحركة 12 مرة). سنقوم بحفظ البيانات الواردة في هذه اللحظة ثم إرسالها إلى الخادم ، حيث سنقوم بتدريب الشبكة العصبية (بهدوء ، سأخبرك بهذا أيضًا)
  • وضع التعرف على الحركة المباشرة. تتم مقارنة البيانات التي تم التقاطها أثناء الحركة بالنموذج الذي تم الحصول عليه نتيجة لتدريب الشبكة العصبية. بناءً على النتائج ، سنحصل بالفعل على "BENDING BRUSH" ، على سبيل المثال.
  • وضع القيادة. وفقًا لنوع معين من الحركة ، يجب عمل شيء ما للتحرك. على سبيل المثال ، تم تجميع مناور في المطبخ من مصمم (PPC ، كم هو مكلف) للشركة المصنعة الدنماركية الشهيرة.

4) البند الروبوت. أنا مطور Android - وكانت خطيئة عدم استخدامه. يعمل Android هنا معنا:

  • يجد جميع الأجهزة BT المتاحة
  • يربط إلى الاستشعار
  • يرسم رسمًا بيانيًا استنادًا إلى البيانات المأخوذة من أجهزة الاستشعار (8 قنوات ، التردد 200 هرتز) 8 ، منحنيات جميلة ملونة.
  • تنفذ وضع التدريب (اختيار نوع الحركة المدربة ، زر بدء التدريب ، زر إرسال البيانات)
  • ينفذ التفاعل بين العميل والخادم. من الضروري إرسال البيانات إلى الخادم حتى يتم تدريب الشبكة العصبية
  • يقوم بتنفيذ الاتصال والتفاعل مع Raspberry PI 3B ، والذي يتم لحام المحركات به ، مما يحرك المعالج في الحركة.

5) التوت PI 3B. لقد وضعنا تطبيق Android Things على التوت ، ثم قمنا برفع خادم BT عليه ، والذي يستقبل رسائل من جهاز Android وينقل المحركات ذات الصلة ، التي بدأت في تشغيل مخلب فائق من LEGO.

6) الخادم. يتم نشره بواسطة Docker محليًا على جهاز كمبيوتر. يتلقى البيانات المرسلة من جهازك ، ويعلم شبكة العصبية ، وإرجاع نموذج.

الجزء رقم 1. أندرويد. هذه المرة سننظر في غطاء المشروع فيما يتعلق بنظام أندرويد حتى يتم إرسال البيانات إلى الخادم.

يطلق عليه NUKLEOS (https://github.com/cyber-punk-me/nukleos)
المكدس:

- كوتلين
- MVP
- خنجر 2
- التحديثية 2
- RxKotlin ، RxAndroid

للتوت:

أشياء -Android

في العمل ، لم يسمحوا لي باللعب مع الهندسة المعمارية ، لكن أخيرًا أتيحت لي الفرصة للعب مع لعبة قديمة تسمى MVP.

يتكون التطبيق من نمط التنقل أسفل النشاط و 4 أجزاء:
أول واحد هو "قائمة بجميع الأجهزة BT المتاحة"

لقد اخترنا مستشعر BT ذي 8 قنوات ، والذي كان له واجهة برمجة التطبيقات الخاصة به للعمل مع BT. لسوء الحظ ، تبين أن واجهة برمجة التطبيقات عديمة الفائدة تمامًا ، لأنها اقترحت على الفور تحديد أحد أنواع الحركة الستة (المشابهة) ، لكن دقة التعرف كانت 80٪ - وهذا لم يكن جيدًا. حسنًا ، نحن بحاجة إلى بيانات فعلية. قيمة التغيرات في الإمكانات الحيوية الحيوية التي تحدث في العضلات البشرية عند إثارة الألياف العضلية. ولهذا كان من الضروري العمل مع هذا المستشعر مباشرة. ترك المبدعون وصفًا للبروتوكول للتعامل معه ، لذلك كان عليهم التنقيب لفترة ليست طويلة. يمكنني وصف مثال للعمل مع أجهزة BT العارية في مقالة منفصلة ، إذا كان ذلك مثيرًا للاهتمام ، ولكن باختصار يبدو الأمر كما يلي:

class BluetoothConnector(val context: Context) { private val mBTLowEnergyScanner by lazy { (context.getSystemService(Activity.BLUETOOTH_SERVICE) as BluetoothManager) .adapter.bluetoothLeScanner } private var mBluetoothScanCallback: BluetoothScanCallback? = null // scan. fun startBluetoothScan(serviceUUID: UUID?) = Flowable.create<BluetoothDevice>({ mBluetoothScanCallback = BluetoothScanCallback(it) if (serviceUUID == null) { mBTLowEnergyScanner.startScan(mBluetoothScanCallback) } else { mBTLowEnergyScanner.startScan( arrayListOf(ScanFilter.Builder().setServiceUuid(ParcelUuid(serviceUUID)).build()), ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(), mBluetoothScanCallback) } }, BackpressureStrategy.BUFFER).apply { doOnCancel { mBTLowEnergyScanner.stopScan(mBluetoothScanCallback) } } // scan with timeout fun startBluetoothScan(interval: Long, timeUnit: TimeUnit, serviceUUID: UUID? = null) = startBluetoothScan(serviceUUID).takeUntil(Flowable.timer(interval, timeUnit)) inner class BluetoothScanCallback(private val emitter: FlowableEmitter<BluetoothDevice>) : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult?) { super.onScanResult(callbackType, result) result?.let { it.device.apply { emitter.onNext(this) } } } override fun onScanFailed(errorCode: Int) { super.onScanFailed(errorCode) emitter.onError(RuntimeException()) } } } 

لف بعناية خدمة BT القياسية في RX واحصل على ألم أقل.

بعد ذلك ، قم بتشغيل المسح الضوئي ، وبفضل rx على الاشتراك ، نقوم بإنشاء قائمة بجميع الأجهزة عن طريق حشوها في RecyclerView:

 mFindSubscription = mFindFlowable ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe({ if (it !in mBluetoothStuffManager.foundBTDevicesList) { addSensorToList(SensorStuff(it.name, it.address)) mBluetoothStuffManager.foundBTDevicesList.add(it) } }, { hideFindLoader() showFindError() if (mBluetoothStuffManager.foundBTDevicesList.isEmpty()) { showEmptyListText() } }, { hideFindLoader() showFindSuccess() if (mBluetoothStuffManager.foundBTDevicesList.isEmpty()) { showEmptyListText() } }) 

تحديد أحد الأجهزة ، حدده ، وانتقل إلى الشاشة التالية:
"إعدادات الاستشعار"

نتصل به ونبدأ في تدفق بيانات المستشعر باستخدام الأوامر التي أعددناها مسبقًا. لحسن الحظ ، يتم وصف بروتوكول للعمل مع هذا الجهاز من قبل المبدعين من أجهزة الاستشعار:

 object CommandList { //Stop the streaming fun stopStreaming(): Command { val command_data = 0x01.toByte() val payload_data = 3.toByte() val emg_mode = 0x00.toByte() val imu_mode = 0x00.toByte() val class_mode = 0x00.toByte() return byteArrayOf(command_data, payload_data, emg_mode, imu_mode, class_mode) } // Start streaming (with filter) fun emgFilteredOnly(): Command { val command_data = 0x01.toByte() val payload_data = 3.toByte() val emg_mode = 0x02.toByte() val imu_mode = 0x00.toByte() val class_mode = 0x00.toByte() return byteArrayOf(command_data, payload_data, emg_mode, imu_mode, class_mode) } ..... 

العمل مع الجهاز يتم لفه بعناية في آر إكس للعمل دون ألم.

إرجاع المستشعرات Byte Arrays بشكل طبيعي ، وكان من الضروري قطع المحول ، وكان تردد المستشعرات 200 هرتز ... إذا كان الأمر ممتعًا ، يمكنني وصفه بالتفصيل (جيدًا ، أو إلقاء نظرة على الكود) ، ولكن في النهاية نحن نعمل مع كمية كبيرة من البيانات بهذه الطريقة:

1 - نحتاج إلى رسم منحنيات كل من المستشعرات. بالطبع ، لا يوجد أي معنى في تقديم جميع البيانات على الإطلاق ، لأنه ليس من المنطقي بالنسبة للعين أن تفحص 200 تغيير في الثانية على كل جهاز استشعار على الجهاز المحمول. لذلك ، لن نأخذ كل شيء.

2 - نحن بحاجة إلى العمل مع كامل كمية البيانات ، إذا كانت عملية التعلم أو الاعتراف.

لتلبية هذه الاحتياجات ، فإن RX مناسب بشكل مثالي لجميع المرشحات.

الرسوم البيانية كان لا بد من القيام بها. من يهتم - انظر إلى PowerfullChartsView في مجلد طرق العرض.

والآن بعض مقاطع الفيديو:


في الفيديو ، سترى كيف يعمل Cyril مع النظام ككل. الفيديو يعمل مع النموذج. لكن النموذج موجود على الخادم. في المستقبل ، سيكون بالطبع على الجهاز ، مما سيسرع بشكل كبير من الاستجابة)

اكتب الجوانب المثيرة للاهتمام ، تلك الجوانب التي يجب سردها بمزيد من التفصيل. بطبيعة الحال ، نحن نعمل على مشروع ومنفتحون على اقتراحاتكم.

مشروع جيثب كله هنا

Source: https://habr.com/ru/post/ar438034/


All Articles