65.9K
CodeProject 正在变化。 阅读更多。
Home

Simulink Matlab 中的 Android 传感器监控

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2020年11月2日

CPOL

7分钟阅读

viewsIcon

4972

downloadIcon

87

在 Simulink (Matlab) 中实现 Android 传感器监控。

引言

通常,Android设备内置传感器的测量数据随后将用于一组高级数学算法,例如校准、噪声消除和估计。这些高级数学算法不能简单地在Android设备上实现,为此需要高级软件。在Simulink Matlab中,可以轻松开发上述算法,其中一些已经开发完成。因此,本文旨在通过Simulink对Android传感器进行传感器监控,以达到上述目标。

为实现本文的主要目标,开发了一个Android应用程序和Simulink文件。该应用程序通过ListViewArrayAdapter对显示Android设备的所有内置传感器。通过单击传感器,可以从显示的传感器列表中选择所需的传感器。然后,通过注册一个eventlistener来测量传感器的相应信号,该eventlistener在文章中有详细描述。

测量数据通过套接字编程技术发送到监控设备。为此,应用程序和Simulink都应实现套接字,其中前者被视为服务器套接字,后者被视为客户端套接字。最后,在Simulink中使用接收到的数据,例如实现数学算法。

在此项目中,定义了一个Java类来管理所有部分,该类为AppManagerappManager对象(AppManager类的实例)控制和管理涉及服务器和传感器部分的项目的全部内容。

本文详细介绍了所需细节,并最终开发了一个易于在下一个项目中使用的应用程序和Simulink文件。事实上,本文可以轻松用于高级传感器监控项目,例如位置导航和定时。

背景

为了提及本文各部分内容,需要介绍以下背景。如前一节所述,利用ListViewArrayAdapter对来显示内置Android设备并选择所需的设备。要完全理解此部分,特此提供以下链接,该链接之前已提供:

上述链接完整地解释了第一部分及其相关的实现细节。

本文的第二部分涉及套接字编程技术。为此,在Android设备和监控设备中开发了两个TCP套接字,其中第一个是服务器,第二个是客户端。这意味着在Android设备中实现的套接字被视为服务器套接字,另一个是客户端服务器。为该概念提供了一些有用的教程,可以通过简单搜索找到,例如以下教程:

应注意,下一节将提供实现所述TCP套接字所需的详细技术信息。

Simulink中的传感器监控

本节介绍了本文的主要部分,包括四个子部分,即ListView实现、Android中的传感器注册、Android中的服务器套接字实现以及Simulink中的客户端套接字实现。这些子部分将在以下章节中介绍。

ListView实现

本小节设计了一个合适的列表视图,用于显示Android设备的内置传感器。应注意,本部分的所有详细信息均在上述链接中提供,可以查看以获得更好的理解。

首先,每个项目(传感器)的XML布局按以下代码块所示进行开发:

<linearlayout 
    android:background="#FFFFFF" 
    android:id="@+id/ll" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent" 
    android:orientation="vertical" 
    android:paddingbottom="3dp" 
    android:paddingleft="3dp" 
    android:paddingright="3dp" 
    android:paddingtop="3dp" 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <textview 
        android:id="@+id/tvn" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:text="" 
        android:textcolor="#000000" 
        android:textsize="20dp"/>
    <textview 
        android:id="@+id/tvv" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:text="" 
        android:textcolor="#000000" 
        android:textsize="20dp"/>
    <textview 
        android:id="@+id/tvt" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:text="" 
        android:textcolor="#000000" 
        android:textsize="20dp"/>
</linearlayout>

正如可以轻松理解的那样,每个项目(传感器)仅通过显示其名称、类型和制造商来显示。相应的ArrayAdapter通过以下代码实现,该代码在提到的链接中已完全描述:

public class MyAdapter extends ArrayAdapter<sensor>{
    public List<sensor> sensors;
    public int selectedItem=-1;
    public AppManager appManager;
    public MyAdapter(Context context, int resource, 
                     List<sensor> sensors,AppManager appManager) {
        super(context, resource,sensors);
        this.sensors=sensors;
        this.appManager=appManager;
    }

    @Override
    public View getView(int position,View convertView,ViewGroup parent){
        View rowView=convertView;
        Sensor sensor=sensors.get(position);
        if(convertView==null){
            rowView=LayoutInflater.from(getContext()).inflate(R.layout.list_item,parent,false);
            TextView tvn=(TextView)rowView.findViewById(R.id.tvn);
            tvn.setText(sensor.getName());
            TextView tvv=(TextView)rowView.findViewById(R.id.tvv);
            tvv.setText(sensor.getVendor());
            TextView tvt=(TextView)rowView.findViewById(R.id.tvt);
            tvt.setText(sensor.getStringType());
            ViewHolder viewHolder=new ViewHolder(tvn,tvv,tvt,Color.WHITE,position);
            rowView.setTag(viewHolder);
            rowView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ViewHolder viewHolder1=(ViewHolder) v.getTag();
                    selectedItem=viewHolder1.position;
                    int color=viewHolder1.color;
                    if(color==Color.WHITE) {
                        appManager.itemSelected(selectedItem);
                        v.setBackgroundColor(Color.GRAY);
                        viewHolder1.color=Color.GRAY;
                        v.setTag(viewHolder1);
                    }else {
                        appManager.itemDeselected();
                        selectedItem=-1;
                        v.setBackgroundColor(Color.WHITE);
                        viewHolder1.color=Color.WHITE;
                        v.setTag(viewHolder1);
                    }
                }
            });
        }else{
            ViewHolder viewHolder=(ViewHolder)rowView.getTag();
            TextView tvn=viewHolder.tvn;
            TextView tvv=viewHolder.tvv;
            TextView tvt=viewHolder.tvt;
            int color=viewHolder.color;
            tvn.setText(sensor.getName());
            tvt.setText(sensor.getStringType());
            tvv.setText(sensor.getVendor());
            viewHolder.position=position;
            rowView.setTag(viewHolder);
            rowView.setBackgroundColor(color);
        }
        return rowView;
    }
}

在上述代码中,ListView设计为通过单击所需的传感器与视图模型进行交互。单击屏幕上的项目(传感器)时,其颜色将从白色变为灰色,以突出显示项目(传感器)已被选中。此外,通过单击选定的项目,传感器将被取消选中,其背景颜色将从灰色恢复为白色。

设计的ListView插入到主布局中,如下所示:

<relativelayout 
    android:layout_height="match_parent" 
    android:layout_width="match_parent" 
    android:paddingbottom="@dimen/activity_vertical_margin" 
    android:paddingleft="@dimen/activity_horizontal_margin" 
    android:paddingright="@dimen/activity_horizontal_margin" 
    android:paddingtop="@dimen/activity_vertical_margin" 
    tools:context="com.example.sensormonitoring.MainActivity" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools">

    <listview 
        android:id="@+id/lv" 
        android:layout_height="match_parent" 
        android:layout_width="match_parent"/>
</relativelayout>

现在ListView显示在下一张图片中:

可以看出,所有传感器都显示在列表中。

指向特定传感器的每个项目都可以通过单击其项目来选择。选定项目(传感器)的背景颜色会更改,如下一张图片所示:

选定的项目将是本文剩余部分中所需的传感器。

传感器注册

本小节描述了如何在Android中注册选定的传感器。为此,提供了以下Java类:

public class SensorServant {
    public SensorManager sensorManager;
    public Sensor activeSensor;
    public List<Sensor> sensors;
    public AppManager appManager;
    public SensorEventListener sensorEventListener=new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            appManager.setData(event.values);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };
    public SensorServant(SensorManager sensorManager, 
                         List<Sensor> sensors,AppManager appManager){
        this.sensorManager=sensorManager;
        this.sensors=sensors;
        this.appManager=appManager;
    }
    public void setSensor(int index) {
        activeSensor=sensors.get(index);
        sensorManager.registerListener(sensorEventListener,
                      activeSensor,SensorManager.SENSOR_DELAY_NORMAL);
    }

    public void deleteSensor(){
        sensorManager.unregisterListener(sensorEventListener);
    }
}

在上述类中,定义了一些属性和方法,它们将在以下内容中进行描述:

  • 属性sensorManager:它是Android框架之一,与传感器概念相关。
  • 属性activeSensor:这是一个Sensor实例,指向所需的传感器。如果没有选择传感器,此属性将为null
  • 属性sensors:它包含Android设备的所有内置传感器。
  • 属性appManager:在第一部分介绍的管理器对象。
  • 方法setSensor():当在列表视图中选择某个项目时,appManager会调用此传感器。通过此方法,将所需的传感器注册到在此类中实现的sensorEvnetListener
  • 方法deleteSnsor():当在列表视图中取消选中活动项目时,appManager会调用此传感器。在这种情况下,将注销sensorEvnetListener

此类会注册所需的传感器,测量其信号,并通过评估其setData方法将其发送给appManager

服务器套接字实现

本小节介绍了如何在Android应用程序中开发服务器套接字。为了实现这一目标,需要提供一些解释性说明。

首先,通过实现以下Java类,服务器可以支持多个客户端:

public class Server  extends Thread {
    public Socket client;
    public ServerSocket serverSocket;
    public AppManager appManager;
    public Server(AppManager appManager) {
        this.appManager=appManager;
        try {
            serverSocket = new ServerSocket(appManager.port);
        }catch(Exception ex){
            Toast.makeText(appManager.context,ex.getMessage(),Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void run() {
        try {
            while(1==1) {
                client = serverSocket.accept();
                ServerServant serverServant = new ServerServant(this, client, appManager);
                serverServant.start();
            }
        } catch (Exception e) {
            Toast.makeText(appManager.context,"hI",Toast.LENGTH_LONG).show();
        }
    }
}

Server类继承自Thread,能够支持多访问属性。该类在从appManager提供的端口上开发服务器套接字。服务器在由appManager生成的常规线程中等待传入的客户端。当客户端连接时,它会生成另一个线程(serverServant)来正确服务客户端。serverServant在此提供:

public class ServerServant extends Thread {
    public Server server;
    public Socket client;
    public PrintWriter writer;
    public BufferedReader reader;
    public AppManager appManager;
    public ServerServant(Server server,Socket client,AppManager appManager){
        this.server=server;
        this.client=client;
        this.appManager=appManager;
        try {
            writer=new PrintWriter(client.getOutputStream());
            reader=new BufferedReader(new InputStreamReader(client.getInputStream()));
        } catch (Exception e) {

        }
    }

    @Override
    public void run(){
        try {
            String str = reader.readLine();
            while(str.contains("ready")) {
                float[] data = appManager.getData();
                String str1="";
                for (int i = 0; i < data.length; i++) {
                    if(i<data.length-1) {
                        str1 += data[i] + ",";
                    }else{
                        str1+=data[i] + "";
                    }
                }
                int l=str1.length();
                String str2=""+l;
                while(str2.length()<2){
                    str2="0"+str2;
                }
                writer.print(str2);
                writer.flush();
                str=reader.readLine();
                if(!str.contains("ok")){
                    break;
                }
                writer.print(str1);
                writer.flush();
                str=reader.readLine();
            }
            reader.close();
            writer.close();
        }catch(Exception ex){

        }
    }
}

事实上,上述对象是为服务客户端而生成的另一个线程。显然,此对象负责将所需传感器的测量信号发送给客户端。为此服务设计了一个简单的协议,该协议可以在上述代码中轻松遵循。首先,客户端应发送“ready”以告知服务器其状态。然后,对象将测量数据准备成一个用逗号分隔的string。服务器首先发送string的长度,并等待接收“ok”以发送string。该协议继续进行,直到未正确接收到其中一个单词为止。

最后,AppManager在以下代码块中给出:

public class AppManager {
    private MyAdapter adapter;
    private ListView listView;
    private SensorManager sensorManager;
    private Server server;
    public SensorServant sensorServant;
    private List<Sensor> sensors;
    public int port=50000;
    private float[] data;
    private boolean sendingData=false;
    public Context context;
    public AppManager(Context context,ListView listView){
        sensorManager=(SensorManager)context.getSystemService(context.SENSOR_SERVICE);
        sensors=sensorManager.getSensorList(Sensor.TYPE_ALL);
        adapter=new MyAdapter(context.getApplicationContext(),0,sensors,this);
        this.listView=listView;
        this.context=context;
        listView.setAdapter(adapter);
        sensorServant=new SensorServant(sensorManager,sensors,this);
        server=new Server(this);
        server.start();
    }
    public void itemSelected(int index){
        sensorServant.setSensor(index);
        sendingData=true;
    }
    public void itemDeselected(){
        sensorServant.deleteSensor();
        sendingData=false;
    }
    public void setData(float[] data){
        this.data=data;
    }
    public float[] getData(){
        return data;
    }
}

应用程序管理器初始化其他对象并与它们进行适当的交互,如上述代码所示。

客户端套接字实现

很明显,客户端是监控设备。客户端套接字在Simulink中实现,该实现将在下一张图片中展示:

Simulink的主要部分是中心块,它是一个掩码块,具有以下解释的Matlab函数:

function ds=monitor(t,IP,port)
global tcp;
if t==0        
    tcp=tcpclient(IP,port);
    mysend(tcp,'ready');
    d1=0;d2=0;d3=0;
    ds=[d1;d2;d3];    
else
    len=read(tcp,2);    
    len=str2double(char(len));
    mysend(tcp,'ok');
    data=read(tcp,len);                 
    str=char(data);    
    ss=textscan(str,'%f,%f,%f');    
    ds=[ss{1} ss{2} ss{3}];
    mysend(tcp,'ready');
end
end

上述代码的步骤可以根据提到的协议轻松推导出来。

掩码块具有以下参数:

上述参数的定义是显而易见的。

接下来的图片分别显示了Simulink示波器中的加速度计的x轴、y轴和z轴信号。

Using the Code

代码可以在Simulink文件中使用。高级数学算法可以使用掩码块来实现其目标。

历史

  • 2020年11月2日:初始版本
© . All rights reserved.