Evolusi atau buat basis untuk robot pada platform ARDUINO, dan kami menggerakkan sensor dan video ke komputer melalui smartphone

Bagi para pembaca GeekTimes, artikel (keempat) berikutnya yang telah lama dinanti-nantikan tentang apa yang akan terjadi jika Anda mencampur arduino, ESP8266, WI-FI lagi, bumbui dengan smartphone Android dan taburkan di atas aplikasi JAVA.

Kami akan berbicara tentang robot dari artikel sebelum yang terakhir, yang datang setidaknya untuk menjadi sedikit lebih pintar.

gambar

Siapa peduli, selamat datang ke kucing.

Jika Anda tidak terlalu tertarik membaca artikel-artikel lama, maka secara singkat - intinya adalah untuk mengendalikan troli beroda empat yang biasa di platform Arduino, jembatan UART nirkabel yang dikembangkan oleh saya berdasarkan modul ESP8266 yang terkenal ditambahkan ke dalamnya. Juga untuk kenyamanan (dan secara umum ini adalah tujuan utama) menggunakan ESP yang sama, saya menulis seorang programmer untuk arduinki, yang memungkinkan Anda untuk mem-flash-nya sendiri dari jarak jauh.

gambar

Yaitu, kereta di suatu tempat yang jauh (tetapi dalam jaringan WI-FI Anda) melakukan perjalanan (ya, saya suka menulis kata ini), mengirim data dan menerima perintah, dan jika perlu, berdasarkan pesanan, ia juga dapat mengubah program dalam mikrokontroler AVR-nya. Dengan demikian, program pada JAVA untuk PC dijalankan, berjalan yang Anda dapat menikmati kontrol dan mendapatkan telemetri primitif dalam bentuk jarak yang ditempuh (saklar buluh dan magnet pada roda).

gambar

Selanjutnya, saya berhasil bereksperimen dalam artikel berikutnya dengan mengendalikan kereta menggunakan smartphone - tombol, miring, dan bahkan suara. Tetapi ketika kereta pergi ke kamar sebelah, maka bahkan suara tidak bisa mengembalikannya (tidak seperti kucing). Dia pergi ke sana, mengetuk dinding dan furnitur, terjerat dalam kabel, tetapi selain informasi tentang jarak yang ditempuh, dia tidak mengirim apa-apa.

Oleh karena itu, segera muncul ide untuk menyediakan Terminator organ indera di masa depan. Salah satu opsi termudah untuk ini adalah penggunaan sonar.

gambar

Algoritma bekerja untuk aib sederhana, kami memulai sensor dari satu depan dan pada saat yang sama beberapa mikrokontroler counter. HC-SR04 mulai menembakkan ultrasound ke kejauhan. Sinyal respons dari sensor melalui kabel lain memberi sinyal akhir pengukuran jarak, dan interval waktu antara awal dan respons sebanding dengan jarak yang diukur. Oleh karena itu, pada saat ini kami memperlambat konter dan melihat berapa banyak yang masuk.

Akurasi diperoleh hingga sekitar satu sentimeter, dan jangkauan meter hingga dua. Dia tidak suka permukaan yang lembut dan wol (misalnya, kucing), di mana setiap sinyal gema tenggelam tanpa dapat ditarik kembali.

Area yang terpengaruh sudut pandang HC-SR04 kecil, sehingga untuk mengetahui apa yang terjadi di depan dalam kerangka sudut setidaknya 90 derajat, diinginkan untuk melakukan hal berikut:

  1. pasang sensor ke mesin servo dan lihat di arah yang berbeda
  2. menaruh beberapa sensor.

Pada awalnya, saya menerapkan opsi pertama, menempatkan sonar pada SG90 servo murah dan kereta berubah menjadi bajak. Begitu banyak waktu yang diperlukan untuk mengambil setidaknya tiga pengukuran, jadi pada dasarnya gerobak berdiri berputar dengan servo, kemudian dengan hati-hati bergerak maju, tetapi tidak terlalu jauh (dan tiba-tiba sebuah hambatan muncul di samping), dan sekali lagi merasakan ruang di depan itu sendiri. Meski begitu, suaranya bukan cahaya untuk Anda.

Karena itu, tanpa basa-basi, saya menempatkan tiga sistem sonar sekaligus. Gerobak itu memperoleh penampilan laba-laba chthonic, berhenti tumpul di depan rintangan, dan mulai menumpang mereka saat bepergian. Tetapi otak pada akhirnya cukup untuk tidak terjebak dalam lingkungan yang ramah. Kami harus pindah - ke otonomi dan kemajuan. Dan di sini, tanpa indra yang berbeda, bahkan cacing nematoda akan memberi tahu Anda, Anda tidak dapat melakukannya.


Lebih jauh, biasanya para penggemar mulai memahat pada kreasi mereka berbagai sensor baru, seperti giroskop, akselerometer, magnetometer, dan bahkan sensor KEBAKARAN (semua produk Cina yang tak bernama dalam jutaan jumlah untuk Arduino). Dan saya juga hampir memulai sepanjang jalan yang licin ini, tetapi mengubah pikiran saya pada waktunya. Dan saya melakukannya karena alasan ini. Dalam perspektif terjauh, robot robot seharusnya mendapatkan penglihatan dalam bentuk kamera dan juga memahami apa yang dilihatnya. Tetapi mikrokontroler AVR dari papan Arduino akan mengucapkan "selamat tinggal" kepada Anda pada tahap menerima video, belum lagi memprosesnya. Dan tiba-tiba tatapanku tertuju pada smartphone tua GALAXY S7, yang sudah hancur lebur.

Kekuatan komputasi seperti itu, delapan core, memori 4 gigabytes, dua kamera, akses ke jaringan, apa lagi yang diperlukan untuk mengubah monyet menjadi seseorang?

Tapi kita hanya perlu desain kecil agar smartphone kita bersandar pada troli dan agar bisa dengan mudah diletakkan dan dilepas.

gambar

Lalu saya naik ke situs pengembang Android untuk mencari tahu fitur apa saja yang bisa diberikan oleh smartphone biasa kepada kami. Ternyata tidak kecil. Secara teoritis Anda bisa

akses sensor sensor berikut.
TYPE_ACCELEROMETER

TYPE_AMBIENT_TEMPERATURE

TYPE_GAME_ROTATION_VECTOR

GEOMAGNETIC_ROTATION_VECTOR

TYPE_GRAVITY

TYPE_GYROSCOPE

TYPE_GYROSCOPE_UNCALIBRATED

TYPE_HEART_BEAT

TYPE_HEART_RATE

TYPE_LIGHT

TYPE_LINEAR_ACCELERATION

TYPE_LOW_LATENCY_OFFBODY_DETECT

TYPE_MAGNETIC_FIELD

TYPE_MAGNETIC_FIELD_UNCALIBRATED

TYPE_MOTION_DETECT

TYPE_ORIENTATION

TYPE_POSE_6DOF

TYPE_PRESSURE

TYPE_PROXIMITY

TYPE_RELATIVE_HUMIDITY

TYPE_ROTATION_VECTOR

TYPE_SIGNIFICANT_MOTION

TYPE_STATIONARY_DETECT

TYPE_STEP_COUNTER

TYPE_STEP_DETECTOR


Seperti yang mereka katakan, apa yang ada di sana saja! Dan memang, tidak banyak yang khusus untuk GALAXY S7. Misalnya, sensor kelembaban. Dan suhu sekitar (meskipun saya mengerti bahwa berada di dalam case, itu akan menunjukkan suhu smartphone itu sendiri). Tetapi sensor tekanan dan cahaya hadir. Belum lagi giroskop, akselerometer, yang menggunakannya, Anda dapat dengan mudah menentukan posisi Anda di ruang angkasa.

Akibatnya, sebuah keputusan telah matang, biarkan smartphone menerima dan memproses semua informasi dari tingkat atas - video dan semua berbagai sensor ini. Dan platform Arduino akan bertanggung jawab, untuk berbicara, untuk ketidaksadaran - untuk segala sesuatu yang sudah bekerja dan tidak memerlukan pengerjaan ulang, semua motor ini, sonar, buluh switch dan sebagainya.

Karena sulit untuk men-debug program secara langsung pada smartphone, bahkan dengan UDB, saya memutuskan untuk membiarkan semuanya ditransfer ke komputer pribadi yang normal dan diproses di sana. Dan entah bagaimana, ketika akan ada versi yang berfungsi, kami akan mengembalikan otak ke gerobak. Kita harus memulai dari yang kecil, dan memang menarik untuk melihat transmisi video dari keranjang yang panik.

Data dari sensor dapat dikirim dalam garis sederhana, melalui server klien primitif, tidak ada masalah dengan ini sama sekali. Tetapi dengan transfer video, segera ada kesulitan. Secara umum, saya membutuhkan siaran streaming real-time dari kamera ponsel cerdas ke jendela aplikasi di komputer. Ini untuk sekarang. Di masa depan, bukan hanya saya, tetapi semacam sistem pengenalan pola bisa menatap gambar ini di jendela. Misalnya JAVA OpenCV. Atau mungkin bahkan jaringan saraf dari cloud: D. Saya tidak tahu, panggung ini masih sangat jauh. Tapi saya ingin melihat dunia dengan "mata" truk robot.

Semua orang tahu tentang banyak aplikasi seperti "kamera seluler" dari Google store, tempat Anda menangkap aliran video dari kamera ponsel cerdas dengan membuka browser dengan IP yang diinginkan di komputer. Oleh karena itu, pada awalnya saya berpikir bahwa tidak akan sulit untuk menerjemahkan dari GALAXY saya sendiri (yang bukan merupakan kesalahan yang lemah), jadi pertama-tama Anda perlu memeriksa bagaimana dengan penerimaannya di komputer, mengingat bahwa saya dapat menulis entah bagaimana hanya di JAVA.

Ternyata, dengan pemutaran video JAWA, secara halus, tidak terlalu baik. Suatu ketika pada tahun 1997, Java Media Framework dirilis - sebuah perpustakaan yang memfasilitasi pengembangan program yang bekerja dengan audio dan video dari pencipta JAVA sendiri. Tapi, di suatu tempat setelah 2003, baut besar diletakkan di atasnya dan sejak itu sudah 15 tahun. Setelah beberapa percobaan, saya berhasil menjalankan satu file di jendela, saya tidak ingat yang mana (sepertinya AVI), tetapi pemandangan ini terlihat sangat menyedihkan. File dengan ekstensi lain tidak ingin berjalan sama sekali, dalam kasus ekstrim ada satu trek audio.

Di Internet, saya menemukan dua proyek alternatif untuk bekerja dengan video: Xuggler dan aprica VLCj. Proyek pertama menarik dalam kemampuannya, tetapi juga sudah lama mati, tetapi yang kedua ternyata cukup hidup dan menarik dalam idenya. Orang-orang mengambil dan diikat ke JAVA media player VLC terkenal yang terkenal. Yaitu, aprica tidak menggunakan codec yang ditulis sendiri, tetapi menggunakan codec yang sudah jadi. Dengan itu, Anda akan kehilangan file apa pun. Keputusan yang bijak, tetapi yang utama adalah Anda sudah menginstal VLC player ini di komputer Anda. Nah, siapa yang tidak memilikinya? Namun, satu-satunya peringatan adalah bahwa Anda memiliki kedalaman bit yang sama dengan pemain dan JAWA. Sebagai contoh, saya kemudian, dengan terkejut, mengetahui bahwa saya masih memiliki VLC 32-bit di komputer saya, tidak seperti JAVA 64-bit. Dan setengah hari hidup hilang sia-sia.

Pengembang Caprica menjanjikan banyak hal kepada pengguna di situs web mereka. Dan semua format file dan peluncuran di beberapa jendela dalam aplikasi JAVA, memutar video dari You-Tube, menangkap aliran video "langsung" dan sebagainya. Tetapi kenyataan pahit menempatkan segalanya pada tempatnya. Tidak, mereka tidak menipu dengan file - semuanya dimainkan. Namun sekarang video dari YouTube tidak lagi diinginkan. Pada awalnya saya tidak mengerti mengapa, tetapi kemudian saya melihat sebuah tulisan di log yang entah bagaimana, di suatu tempat, tidak mungkin untuk menjalankan skrip lua tertentu dan segera mengingatnya:
'Kerusakan layar' laman web YouTube ini rapuh - jika YouTube mengubah struktur laman web mereka, maka VLC terkadang gagal menemukan URL streaming, saat ini terjadi, Anda harus menunggu pengembang untuk memberikan LUA baru skrip dan tunggu VLC versi baru dirilis.
Singkatnya, tampaknya YouTube telah mengubah struktur halaman webnya dan saya harus menunggu rilis baru. Di sisi lain, saya memerlukan siaran video "langsung", dan tidak memutar file dari situs. Artinya, bahkan jika skrip lua bekerja, itu tidak akan banyak membantu saya.

Tetapi saya tidak menemukan streaming yang dijanjikan sama sekali, meskipun ada tertulis bahwa:

Server streaming jaringan (mis. Stasiun radio jaringan atau video on demand server);
Klien streaming jaringan;
Mungkin Daftar Keinginan, atau mungkin dalam versi komersial, sulit dikatakan.

Tetapi file diputar, saya ulangi, tanpa keluhan. Misalnya, Anda dapat membuat, di sini ada ketidaksenonohan


Instalasi paket itu sendiri tidak sulit dan bahkan dijelaskan secara rinci, misalnya di sini . Benar, saya entah bagaimana dengan kikuk meresepkan variabel lingkungan dan sekarang VLC saya mulai untuk pertama kalinya dengan penundaan sepuluh detik, tetapi kemudian menyimpan apa yang dibutuhkan dalam cache dan kemudian dalam sesi saat ini dimulai tanpa jeda.

Kemudian dalam proyek JAVA Anda, Anda meresepkan dependensi yang diperlukan dan Anda dapat mulai memukau pemutar media di windows JAVA untuk bekerja.

Dengan demikian menandai perkiraan rencana kerja di sisi komputer pribadi, saya kembali ke sumber data video, ponsel Android ANDROID saya.

Tapi di sini saya mengharapkan kekecewaan yang agak besar. Setelah memindai beberapa situs dan bahkan manual ANDROID online, saya menemukan bahwa tidak mungkin untuk mengatur streaming secara real time menggunakan cara biasa. Seperti di Caprica, Anda hanya dapat membaca file yang sudah direkam. Artinya, kamera dihidupkan, MEDIA RECORDER mulai merekam ketika perlu dihentikan. Dan kita bisa mendapatkan akses ke data (dan transfernya) hanya setelah berhenti.

Saya menemukan konfirmasi kesimpulan saya di sebuah artikel kuno. Benar-benar ada petunjuk bahwa seseorang pernah menipu Android untuk berpikir bahwa itu sedang menulis ke file, tetapi kenyataannya mereka menyelipkan buffer. Tetapi bagaimanapun juga, seperti yang sudah dijelaskan sebelumnya, saya tidak bisa menangkap aliran video seperti itu di sisi PC dalam aplikasi JAVA.

Oleh karena itu, keputusan sederhana, ek, sementara (saya ingin tekankan) dibuat - untuk mengirim streaming video dari kamera menjadi beberapa bagian selama dua detik. Bukan bajak, tentu saja, tetapi bajak sudah cukup.

Setelah menetapkan keputusan ini, saya mulai menguasai kelas CAMERA dan MEDIA RECORDER. Ada beberapa contoh kode di jaringan untuk meluncurkan kamera dan merekam file video, tetapi untuk beberapa alasan tidak ada yang bekerja untuk saya. Baik pada emulator, maupun pada perangkat nyata. Ternyata alasannya terletak pada izin, yaitu izin. Ternyata contoh kode ditulis pada masa itu ketika Android masih bebas dan programmer dapat melakukan apa pun yang dia inginkan jika dia menulis semua izin yang diperlukan dalam manifes. Tetapi versi OC saya saat ini tidak mengizinkan ini. Pertama, Anda harus memberi izin kepada pengguna untuk menulis file dan menghidupkan kamera segera setelah memulai aplikasi. Ini membebani saya aktivitas ekstra, yaitu dalam Aktivitas.

Kelas MainActivity.java
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.Camera; import android.media.MediaRecorder; import android.os.AsyncTask; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; import java.io.File; public class MainActivity extends AppCompatActivity { Camera camera; MediaRecorder mediaRecorder; public static MainActivity m; public static boolean Camera_granted; File videoFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) ||(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) ) //ask for authorisation { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 50); } if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)& (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) { Toast.makeText(MainActivity.this,"", Toast.LENGTH_SHORT).show();} m=this; MyTask mt = new MyTask(); mt.execute(); } } class MyTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { boolean ready = false; while(!ready) { if ((ContextCompat.checkSelfPermission(MainActivity.m, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)& (ContextCompat.checkSelfPermission(MainActivity.m, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) { System.out.println(" "); ready=true; } } return null; } @Override protected void onPostExecute(Void result) { Toast.makeText(MainActivity.m,"", Toast.LENGTH_SHORT).show(); Intent intent=new Intent(MainActivity.m,CameraActivity.class); //   : MainActivity.m.startActivity(intent); } } 


Setelah itu, semuanya berjalan dengan baik dan kode kerja berikut muncul. Aktivitas kedua dari kelas Camera_Activity bertanggung jawab untuk bekerja dengan kamera dan merekam file video. Kelas Http_server untuk penerusan (nama, tentu saja, tidak benar, tetapi ternyata secara historis). Kode ini sederhana, di mana pun ada penjelasan.



Semuanya benar-benar terletak di Github. Tautan

Camera_Activity
 import android.hardware.Camera; import android.media.MediaRecorder; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.ServerSocket; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.content.Context; import android.hardware.Sensor; import static android.hardware.Camera.getNumberOfCameras; import java.io.BufferedOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; /** * Created by m on 01.02.2019. */ public class CameraActivity extends AppCompatActivity implements SensorEventListener { SurfaceView surfaceView; TextView mTextView; Button mStart; Button mStop; Camera camera; MediaRecorder mediaRecorder; public static ServerSocket ss; public static ServerSocket ss2; public static MainActivity m; public static volatile boolean stopCamera=true; public static int count=1; public static File videoFile1; public static File videoFile2; public static File videoFile3; public static volatile byte[] data; public SensorManager mSensorManager; public Sensor mAxeleration, mLight,mRotation,mHumidity,mPressure,mTemperature; public int ax; public int ay; public int az; public double light; public int x; public int y; public int z; public double hum; public double press; public double tempr; public static String Sensors; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); //    mAxeleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); //    mSensorManager.registerListener(this, mAxeleration, SensorManager.SENSOR_DELAY_NORMAL); mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL); mRotation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); mSensorManager.registerListener(this, mRotation, SensorManager.SENSOR_DELAY_NORMAL); mHumidity = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); mSensorManager.registerListener(this, mHumidity, SensorManager.SENSOR_DELAY_NORMAL); mPressure = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); mSensorManager.registerListener(this, mPressure, SensorManager.SENSOR_DELAY_NORMAL); mTemperature = mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); mSensorManager.registerListener(this, mTemperature, SensorManager.SENSOR_DELAY_NORMAL); setContentView(R.layout.camera); // videoFile = new File(Environment.getExternalStorageDirectory() + File.separator+ Environment.DIRECTORY_DCIM + File.separator + "test.3gp"); videoFile1 = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test1.3gp"); videoFile2 = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test2.3gp"); videoFile3 = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test3.3gp"); // videoFile4 = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "testOUT.3gp"); // File file = new File(Environment.getExternalStorageDirectory(),VIDEO_PATH_NAME);//   // "touch" the file try { videoFile1.createNewFile(); videoFile2.createNewFile(); videoFile3.createNewFile(); } catch (IOException e) { } mStart = (Button) findViewById(R.id.btnStartRecord); mStop = (Button) findViewById(R.id.btnStopRecord); surfaceView = (SurfaceView) findViewById(R.id.surfaceView); mTextView = (TextView) findViewById(R.id.textView); SurfaceHolder holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); camera.startPreview(); } catch (Exception e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); mStart.setOnClickListener(new View.OnClickListener() { public void onClick(View v){ System.out.println("  "); //    WriteVideo WV = new WriteVideo(); WV.start(); mStart.setClickable(false); mStop.setClickable(true); Http_server.File_is_sent=true; new ServerCreation().execute();//      } } ); mStop.setOnClickListener(new View.OnClickListener() { public void onClick(View v){ stopCamera = false; // mTextView.setText(" "); releaseMediaRecorder(); releaseCamera(); mStart.setClickable(true); mStop.setClickable(false); System.out.println("  "); Http_server.File_is_sent=true; } } ); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { //    } @Override public void onSensorChanged(SensorEvent event) { //   switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: ax = (int)(event.values[0] * 9); // + -  az = (int)(event.values[2] * 9);//  +  -  // System.out.println(" = "+ ax + "  = " + az); break; case Sensor.TYPE_LIGHT: light = event.values[0]; // System.out.println(" = "+ light); break; case Sensor.TYPE_ORIENTATION: x = (int)event.values[0]; y = (int)event.values[1]+90; //  +  -  z = (int)event.values[2]; // + -  // System.out.println("x = "+ x + " y = " + y+ " z= "+ z); break; case Sensor.TYPE_LINEAR_ACCELERATION: hum = event.values[2]; int k = (int)(hum*100); hum = - (double)k;//   ,    /2 // System.out.println(hum); break; case Sensor.TYPE_PRESSURE: press = event.values[0]*760/10.1325; int i = (int) press; press = (double)i/100; // System.out.println(press); break; case Sensor.TYPE_AMBIENT_TEMPERATURE: tempr = event.values[0]; System.out.println(tempr); break; } Sensors = " tangaz_1 "+ az+ " kren_1 " + ax + " tangaz_2 "+ y + " kren_2 " + z + " forvard_accel "+ hum + " light " + light+ " "; // System.out.println(Sensors); } @Override protected void onResume() { super.onResume(); releaseCamera(); releaseMediaRecorder(); int t = getNumberOfCameras(); mTextView.setText(""+t); if(camera == null) { camera = Camera.open(); // camera.unlock(); } else{ } } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); releaseCamera(); releaseMediaRecorder(); } private boolean prepareVideoRecorder() { if (mediaRecorder==null) {mediaRecorder = new MediaRecorder();} camera.unlock(); mediaRecorder.setCamera(camera); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); switch (count) { case 1: mediaRecorder.setOutputFile(videoFile1.getAbsolutePath()); break; case 2: mediaRecorder.setOutputFile(videoFile2.getAbsolutePath()); break; case 3: mediaRecorder.setOutputFile(videoFile3.getAbsolutePath()); break; } mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface()); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setVideoSize(640,480); mediaRecorder.setOrientationHint(90);//    ,     mediaRecorder.setVideoFrameRate(30); mediaRecorder.setVideoEncoder(1); try { mediaRecorder.prepare(); } catch (Exception e) { e.printStackTrace(); releaseMediaRecorder(); return false; } return true; } private void releaseMediaRecorder() { if (mediaRecorder != null) { mediaRecorder.release(); mediaRecorder = null; if (camera != null) { // camera.lock();//         } } } public void releaseCamera() { if (camera != null) { // camera.setPreviewCallback(null); // SurfaceView.getHolder().removeCallback(SurfaceView); camera.release(); camera = null; } } private class WriteVideo extends Thread{ public void run () { stopCamera=true; do{ // releaseMediaRecorder(); // releaseCamera(); if(camera == null) { camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); } System.out.println(""); if (prepareVideoRecorder()) { mediaRecorder.start(); } else { releaseMediaRecorder(); } try { Thread.sleep(2000);//     } catch (Exception e) { } count++; if(count==3){count=1;}// 4 if (mediaRecorder != null) { releaseMediaRecorder(); } System.out.println(""); File f=null; switch (count) { case 1: f = videoFile2;// 3 break; case 2: f = videoFile1;//1 break; case 3: f = videoFile2; break; } try { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f)); data = new byte[bis.available()]; bis.read(data); bis.close(); }catch (Exception e) { System.out.println(e); } if(Http_server.File_is_sent)//       { System.out.println("  "); new HTTP_Server_Calling().execute(); Http_server.File_is_sent=false; } } while(stopCamera); if (mediaRecorder != null) { System.out.println(""); } } } } class ServerCreation extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { try { CameraActivity.ss = new ServerSocket(40001);//      System.out.println("  "); new Http_server(CameraActivity.ss.accept()); CameraActivity.ss2 = new ServerSocket(40002);//      }catch (Exception e) { System.out.println(e); System.out.println("   "); } new HTTP_Server_Calling2().start(); return null; } @Override protected void onPostExecute(Void result) { } } class HTTP_Server_Calling extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { try { new Http_server(CameraActivity.ss.accept()); } catch (Exception e) { System.out.println(e); } return null; } @Override protected void onPostExecute(Void result) { } } class HTTP_Server_Calling2 extends Thread//           { public void run() { while (CameraActivity.stopCamera) { try { Thread.sleep(500);//      new Http_server_Sensors(CameraActivity.ss2.accept()); } catch (Exception e) { System.out.println(e); } } } } class Http_server extends Thread { public Socket socket; public static volatile boolean File_is_sent=true; Http_server(Socket s) { System.out.println("  "); socket = s; setPriority(MAX_PRIORITY); start(); } public void run() { try { System.out.println("    "); BufferedOutputStream bos = new BufferedOutputStream((socket.getOutputStream())); bos.write(CameraActivity.data); bos.flush(); bos.close(); socket.close(); } catch (Exception e) { System.out.println(e); } File_is_sent = true; } } public class Http_server_Sensors extends Thread { public Socket socket; PrintWriter pw; Http_server_Sensors(Socket s) { socket = s; setPriority(MAX_PRIORITY); start(); } public void run() { try { pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);//    System.out.println(CameraActivity.Sensors); pw.println(CameraActivity.Sensors);//  pw.flush(); pw.close(); socket.close(); } catch (Exception e) { System.out.println(e); } } } 



Inti dari program setelah memulai dan menyalakan kamera adalah sebagai berikut:
menulis video selama dua detik ke file pertama,
kami menulis video selama dua detik ke file kedua, dan sementara itu kami mengirim file pertama melalui TCP-IP melalui WI-FI lokal ke komputer,
tulis file pertama lagi, dan sementara itu kirim yang kedua,
dan sebagainya.

Kemudian siklus berulang sampai tombol "berhenti" ditekan atau baterai ponsel pintar mati. Pada prinsipnya, dimungkinkan untuk menerapkan analog dari tombol yang ditekan, menggunakan perintah dari komputer, juga melalui TCP, ini tidak sulit.

Pada awalnya, buffer video, untuk berjaga-jaga, terdiri dari tiga file format 3GP (kami menulis yang pertama, mengirim yang ketiga, kami menulis yang kedua, kami mengirim yang pertama, kami menulis yang ketiga, kami mengirim yang kedua), tetapi kemudian ternyata kedua file tersebut cukup cukup (merekam dan saling mengirim satu sama lain jangan ikut campur).

Dengan resolusi kamera 640 x 480, file diperoleh, sekitar 200-300 kB, yang cukup sulit untuk router saya. Saya belum repot-repot dengan suaranya, tetapi semuanya tampak sederhana di sana: Anda memasang enkoder audio yang diperlukan, bitrate, jumlah saluran dan sejenisnya.

Beberapa saat kemudian, ketika saya melakukan debug pada transfer video, saya menambahkan kode juga dengan mengirimkan informasi dari sensor smartphone. Semuanya ditransmisikan sepele dalam satu baris, tetapi saya tidak bisa mentransfernya melalui soket yang sama dengan video. Rupanya, kelas-kelas untuk mentransmisikan string PrintWriter dan mentransmisikan data biner. BufferedOutputStream menggunakan aliran yang berbeda, tetapi kemudian mereka memiliki satu buffer output, di mana mereka berhasil merusak satu sama lain. Akibatnya, video mulai glitch dan hancur. Selain itu, file video ditransmisikan setiap dua detik sekali, dan untuk sensor, interval ini terlalu besar. Oleh karena itu, diputuskan untuk mendistribusikannya di soket yang berbeda sehingga tidak saling mengganggu. Untuk alasan ini, kelas baru Http_server_Sensors telah muncul.

Jadi, kami mengatur pengiriman, sekarang lagi kami akan kembali ke sisi penerima gelap.

Seperti yang telah kita lihat dari contoh pertama, memutar file video dalam aplikasi JAVA menggunakan pemutar VLC sekarang tidak menimbulkan masalah. Yang utama adalah mendapatkan file-file ini.

Program demo berikut bertanggung jawab untuk ini.

Pemutar video
 import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent; import uk.co.caprica.vlcj.discovery.NativeDiscovery; import uk.co.caprica.vlcj.player.MediaPlayer; import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter; import java.io.*; import java.net.Socket; public class VideoPlayer { public final JFrame frame; public static EmbeddedMediaPlayerComponent mediaPlayerComponent; public final JButton pauseButton; public final JButton rewindButton; public final JButton skipButton; public static String mr1, mr2; public static boolean playing_finished = false; public static boolean File_1_play_starting = false; public static boolean File_1_play_finished = false; public static boolean File_2_play_starting = false; public static boolean File_2_play_finished = false; //192.168.1.128 public static void main(final String[] args) { new NativeDiscovery().discover(); mr1 = "D:\\test1.3gp"; mr2 = "D:\\test2.3gp"; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { VideoPlayer vp = new VideoPlayer(); vp.mediaPlayerComponent.getMediaPlayer().setPlaySubItems(true); VideoPlayer.playing_finished=false; new Control().start();//   // while (!Http_client.File_ready) { // System.out.println(""); try { Thread.sleep(100); } catch (Exception e) { } } // while (true) { playing_finished = false; } // System.out.println("  1"); } }); } public VideoPlayer() { frame = new JFrame("My First Media Player"); frame.setBounds(100, 100, 600, 400); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println(e); mediaPlayerComponent.release(); System.exit(0); } }); JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); mediaPlayerComponent = new EmbeddedMediaPlayerComponent(); contentPane.add(mediaPlayerComponent, BorderLayout.CENTER); frame.setContentPane(contentPane); JPanel controlsPane = new JPanel(); pauseButton = new JButton("Pause"); controlsPane.add(pauseButton); rewindButton = new JButton("Rewind"); controlsPane.add(rewindButton); skipButton = new JButton("Skip"); controlsPane.add(skipButton); contentPane.add(controlsPane, BorderLayout.SOUTH); pauseButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { mediaPlayerComponent.getMediaPlayer().pause(); } }); rewindButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { mediaPlayerComponent.getMediaPlayer().skip(-10000); } }); skipButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { mediaPlayerComponent.getMediaPlayer().skip(10000); } }); mediaPlayerComponent.getMediaPlayer().addMediaPlayerEventListener(new MediaPlayerEventAdapter() { @Override public void playing(MediaPlayer mediaPlayer) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame.setTitle(String.format( "My First Media Player - %s", mediaPlayerComponent.getMediaPlayer() )); } }); } @Override public void finished(MediaPlayer mediaPlayer) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { playing_finished = true; System.out.println("finished " + playing_finished); //closeWindow(); } }); } @Override public void error(MediaPlayer mediaPlayer) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog( frame, "Failed to play media", "Error", JOptionPane.ERROR_MESSAGE ); closeWindow(); } }); } }); frame.setVisible(true); //mediaPlayerComponent.getMediaPlayer(); } public void start(String mrl) { mediaPlayerComponent.getMediaPlayer().setPlaySubItems(true); mediaPlayerComponent.getMediaPlayer().prepareMedia(mrl); //mediaPlayerComponent.getMediaPlayer().parseMedia(); mediaPlayerComponent.getMediaPlayer().playMedia(mrl); // mediaPlayerComponent. } public void closeWindow() { frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); } } class PlayFile { public static void run(int number) { if (number==1) { VideoPlayer.mediaPlayerComponent.getMediaPlayer().prepareMedia(VideoPlayer.mr1); System.out.println("  1"); VideoPlayer.mediaPlayerComponent.getMediaPlayer().start(); VideoPlayer.mediaPlayerComponent.getMediaPlayer().playMedia(VideoPlayer.mr1); VideoPlayer.File_1_play_starting = true; VideoPlayer.File_1_play_finished = false; while (!VideoPlayer.playing_finished) {//      try { Thread.sleep(1); } catch (Exception e) { } } VideoPlayer.mediaPlayerComponent.getMediaPlayer().stop(); VideoPlayer.playing_finished = false; VideoPlayer.File_1_play_starting = false; VideoPlayer.File_1_play_finished = true; } { try { Thread.sleep(10); } catch (Exception e) { } } if (number==2) { VideoPlayer.mediaPlayerComponent.getMediaPlayer().prepareMedia(VideoPlayer.mr2); System.out.println("  2"); VideoPlayer.mediaPlayerComponent.getMediaPlayer().start(); VideoPlayer.mediaPlayerComponent.getMediaPlayer().playMedia(VideoPlayer.mr2); VideoPlayer.File_2_play_starting = true; VideoPlayer.File_2_play_finished = false; while (!VideoPlayer.playing_finished) { try { Thread.sleep(1); } catch (Exception e) { } } VideoPlayer.mediaPlayerComponent.getMediaPlayer().stop(); VideoPlayer.playing_finished = false; VideoPlayer.File_2_play_starting = false; VideoPlayer.File_2_play_finished = true; } { try { Thread.sleep(10); } catch (Exception e) { } } } } public class Control extends Thread{ public static boolean P_for_play_1=false; public static boolean P_for_play_2=false; public void run() { // new Http_client(1).start(); while (!Http_client.File1_reception_complete) { try { Thread.sleep(1); } catch (Exception e) { } } while (true) { new Http_client(2).start(); PlayFile.run(1); while (!VideoPlayer.File_1_play_finished) { try { Thread.sleep(1); } catch (Exception e) { } } while (!Http_client.File2_reception_complete) { try { Thread.sleep(1); } catch (Exception e) { } } new Http_client(1).start(); PlayFile.run(2); while (!VideoPlayer.File_2_play_finished) { try { Thread.sleep(1); } catch (Exception e) { } } while (!Http_client.File1_reception_complete) { try { Thread.sleep(1); } catch (Exception e) { } } //PlayFile.run(1); } } } public class Http_client extends Thread { public static boolean File1_starts_writing=false; public static boolean File1_reception_complete=false; public static boolean File2_starts_writing=false; public static boolean File2_reception_complete=false; public int Number; public BufferedOutputStream bos; Http_client(int Number) { this.Number = Number; } public void run(){ try { Socket socket= new Socket("192.168.1.128", 40001); // System.out.println(" "); // PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true); // pw.println("ready");// Greetings with CLIENT // System.out.println(" "); BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); if (Number ==1) { File1_starts_writing=true; File1_reception_complete=false; System.out.println("  1,    2"); bos = new BufferedOutputStream(new FileOutputStream(VideoPlayer.mr1)); byte[] buffer = new byte[32768]; while (true) { //     int readBytesCount = bis.read(buffer); if (readBytesCount == -1) { //   break; } if (readBytesCount > 0) { //    - ,   bos.write(buffer, 0, readBytesCount); } } System.out.println(" "); System.out.println(" "); bos.flush(); bos.close(); File1_starts_writing=false; File1_reception_complete=true; } if (Number==2) { File2_starts_writing=true; File2_reception_complete=false; System.out.println("  2,    1"); bos = new BufferedOutputStream(new FileOutputStream(VideoPlayer.mr2)); byte[] buffer = new byte[32768]; while (true) { //     int readBytesCount = bis.read(buffer); if (readBytesCount == -1) { //   break; } if (readBytesCount > 0) { //    - ,   bos.write(buffer, 0, readBytesCount); } } System.out.println(" "); System.out.println(" "); bos.flush(); bos.close(); File2_starts_writing=false; File2_reception_complete=true; } socket.close(); // pw.close(); bis.close(); } catch(Exception e){ System.out.println(e); System.out.println("gfgfg"); } try { Thread.sleep(10); } catch (Exception e) { } } } 


Esensinya sederhana. Klien TCP diluncurkan, yang mulai menunggu server siap pada smartphone. Setelah menerima file pertama, itu mulai diputar segera dan file nomor dua diharapkan secara paralel. Selanjutnya, diharapkan akhir penerimaan file kedua, atau akhir memainkan yang pertama. Yang terbaik dari semuanya, tentu saja, tiga bintang , ketika file mengunduh lebih cepat daripada yang dimainkannya. Jika Anda kehilangan file pertama, tetapi belum menerima yang kedua, maka semuanya menunggu ... Kami akan menunjukkan layar hitam. Jika tidak, segera mulai putar file kedua dan unduh secara bersamaan file pertama lagi.
Saya memiliki harapan yang tidak jelas bahwa jeda antara beralih file yang diputar akan kurang dari waktu reaksi mata manusia, tetapi itu tidak terwujud. Santai, tentu saja, VLC ini.



Alhasil, kami mendapatkan semacam video diskontinyu keji (trilobite pertama, tampaknya, melihat dunia seperti ini), di mana penyesuaian ketajaman terus terjadi. Dan kita harus memperhitungkan bahwa videonya juga terlambat selama dua detik. Singkatnya, dalam produksi saya tidak merekomendasikan untuk menyebarkannya. Tapi saya kekurangan ikan dan trilobite, seperti yang mereka katakan ...

Meringkas hal di atas, kita dapat benar-benar mengatakan bahwa:

Mengirim video dalam jumlah banyak pada dasarnya tidak beroperasi dan hanya dapat berguna dalam kasus-kasus di mana video cukup panjang dan Anda tidak perlu reaksi kedua terhadap apa yang terjadi.

Transmisi video melalui TCP-IP juga merupakan ide yang salah, apa pun yang dikatakan beberapa orang tentang Habr tentang kecepatan transfer data menggunakan protokol ini (yang seharusnya lebih cepat daripada UDP). Tentu saja, intranet nirkabel modern memiliki karakteristik yang baik untuk menyediakan jabat tangan berkesinambungan dari server dan klien TCP, dan TCP itu sendiri tampaknya telah ditingkatkan untuk data yang panjang, tetapi tetap saja colokan di antara pemutaran video fokus, seperti yang Anda lihat di demo, muncul secara berkala.

Tapi, setidaknya untuk masa depan, pemikiran berikut muncul:

  1. mengirim frame-by-frame (bukan video, tetapi foto) melalui UDP, tetapi mengontrol informasi melalui TCP,
  2. drive bingkai foto semua melalui UDP tetapi dengan sinyal sinkronisasi di saluran yang sama.

Tentu saja, sejauh ini ada lebih banyak pertanyaan daripada jawaban. Apakah ada kecepatan penerimaan yang cukup di JAVA dengan tingkat abstraksi dalam bekerja dengan jaringan dan gambar? Apakah mungkin untuk melakukan 30 pemotretan normal per detik di tingkat yang dapat saya akses di Android? Apakah saya harus memetiknya sebelum mengirim untuk mengurangi bitrate? Lalu apakah JAVA cukup untuk pengemasan dan pembongkaran? Dan jika, tiba-tiba, sesuatu berhasil, apakah mungkin untuk melalui langkah berikutnya, untuk mengacaukan sistem visi komputer JAVA OpenCV di sini? Dirinya sendiri, tentu saja, selalu menarik untuk menonton video streaming dari lantai, tetapi kita tidak boleh melupakan tujuan tertinggi - robot robot dengan kecerdasan semut!

Tapi, sambil memiliki apa yang kita miliki, kita akan kembali ke troli saat ini. Program lama dari artikel sebelum terakhir untuk mikrokontroler AVR pada platform Arduino tidak banyak berubah, hanya saja pilihan percabangan telah ditambahkan - mengemudi secara otonom atau dikendalikan oleh operator. Data yang ditransmisikan oleh kereta (spinal cord) melalui WIFI adalah sama - jarak yang ditempuh. Beberapa saat kemudian, saya juga memasang transfer suhu dari elemen kritis - driver motor. Semua ini dikirim dan diterima pertama kali oleh UART, dan sudah ketika memasuki jaringan melalui UDP. Saya tidak memberikan kode, semua analisis lengkap dalam artikel sebelum yang terakhir .



Untuk dua motor itu (pengemudi) masih cukup kurang lebih, tetapi dengan empat setelah beberapa saat itu terlalu panas ke keadaan tidak beroperasi. Pada awalnya saya mencoba menggunakan sensor suhu analog paling sederhana berdasarkan dioda zener, seperti LM335, tetapi tidak ada yang datang darinya. Singkatnya, saya tidak memiliki sumber tegangan referensi, ION.Dan menangkap millivolt dengan baterai rendah tidak masuk akal. Ngomong-ngomong, tentang baterai - ketika saya lelah terus-menerus melepas dan memasukkan kembali baterai lithium 14500 untuk mengisi ulang, saya hanya mengambil baterai cadangan dari obeng dan gerobak mulai mengemudi terus menerus selama satu setengah jam, ditambah lagi memiliki tampilan dan berat yang mengancam (ya, itu ada pada baterai ini) "Mata"). Oleh karena itu, untuk mengukur suhu, saya memasang giroskop-akselerometer yang salah berdasarkan L3G4200D. Untungnya, dia juga mengukur suhu dan mengirimkannya melalui bus I2C.

Ponsel cerdas yang duduk di belakang baterai dan di atas mata gema-sonar mentransmisikan aliran video, pembacaan akselerometer (Anda dapat menggunakannya untuk memiringkan gerobak dan lemparan gerobak ketika sedang berdiri), menggulung dan melempar dalam derajat dalam dinamika yang sudah dihitung oleh smartphone itu sendiri (pilihan yang sangat nyaman) , percepatan linier dalam arah perjalanan, penerangan. Secara umum, tentu saja, Anda dapat mentransfer segala sesuatu yang dapat diukur ponsel cerdas Anda dari tekanan udara ke arah ke utara.

Akibatnya, aplikasi di JAVA memperoleh bentuk berikut:



Yang lucu adalah bahwa itu benar-benar terlihat seperti panel kontrol penjelajah bulan Soviet, hanya saja saya memiliki layar TV di samping, dan mereka memilikinya di tengah.



Saat Anda menyalakannya, sambungkan terlebih dahulu ke keranjang dan smartphone di atasnya, pilih mode kontrol manual atau otonomi penuh - dan pergi! Sampai jendela suhu pengemudi berubah menjadi kuning dan kemudian berubah menjadi merah. Grafik!

Sesuatu seperti ini terlihat hidup.


Saya tidak membawa program ke sini, karena seperti biasa, membangun windows membutuhkan 95% dari kode. Itu dapat ditemukan di sini.

Di masa depan, otak (smartphone) harus belajar mentransfer video normal (dan bukan seperti sekarang) ke komputer, di mana ia harus dikenali dengan cara yang baik (untuk saat ini, tujuannya adalah membangun peta ruang di lantai dan menentukan lokasinya). Yah, dan sudah di bawah komunisme, saya ingin port kembali ke smartphone saya sehingga dia tidak membutuhkan komputer.

Jika setidaknya ada sesuatu yang berhasil, saya pasti akan mempostingnya. Terima kasih atas perhatian anda

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


All Articles