Kontrol Drone Menggunakan Suara dengan Intel RealSense SDK

Kontrol Drone Menggunakan Suara dengan Intel RealSense SDK

[source code artikel ini bisa diunduh di tautan ini]

Kini kita sering mendengar berita tentang drone, mulai dari penggunaan drone untuk mata-mata dan operasi penerbangan, fotografi dan video, hingga hanya untuk senang-senang saja. Teknologi drone saat ini menarik untuk kita lihat lebih dalam.

Sebagai developer, kita memiliki kemampuan untuk membuat aplikasi yang dapat mengontrol drone. Sebuah drone sebetulnya adalah perangkat yang dapat diprogram. Jadi membuat koneksi ke drone dan mengirim perintah untuk melakukan sesuatu yang diinginkan dapat kita lakukan melalui aplikasi PC atau ponsel biasa.

Di artikel ini, saya¬†memilih untuk menggunakan salah satu drone yang paling mudah untuk di-“hack” dan tersedia di pasar yaitu Parrot AR.Drone 2.0.

Kita akan melihat bagaimana caranya untuk berinteraksi dengan drone tersebut dan mengontrolnya menggunakan sebuah library yang ditulis dalam bahasa C#. Dengan menggunakan hal tersebut sebagai dasar, kita akan menambahkan perintah untuk mengontrol drone menggunakan Intel RealSense SDK.

Parrot AR.Drone 2.0

Di antaranya banyaknya drone yang dipasarkan untuk para penggemar drone, salah satu yang paling menarik adalah AR.Drone 2.0 dari Parrot. Drone ini dilengkapi dengan banyak fitur dan di dalamnya terdapat sistem bantuan yang menyediakan antarmuka stabilisasi dan kalibrasi.

Proteksi Sytrofoam yang tangguh membuat drone ini juga dapat menghindari kerusakan pada bagian baling-baling atau bagian beregerak lainnya jika terjadi tabrakan atau drone-nya jatuh.

AR DroneAR.Drone 2.0 dari Parrot

Perangkat keras di dalam AR.Drone 2.0 menyediakan koneksi dengan perangkat eksternal seperti ponsel, tablet, atau PC melalui jaringan WiFi mereka sendiri. Protokol komunikasinya berbasiskan pesan bertipe seperti AT (protokolnya mirip dengan yang pernah digunakan untuk memprogram dan mengontrol modem telepon jaman dulu).

Menggunakan protokol sederhana ini, sangatlah memungkinkan untuk mengirim berbagai macam perintah ke drone seperti perintah untuk terbang, meninggikan atau menurunkan ketinggian, hingga terbang ke berbagai arah.

Kita juga dapat melihat aliran gambar yang diambil dari kamera yang terpasang pada drone ini (satu kamera depan, dan satu kamera menghadap bawah) untuk kemudian kita simpan atau membuat video ketika drone-nya terbang.

Parrot menyediakan beberapa aplikasi untuk mengendarai AR.Drone 2.0 secara manual; namun, akan lebih menarik untuk mempelajari bagaimana caranya untuk mengontrol penerbangan secara otomatis. Oleh karena itu saya memutuskan untuk membuat antarmuka yang membuat kita dapat mengontrol drone tersebut dari perangkat yang berbeda-beda.

Mengontrol Drone dengan Program

Sebelumnya disebutkan bahwa AR.Drone 2.0 memiliki jaringan Wi-Fi tersendiri, oleh karena itu kita akan membuat koneksi ke drone tersebut untuk kemudian dikirim perintah kontrol. Panduan developer AR.Drone 2.0 memberikan kita semua informasi yang diperlukan.

Contohnya, panduannya mengatakan bahwa mengirim perintah dapat dilakukan melalui UDP ke alamat 192.168.1.1 pada port 5556. Berikut adalah contoh teks dalam format AT:

  • AT * REF untuk kontrol lepas landas dan pendaratan
  • AT * PCMD¬†untuk menggerakkan drone¬†(arah, kecepatan, ketinggian)

Setelah kita membuat koneksi ke drone, kita akan membuat semacam “game” di mana kita mengirim perintah ke drone berbasiskan masukan dari aplikasi kita. Mari kita lihat bagaimana caranya untuk membuat class library.

Pertama, kita harus membuat koneksi ke perangkat:

public static async Task ConnectAsync(string hostName = HOST_NAME, string port = REMOTE_PORT)
        {
             // Set up the UDP connection.
             var droneIP = new HostName(hostName);

             udpSocket = new DatagramSocket();
             await udpSocket.BindServiceNameAsync(port);
             await udpSocket.ConnectAsync(droneIP, port);
             udpWriter = new DataWriter(udpSocket.OutputStream);

             udpWriter.WriteByte(1);
             await udpWriter.StoreAsync();

             var loop = Task.Run(() => DroneLoop());
        }

Seperti yang sudah disebutkan, kita perlu menggunakan protokol UDP, jadi kita perlu objek DatagramSocket. Setelah membuat koneksi dengan metode ConnectAssync, kita buat sebuah DataWriter pada output stream untuk mengirimkan perintah ke mereka sendiri. Terakhir, kita kirimkan byte pertama melalui Wi-Fi. Byte tersebut akan dihilangkan oleh drone karena itu dimaksudkan hanya untuk menginisiasi sistem.

Cek perintah yang dikirim ke drone berikut ini:

private static async Task DroneLoop()
{
    while (true)
    {

        var commandToSend = DroneState.GetNextCommand(sequenceNumber);
        await SendCommandAsync(commandToSend);

        sequenceNumber++;
        await Task.Delay(30);
    }
}

Tag DroneState.GetNextCommand memformat teks perintah AT yang harus dikirimkan ke perangkat. Untuk melakukan hal ini, kita memerlukan sebuah rangkaian angka karena drone hanya akan meproses perintah yang disertai dengan angka progresif dan menghiraukan perintah yang menggunakan angka sama atau lebih kecil dari yang sudah diguankan sebelumnya.

Lalu kita menggunakan WriteString untuk mengirim perintah melalui StreamSocket ke dalam stream, memaksa StoreAsync untuk menulis buffer dan menyerahkannya. Terakhir, kita menaikkan rangkaian angka dan menggunakan Task Delay untuk membuat penundaan untuk interasi berikutnya.

Class DroneState adalah class yang mengurus perintah mana yang akan dikirimkan:

public static class DroneState
{
   public static double StrafeX { get; set; }
   public static double StrafeY { get; set; }
   public static double AscendY { get; set; }
   public static double RollX { get; set; }
   public static bool Flying { get; set; }
   public static bool isFlying { get; set; }

    internal static string GetNextCommand(uint sequenceNumber)
    {
        // Determine if the drone needs to take off or land
        if (Flying && !isFlying)
        {
            isFlying = true;
            return DroneMovement.GetDroneTakeoff(sequenceNumber);
        }
        else if (!Flying && isFlying)
        {
            isFlying = false;
            return DroneMovement.GetDroneLand(sequenceNumber);
        }

        // If the drone is flying, sends movement commands to it.
        if (isFlying && (StrafeX != 0 || StrafeY != 0 || AscendY != 0 || RollX != 0))
            return DroneMovement.GetDroneMove(sequenceNumber, StrafeX, StrafeY, AscendY, RollX);

        return DroneMovement.GetHoveringCommand(sequenceNumber);
    }
}

Property StrafeX, StrafeY, AscendY, dan RollX secara berurutan menentukan kecepatan navigasi kiri/kanan, depan/belakang, ketinggian, dan perubahan rotasi dari drone. Property tersebut formatnya double dan menerima angka antara 1 dan -1. Sebagai contoh, mengeset StrafeX ke -0.5 menggerakkan drone ke kiri dengan setengah kecepatan dari maksimal; mengeset 1 akan menggerakkan drone ke kanan dengan kecepatan penuh.

Flying adalah variabel yang menentukan lepas landas atau pendaratan. Pada method GetNextCommand kita mengecek nilai dari field tersebut untuk menentukan perintah mana yang akan dikirimkan ke drone. Perintah ini lalu akan dikelola lebih lanjut oleh class DroneMovement.

Perlu dicatat bahwa jika tidak ada perintah yang ditentukan, baris kodingan terakhir akan membuat perintah Hovering, sebuah perintah kosong yang menjaga kanal komunikasi agar tetap terbuka antara drone dan perangkat. Drone harus terus menerima pesan dari aplikasi yang mengontrol, bahkan jika tidak ada aksi yang dilakukan dan tidak ada status yang berubah.

Method yang paling menarik dari class DroneMovement adalah GetDroneMove, yang mana tugasnya adalah membuat dan mengirim perintah ke drone. Untuk method lainnya terkait pergerakan, lihat contoh berikut.

public static string GetDroneMove(uint sequenceNumber, double velocityX, double velocityY, double velocityAscend, double velocityRoll)
    {
        var valueX = FloatConversion(velocityX);
        var valueY = FloatConversion(velocityY);
        var valueAscend = FloatConversion(velocityAscend);
        var valueRoll = FloatConversion(velocityRoll);

        var command = string.Format("{0},{1},{2},{3}", valueX, valueY, valueAscend, valueRoll);
        return CreateATPCMDCommand(sequenceNumber, command);
    }
private static string CreateATPCMDCommand(uint sequenceNumber, string command, int mode = 1)
    {
        return string.Format("AT*PCMD={0},{1},{2}{3}", sequenceNumber, mode, command, Environment.NewLine);
    }

Method FloatConversion tidak ditulis di sini, tapi method tersebut dapat mengubah angka double antara -1 dan 1 menjadi signed integer yang dapat digunakan oleh perintah AT, seperti teks PCMD untuk mengontrol pergerakan.

Kodingan yang ditampilkan di sini tersedia secara gratis sebagai library di NuGet, yang bernama AR.Drone 2.0 Interaction Library, yang menyediakan apa saja yang kamu perlukan untuk mengontrol perangkat dari lepas landas hingga pendaratan.

Antarmuka AR.Drone di NuGetAntarmuka AR.Drone di NuGet

Berkat contoh aplikasi ini, kita bisa menghiraukan detail-detail yang tidak diperlukan sehingga dapat fokus dalam membuat aplikasi, melalui mode interaksi yang berbeda, yang dapat membuat kita mengemudikan drone.

Intel RealSense SDK

Sekarang, mari lihat salah satu fitur paling canggih dan paling mudah digunakan (bagi saya) dari Intel RealSense SDK Рpengenalan suara.

SDK-nya menawarkan dua pendekatan untuk pengenalan suara:

  • Pengalan perintah (dari kamus yang ada)
  • Pengalan teks bebas (pendiktean)

Pendekatan¬†pertama pada dasarnya adalah¬†daftar perintah yang telah didefinisikan pada¬†aplikasi dalam bahasa tertentu untuk mengintruksikan “recognizer“. Kata-kata yang tidak ada dalam daftar akan dihiraukan.

Yang kedua adalah semacam perekam yang “memahami” kosa kata apapaun dalam sebauh free-form stream. Ini ideal untuk transkripsi,¬†automatic subtitling, dan lain-lain.

Untuk proyek kita, kita akan menggunakan opsi pertama karena kita ingin mengimplementasikan beberapa perintah saja untuk dikirimkan ke drone.

Pertama, kita harus mendefinisikan beberapa variabel untuk digunakan:

private PXCMSession Session;
private PXCMSpeechRecognition SpeechRecognition;
private PXCMAudioSource AudioSource;
private PXCMSpeechRecognition.Handler RecognitionHandler;

Session adalah sebuah tag yang diperlukan untuk mengakses I/O dan algoritma SDK-nya, karenakan aksi-aksi berikutnya diturunkan dari instance ini.

SpeechRecognition adalah instance dari modul pengenalan suara yang dibuat dengan fungsi CreateImpl di dalam lingkungan Session.

AudioSource adalah antarmuka dari perangkat untuk membuat dan memilih perangkat masukan audio (dalam contoh kita dipilih perangkat audio pertama yang tersedia agar membuat kodingan sederhana).

RecognitionHandler adalah handler asli yang menetapkan eventhandler untuk event OnRecognition.

Sekarang mari menginisiasi instance Session, AudioSource, dan SpeechRecognition.

Session = PXCMSession.CreateInstance();
if (Session != null)
{
    // session is a PXCMSession instance.
    AudioSource = Session.CreateAudioSource();
    // Scan and Enumerate audio devices
    AudioSource.ScanDevices();

    PXCMAudioSource.DeviceInfo dinfo = null;

    for (int d = AudioSource.QueryDeviceNum() - 1; d >= 0; d--)
    {
        AudioSource.QueryDeviceInfo(d, out dinfo);
    }
    AudioSource.SetDevice(dinfo);

    Session.CreateImpl<PXCMSpeechRecognition>(out SpeechRecognition);

Seperti yang diberitahukan sebelumnya, untuk membuat kodingan tetap sederhana, kita pilih perangkat audio pertama yang tersedia.

PXCMSpeechRecognition.ProfileInfo pinfo;
              SpeechRecognition.QueryProfile(0, out pinfo);
              SpeechRecognition.SetProfile(pinfo);

Lalu ktia perlu meng-query sistem tentang parameter konfigurasi sebenarnya dan menetapkannya ke dalam variabel (pinfo).

Kita juga harus mengeset beberapa paramater ke dalam info profil untuk mengubah bahasa yang diakui. Set level kepercayaan pengenalan suara (nilai yang tinggi meminta pengenalan yang lebih kuat), titik waktu habis pengenalan suara, dan lain-lain.

Pada kasus kita, diset parameter awal pada profil 0 (yang pertama diterima dari Queryprofile).

String[] cmds = new String[] { "Takeoff", "Land", "Rotate Left", "Rotate Right", "Advance",
    "Back", "Up", "Down", "Left", "Right", "Stop" , "Dance"};
int[] labels = new int[] { 1, 2, 4, 5, 8, 16, 32, 64, 128, 256, 512, 1024 };
// Build the grammar.
SpeechRecognition.BuildGrammarFromStringList(1, cmds, labels);
// Set the active grammar.
SpeechRecognition.SetGrammar(1);

Selanjutnya, kita akan mendefinisikan kamus tata bahasa untuk menginstruksikan sistem pengenalan suara. Menggunakan BuildGrammarFromStringList, kita buat daftar senderhana dari kata kerja dan menyesuaikan nilai yang dihasilkan mendefinisikan tata bahasa nomor 1.

Kita bisa mendefinisikan beberapa tata bahasa untuk digunakan di aplikasi kita dan mengaktifkan satu per satu ketika dibutuhkan, jadi kita bisa membuat kamus perintah yang berbeda-beda untuk semua bahasa yang didukung dan menyediakan jalan bagi pengguna untuk berganti bahasa yang dikenal oleh SDK.

Pada kasus ini, kamu harus menginstal file DLL terkait untuk dukungan bahasa tertentu (instalasi awal SDK hanya menginstal bahasa US English). Pada contoh ini, kita hanya menggunakan satu set tata bahasa dengan instalasi bawaan US English.

Kita lalu memilih tata bahasa mana yang akan diaktifkan di instance SpeechRecognition.

RecognitionHandler = new PXCMSpeechRecognition.Handler();

RecognitionHandler.onRecognition = OnRecognition;

Instruksi tersebut mendefinisikan eventhandler baru untuk event OnRecognition dan menyerahkannya ke method berikut:

public void OnRecognition(PXCMSpeechRecognition.RecognitionData data)
{
    var RecognizedValue = data.scores[0].label;
    double movement = 0.3;
    TimeSpan duration = new TimeSpan(0, 0, 0, 500);
    switch (RecognizedValue)
    {
        case 1:
            DroneState.TakeOff();
            WriteInList("Takeoff");
            break;
        case 2:
            DroneState.Land();
            WriteInList("Land");
            break;
        case 4:
            DroneState.RotateLeftForAsync(movement, duration);
            WriteInList("Rotate Left");
            break;
        case 5:
            DroneState.RotateRightForAsync(movement, duration);
            WriteInList("Rotate Right");
            break;
        case 8:
            DroneState.GoForward(movement);
            Thread.Sleep(500);
            DroneState.Stop();
            WriteInList("Advance");
            break;
        case 16:
            DroneState.GoBackward(movement);
            Thread.Sleep(500);
            DroneState.Stop();
            WriteInList("Back");
            break;
        case 32:
            DroneState.GoUp(movement);
            Thread.Sleep(500);
            DroneState.Stop();
            WriteInList("Up");
            break;
        case 64:
            DroneState.GoDown(movement);
            Thread.Sleep(500);
            DroneState.Stop();
            WriteInList("Down");
            break;
        case 128:
            DroneState.StrafeX = .5;
            Thread.Sleep(500);
            DroneState.StrafeX = 0;
            WriteInList("Left");
            break;
        case 256:
            DroneState.StrafeX = -.5;
            Thread.Sleep(500);
            DroneState.StrafeX = 0;
            WriteInList("Right");
            break;
        case 512:
            DroneState.Stop();
            WriteInList("Stop");
            break;
        case 1024:
            WriteInList("Dance");
            DroneState.RotateLeft(movement);
            Thread.Sleep(500);
            DroneState.RotateRight(movement);
            Thread.Sleep(500);
            DroneState.RotateRight(movement);
            Thread.Sleep(500);
            DroneState.RotateLeft(movement);
            Thread.Sleep(500);
            DroneState.GoForward(movement);
            Thread.Sleep(500);
            DroneState.GoBackward(movement);
            Thread.Sleep(500);
            DroneState.Stop();
            break;
        default:
            break;

    }
    Debug.WriteLine(data.grammar.ToString());
    Debug.WriteLine(data.scores[0].label.ToString());
    Debug.WriteLine(data.scores[0].sentence);
    // Process Recognition Data
}

Method ini berfungsi untuk mendapatkan nilai yang dihasilkan dari data pengenalan suara dan mengeksekusi perintah terkait (pada kasus ini perintah terkaitnya adalah instruksi penerbangan untuk drone).

Setiap perintah drone mengacu pada panggilan DroneState dengan method tertentu (TakeOff, GoUp, DoDown, dan lain-lain) dengan parameter tertentu untuk pergerakan atau durasi, mengacu pada tiap kasus ke jumlah pergerakan atau durasi waktu tertentu untuk itu.

Beberapa perintah membutuhkan panggilan eksplisit ke method Stop untuk menginterupsi aksi sedang dilakukan, jika tidak drone akan terus bergerak seperti yang diinstruksikan (lihat kodingan sebelumnya untuk perintah-perintah tersebut).

Pada beberapa kasus, Thread.Sleep perlu untuk dimasukkan antara dua perintah untuk mengizinkan penyelesaian operasi sebelumnya sebelum mengirim perintah baru.

Untuk dapat mengetes pengenalan suara bahkan jika kita sedang tidak memili drone yang tersedia, saya sudah memasukkan variabel (dikontrol dengan boks centang yang tersedia di menu utama) yang menginstruksikan mode fungsional Drone Stub yang membuat perintah namun tidak mengirimnya.

Untuk menutup aplikasi, panggil method OnClosing untuk menutup dan menghancurkan semua instance dan handler dan untuk membersihkan sistem.

Pada kodingannya, kamu dapat melihat beberapa perintah debug yang mencetak beebrapa informasi di tampilan debug Visual Studio ketika mengetes sistem.

Kesimpulan

Pada artikel ini telah ditampilkan bagaimana caranya kita dapat berinteraksi dengan perangkat sekompleks drone menggunakan antar muka interaksi dengan bahasa natural.

Kita juga sudah melihat bagaimana caranya mendefinisikan kamus kata kerja sederhana dan menginstruksikan sistem untuk memahaminya dan oleh karena itu mengontrol perangkat kompleks seperti drone dalam penerbangan.

Apa yang saya tampilkan di artikel ini adalah hanya sebagian kecil dari kemungkinan-kemungkinan yang ada untuk mengoperasikan drone, dan terdapat opsi tak terbatas yang memungkinkan.

* Artikel ini adalah hasil kerja sama dengan Intel Developer Zone. Artikel asli bisa dilihat di link ini.