在ARDUINO平台上进化或为机器人奠定基础,我们通过智能手机将传感器和视频驱动到计算机

对于GeekTimes的亲爱的读者,下一篇(第四篇) 期待已久的文章介绍了如果再次混合使用arduino,ESP8266和WI-FI,然后将其与Android智能手机一起调味,然后将其撒在JAVA应用程序之上会发生什么。

我们将在上一篇文章中讨论机器人,这是时候至少要变得更聪明了。

图片

谁在乎,欢迎来猫。

如果您对阅读旧文章不是很感兴趣,那么请简要介绍一下-关键是要在Arduino平台上控制普通的四轮手推车,我在其上基于著名的ESP8266模块开发了一个无线UART桥。 为了方便起见(通常这是主要目标),使用相同的ESP,我为arduinki写了一个程序员,使您可以自己远程刷新它。

图片

就是说,购物车在某个地方(但在您的WI-FI网络中)行驶(是的,我喜欢写这个词),发送数据并接收命令,并且如果需要的话,还可以按顺序更改AVR微控制器中的程序。 因此,JAVA上用于PC的程序已运行,您可以在运行该程序的同时享受控制权并以行进距离的形式获得原始遥测(簧片开关和车轮上的磁铁)。

图片

此外,我在下一篇文章中成功地进行了实验,使用智能手机控制购物车-按钮,倾斜甚至声音。 但是当手推车离开去下一房间时,甚至声音也无法将它退回(与猫不同)。 她去了那里,敲了敲墙壁和家具,被电线缠住了,但是除了行进距离的信息外,她什么也没发。

因此,立即产生了为未来的终结者提供感觉器官的想法。 最简单的选择之一就是使用声纳。

图片

令人羞耻的工作算法很简单,我们从一个方面启动传感器,同时又有一些微控制器计数器。 HC-SR04开始将超声波发射到远方。 来自传感器的通过另一根导线发出的响应信号会向距离测量的结束发出信号,并且开始和响应之间的时间间隔与所测量的距离成比例。 因此,此刻我们放慢计数器的速度,看看其中有多少。

获得的精度高达大约1厘米,范围为2米。 他不喜欢绒毛和羊毛表面(例如猫),在这些表面上任何回声都会被淹死。

HC-SR04 的受影响区域的视角很小,因此为了知道在至少90度的角度范围内正在发生什么,希望执行以下操作:

  1. 将传感器拧到伺服机上,并朝不同方向看
  2. 放一些传感器。

首先,我实现了第一种选择,将声纳安装在便宜的SG90伺服上,然后推车变成了流动站。 至少需要花费大量时间进行至少三项测量,因此基本上,小车在带伺服的情况下旋转,然后小心地向前移动,但距离不远(突然在侧面出现障碍物),并再次感觉到其前方的空间。 声音仍然不是您的光。

因此,事不宜迟,我立即将三个声纳系统安装在上面。 推车呈现出震颤的蜘蛛外观,在障碍物前停滞不前,并开始在旅途中滑行。 但是最后的大脑只足以不被困在一个友好的环境中。 我们必须继续前进-自治和进步。 在这里,如果没有不同的感觉,即使是线虫也会告诉你,你做不到。


此外,通常发烧友会开始雕刻各种新颖的传感器,例如陀螺仪,加速度计,磁力计,甚至是FIRE传感器(无名的中国人为Arduino生产了数百万个)。 我也几乎是从这条湿滑的路开始的,但及时改变了主意。 因为这个原因,我做到了。 从最远的角度看,该机器人应该以照相机的形式获得视觉,并理解其所见。 但是,Arduino开发板中的AVR微控制器会在接收视频的阶段对您说再见,更不用说对其进行处理了。 突然,我的目光落在了已经被生命折磨的老年GALAXY S7智能手机上。

这样的计算能力,八个核心,4 GB的内存,两个摄像头,可访问网络,还需要什么才能将猴子变成一个人?

但是,我们只需要一个小巧的设计,就可以将我们的智能手机放在手推车上,从而可以轻松地将其放置和卸下。

图片

然后,我爬到Android开发人员网站上,了解普通智能手机还能为我们提供哪些其他功能。 事实证明,这并不小。 理论上你可以

访问以下传感器。
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


正如他们所说,只有什么! 确实,GALAXY S7并没有太多特定功能。 例如,湿度传感器。 和环境温度(尽管我知道它位于外壳内部,它将显示智能手机本身的温度)。 但是存在压力和光传感器。 更不用说陀螺仪,加速度计,使用它们,您可以轻松确定自己在太空中的位置。

结果,决定已经成熟,让智能手机可以接收和处理所有顶级信息-视频和所有这些各种传感器。 可以说,Arduino平台将对潜意识负责-对所有已经工作且不需要返工的事物,所有这些电机,声纳,簧片开关等。

由于即使使用UDB,也很难直接在智能手机上调试程序,所以我决定将整个程序转移到普通的个人计算机上并在其中进行处理。 然后以某种方式,当有一个可行的版本时,我们将把大脑重新带回购物车。 我们必须从小处着手,从疯狂的购物车中观看视频的传输确实很有趣。

来自传感器的数据可以通过原始客户端服务器以简单的方式发送,这完全没有问题。 但是随着视频的传输,立即出现了困难。 通常,我需要从智能手机相机到计算机上应用程序窗口的实时流广播。 这是现在。 将来,不仅我自己,而且某种模式识别系统也可以盯着窗口中的这张照片。 例如JAVA OpenCV。 甚至是来自云端的神经网络:D. 我不知道,这个阶段还很遥远。 但是我想用机器人卡车的“眼睛”看世界。

每个人都知道Google商店中的许多应用程序,例如“移动摄像头”,您可以通过在计算机上打开具有所需IP的浏览器来捕获智能手机摄像头的视频流。 因此,起初我以为自己可以通过GALAXY实施广播很容易(这是一个很好的错误),因此鉴于我只能以某种方式在JAVA上进行编写,因此您首先需要检查计算机上的广播接收情况。

事实证明,使用JAVA视频播放时,说得不太好。 早在1997年,就发布了所谓的Java Media Framework(一个Java库),该库有助于开发与JAVA本身的创作者一起使用的音频和视频的程序。 但是,在2003年以后的某个地方,上面放了一个大螺栓,从那以后已经有15年了。 经过一些实验,我设法在窗口中运行了一个文件,我不记得已经有哪个文件了(似乎是AVI),但是这种景象看起来很惨。 具有其他扩展名的文件根本不希望运行,在极端情况下,只有一个音轨。

在Internet上,我发现了另外两个用于处理视频的替代项目:Xuggler和apricaVLCj。 第一个项目的功能吸引人,但很久以前就死了,但是第二个项目的构想却非常生动有趣。 伙计们把JAVA固定在著名的VLC媒体播放器上。 也就是说,aprica不使用自写编解码器,而是使用现成的编解码器。 有了它,您将丢失任何文件。 一个明智的决定,但是最主要的是,您已经在计算机上安装了此VLC播放器。 好吧,谁没有呢? 但是,唯一需要注意的是,播放器和JAVA的位深度相同。 例如,后来我惊讶地发现,与64位JAVA相比,我的计算机上仍然有32位VLC。 而白白浪费了半天的生命。

Caprica开发人员向用户承诺在其网站上会做很多事情。 并且所有文件格式都将在JAVA应用程序中的多个窗口中启动,从You-Tube播放视频,捕获“实时”视频流,等等。 但是严酷的现实把一切都摆在了原地。 不,他们没有欺骗文件-一切都播放了。 但是现在,来自YouTube的视频不再需要。 起初我不明白为什么,但是后来我在日志中看到一个题词,以某种方式无法运行某个lua脚本,并立即回忆起:
YouTube网页的这种“屏幕抓取”功能很脆弱-如果YouTube更改了网页的结构,则VLC有时会找不到流URL,这时您必须等待开发者提供新的LUA脚本并等待VLC的新版本发布。
简而言之,YouTube显然已经改变了其网页结构,我需要等待新版本的发布。 另一方面,我需要进行“实时”视频广播,而不要播放站点中的文件。 也就是说,即使lua脚本起作用了,对我也没有太大帮助。

但是,尽管上面写着:

网络流服务器(例如,网络广播电台或视频点播服务器);
网络流客户端
也许是愿望清单,或者是商业版本,这很难说。

但是,我重复播放文件,没有任何抱怨。 例如,您可以创建,这里就是这样的in亵


包装本身的安装并不困难,甚至例如在这里详细描述。 没错,我以某种方式笨拙地指定了环境变量,现在我的VLC第一次以十秒的延迟启动,但是随后它保存了缓存中所需的内容,随后在当前会话中它没有暂停就开始了。

然后,在您的JAVA项目中,您将规定必要的依赖关系,然后就可以开始在JAVA窗口中铆接媒体播放器了

这样,在个人计算机的侧面标出了大概的工作计划后,我回到了视频数据源,即我的ANDROID智能手机。

但是在这里,我期待着另一个相当大的失望。 扫描了很多站点,甚至扫描了在线ANDROID手册后,我发现不可能使用常规方式实时组织流式传输。 与Caprica一样,您只能读取已录制的文件。 也就是说,打开相机后,需要停止时MEDIA RECORDER才开始记录。 而且只有停止后我们才能访问数据(及其传输)。

我在一篇古老的文章中证实了我的结论。 确实有迹象表明,一旦有人设法欺骗Android使其认为它正在写入文件,但实际上他们漏掉了缓冲区。 但是无论如何,正如前面已经解释过的那样,我无法在JAVA应用程序的PC端捕获这样的视频流。

因此,做出了一个简单的, 临时的 (我想强调)的决定-将来自摄像机的视频流分段发送两秒钟。 当然,这不是流动站,但是流动站已经相当不错了。

决定了这个决定之后,我开始精通CAMERA和MEDIA RECORDER课程。 网络上有很多用于启动相机和录制视频文件的代码示例,但是由于某些原因,它们都不适合我。 既不在模拟器上,也不在真实设备上。 事实证明,原因在于权限,即权限。 事实证明,代码示例是在Android仍免费的那些日子编写的,并且程序员如果在清单中写了所有必要的权限,便可以做任何他想做的事情。 但是我当前的OC版本不允许这样做。 首先,您必须授予用户写入文件的权限,并在启动应用程序后立即打开相机。 这花费了我额外的活动,即“活动”。

类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); } } 


之后,一切顺利,出现了以下工作代码。 Camera_Activity类的第二个活动负责使用相机并记录视频文件。 用于转发的Http_server类(名称当然不正确,但历史证明是正确的)。 只要有解释,代码就很简单。



一切都完全放在Github上。 友情链接

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); } } } 



启动并打开相机后,程序的本质如下:
将视频录制两秒钟到第一个文件,
我们将视频写入第二个文件两秒钟,与此同时,我们通过本地WI-FI通过TCP-IP将第一个文件发送到计算机,
再次写入第一个文件,同时发送第二个文件,
等等。

然后重复循环,直到按下“停止”按钮或智能手机的电池耗尽。 原则上,可以使用来自计算机的命令(也可以通过TCP)来实现按钮的模拟,这并不困难。

首先,以防万一,视频缓冲区由三个3GP格式的文件组成(我们写第一个文件,发送第三个文件,写第二个文件,发送第一个文件,写第三个文件,发送第二个文件),但是后来证明这两个文件已经足够了(记录并互相发送)请勿干涉)。

使用640 x 480的摄像机分辨率,可以获得大约200-300 kB的文件,这对于我的路由器而言相当困难。 我还没有打扰声音,但是那里的一切似乎都很简单:您安装了必要的音频编码器,比特率,通道数等。

稍后,当我调试视频传输时,我还通过从智能手机的传感器传输信息来补充代码。 一切都在同一行中传输,但我无法通过与视频相同的插座传输。 显然,用于传输PrintWriter字符串和用于传输二进制数据BufferedOutputStream的类使用不同的流,但是它们具有一个输出缓冲区,它们在彼此之间成功进行了废话。 结果,视频开始出现故障和崩溃。 此外,视频文件每两秒钟发送一次,对于传感器而言,此间隔太大。 因此,决定将它们分布在不同的插槽中,以免彼此干扰。 因此,出现了新的类Http_server_Sensors。

因此,我们组织了调度,现在我们将再次回到黑暗的接收方。

正如我们从第一个示例中已经看到的那样,使用VLC播放器在JAVA应用程序中播放视频文件现在没有问题。 最主要的是获取这些文件。

以下演示程序对此负责。

影片播放器
 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) { } } } 


其本质很简单。 启动TCP客户端,该客户端开始等待服务器在智能手机上准备就绪。 收到第一个文件后,它将立即开始播放,并且预计将并行播放第二个文件。 此外,可以预期第二个文件的接收结束或第一个文件的播放结束。 最重要的是,文件下载速度比播放速度快三颗星 。 如果您丢失了第一个文件,但还没有收到第二个文件,那么一切都在等待中……我们将演示黑屏。 如果没有,请快速开始播放第二个文件,并同时再次下载第一个文件。
我含糊地希望切换播放文件之间的间隔时间少于人眼的反应时间,但是并没有实现。 悠闲地,当然,这个VLC。



结果,我们得到了一种不连续的视频(显然,第一个三叶虫看到了这样的世界),清晰度的调整一直在发生。 而且我们必须考虑到视频也迟到了两秒钟。 简而言之,在生产中,我不建议您进行传播。 但正如他们所说,我缺少鱼和三叶虫...

综上所述,我们可以绝对地说:

分批发送视频实际上是不起作用的,仅在视频足够长且您不需要对发生的事情做出第二反应的情况下才有用。

通过TCP-IP进行视频传输也是一个不正确的想法,无论Habr上的某些人如何使用该协议(据说它甚至比UDP都快)说出数据传输速度。 当然,现代的无线Intranet具有良好的特性,可以确保TCP服务器和客户端的连续握手,并且TCP本身似乎已针对长数据进行了升级,但是在演示过程中仍会定期播放视频焦点之间的插头。

但是,至少在将来,出现了以下想法:

  1. 通过UDP发送逐帧(不是视频,而是照片),但通过TCP发送控制信息,
  2. 通过UDP驱动相框,但在同一通道中具有同步信号。

当然,到目前为止,问题多于答案。 JAVA在处理网络和图像时是否具有足够的抽象抽象级别的接收速度? 是否可以在Android中以我可以访问的水平每秒进行30张正常拍摄? 为了降低比特率,我是否必须在发送之前先收割它们? JAVA是否足以包装和拆箱? 而且,如果突然出现了问题,是否可以继续下一步,将JAVA OpenCV计算机视觉系统固定在这里? 当然,从地板上观看流视频本身总是很有趣,但我们不要忘记最高的目标-具有蚂蚁智能的机器人!

但是,在拥有所拥有的东西的同时,我们将返回当前的购物车。 前一篇针对Arduino平台上的AVR微控制器的旧程序变化不大,仅增加了分支选择-自动驾驶或由操作员控制。 推车(脊髓)通过WIFI传输的数据是相同的-行驶的距离。 再过一会儿,我还附加了一个关键要素-电动机驱动器的温度传递。 所有这些都首先通过UART发送和接收,当通过UDP进入网络时已经发送和接收。 在上一篇文章中 ,我没有提供代码和所有完整的分析



对于两台电动机,它(驱动器)或多或少仍然足够,但过一会又有四台电动机过热到不工作状态。 最初,我尝试使用基于齐纳二极管的最简单的模拟温度传感器,例如LM335,但没有任何结果。 简而言之,我没有基准电压源ION。在低电量的电池上捕获毫伏是没有意义的。顺便说一下,关于电池-当我厌倦了不断取出和重新插入14500锂电池以进行充电时,我只是从螺丝起子中取出了一块备用电池,手推车开始连续行驶了一个半小时,而且它的外观和重量都令人生畏(是的,它在电池上“眼睛”)。因此,为了测量温度,我安装了基于L3G4200D的故障陀螺仪加速度计。幸运的是,他还测量了温度并通过I2C总线进行了传输。

坐在电池后面并在回波声纳眼上方的智能手机传输视频流,加速度计读数(您可以使用它来倾斜购物车和站立时的购物车倾斜度),以智能手机本身已经计算出的动态度(以度为单位)滚动和倾斜(非常方便的选择) ,在行进,照明方向上的线性加速度。通常,当然,您可以将智能手机可以测量的所有内容从气压转移到向北的方向。

结果,JAVA上的应用程序获得了以下形式:



有趣的是,它看起来真的像是苏联月球车的控制面板,只有侧面有电视屏幕,中间是电视屏幕。



当您打开它时,首先连接到购物车和智能手机,选择手动控制模式或完全自治-然后开始!直到驾驶员的温度窗口变为黄色,然后变为红色。图形!

像这样的东西看起来很生动。


我没有将程序带到这里,因为像往常一样,构建窗口占用了95%的代码。可以在这里 找到

将来,大脑(智能手机)应该学会将正常的(而不是现在的)视频传输到计算机上,并应该以一种良好的方式对其进行识别(目标是在楼层上建立房间地图并确定其位置)。好吧,由于已经处于共产主义时代,我想将其移植回我的智能手机,这样他就不需要电脑了。

如果至少有什么解决办法,我一定会发布。谢谢您的关注。

Source: https://habr.com/ru/post/zh-CN448516/


All Articles