Simulink Matlab 中的 Android 传感器监控





0/5 (0投票)
在 Simulink (Matlab) 中实现 Android 传感器监控。
引言
通常,Android设备内置传感器的测量数据随后将用于一组高级数学算法,例如校准、噪声消除和估计。这些高级数学算法不能简单地在Android设备上实现,为此需要高级软件。在Simulink Matlab中,可以轻松开发上述算法,其中一些已经开发完成。因此,本文旨在通过Simulink对Android传感器进行传感器监控,以达到上述目标。
为实现本文的主要目标,开发了一个Android应用程序和Simulink文件。该应用程序通过ListView
和ArrayAdapter
对显示Android设备的所有内置传感器。通过单击传感器,可以从显示的传感器列表中选择所需的传感器。然后,通过注册一个eventlistener
来测量传感器的相应信号,该eventlistener
在文章中有详细描述。
测量数据通过套接字编程技术发送到监控设备。为此,应用程序和Simulink都应实现套接字,其中前者被视为服务器套接字,后者被视为客户端套接字。最后,在Simulink中使用接收到的数据,例如实现数学算法。
在此项目中,定义了一个Java类来管理所有部分,该类为AppManager
。appManager
对象(AppManager
类的实例)控制和管理涉及服务器和传感器部分的项目的全部内容。
本文详细介绍了所需细节,并最终开发了一个易于在下一个项目中使用的应用程序和Simulink文件。事实上,本文可以轻松用于高级传感器监控项目,例如位置导航和定时。
背景
为了提及本文各部分内容,需要介绍以下背景。如前一节所述,利用ListView
和ArrayAdapter
对来显示内置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日:初始版本