一、软件说明
1.软件配置
选用Andriod studio,并且下载SDK(作用:对写出的程序进行手机端模拟运行),或者通过数据线与手机端进行连接,此时需要打开手机设置的开发者模式(打开方式请百度搜索)。
2.工程部分目录说明
文件夹说明:
在使用时选择Project,其中app>src存放项目代码,java>com.example.smad存放项目主函数mainactivity。这是项目运行的开始地方,每一次运行,都先启动此文件,按照生命周期(开始 运行 结束)来进行程序。
一个项目有多个activity 和 fragement,activity可以相互切换fragment则是嵌入到activity之中
layout问价夹下存放项目的activity和fragment的布局文件。
注(一个项目下可以有多个activity和fragment,但是只能有一个mainactivity文件,一个activity里面可以有多个fragment)。
values存放一些自定义的资源:颜色、通用的字符串等等。
二、新建项目
1.建立项目
在file下新建一个项目,选择empty activity 的项目,语言选择java,一路finish即可,第一次运行时耗时较长,需安装很多配件,请自行将SDK以及其他文件更换一个路径,文件太大不要放在C盘(运行时的模拟器接近10G)。
2.导入jar包
由于需要使用平台方的jar包,所以需要导入jar包。文件位置。复制jar包,在项目lib下粘贴,选中包,单击右键,选中单机add as libiary即可导入jar包。(pynect-nir-release-xxx.jar)
由于使用的有SVM模型,所以需要再次导入LibSvm的jar包。
3.添加动态库
将本文件夹下面的jniLibs全部复制到lib问价下下即可。文件位置
4.添加权限
4.1 蓝牙全选
注意不同安卓版本的手机有不同的蓝牙权限,由于andriod12更新了许多内容,故andriod12的andriod10就不太一样,请在App->src->main->AndroidManifest.xml 里面添加权限,以andriod12为例,其他版本自行查找。
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--Android12 的蓝牙权限 如果您的应用与已配对的蓝牙设备通信或者获取当前手机蓝牙是否打开-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!--Android12 的蓝牙权限 如果您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备)-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<!--Android12 的蓝牙权限 如果您的应用使当前设备可被其他蓝牙设备检测到-->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
tools:ignore="ProtectedPermissions" />
<!-- 添加位置权限申请!-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
4.2文件权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
5.添加服务
此服务则为软件与光谱仪绑定,项目运行成功需要本项目下可查找到相关动态库,若尚未添加动态库,软件运行时则会闪退,此xml代码同样添加到AndroidManifest.xml 中。
<service android:name="com.pynect.nirservice.bluetooth.EasyBLEService"
android:enabled="true"
tools:ignore="MissingClass" >
</service
6.设置第三方库指定路径
此代码需要放在src->build.gradle文件中,添加到buildTypes上方,此文件作用是提供动态库.so的位置给程序,尚未指定该路径程序闪退(可能程序还是找不到文件位置,可以根据情况做相关的更改)
// sourceSets用于指定依赖的第三方so库的存放地址
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
注意:
1.绑定服务时需要获取证书,如未获取,请联系厂商。
2.有些功能需要前置条件,如蓝牙获取与GPS定位权限,否则程序会闪退。
3.注意开发文档中的项目代码,如果需要运行,如果打不开,请重新部署项目,按照文件需求重写项目代码,不然有些文件会报错与找不到,还有就是所给地文件是一个activity,我们则需要进行多页面开发,此时就需要将acivity与fragment进行代码地转换,在oncreat的面的代码则需要将之复制到新建地fragment里面地onactivitycreate之中,但是这个方法被启用了,尽管如此也是可以运行的。
三、项目代码说明
(如果代码在andriod studio 里面运行时报错,请记住需要配置的地方,按照文件格式重新新建一个文件,原代码复制即可!!!)或者清除缓存试一下
1.动态申请权限
在andriod12里面获取蓝牙权限除了需要在xml里面添加权限申请,还需要动态申请,运行时在启动相关配置时调用即可,调用应在蓝牙使用或配置前申请权限。
protected void checkPermission(){//动态申请扫描权限,其中申请一个权限后,自动申请其余所需权限
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
request.add(Manifest.permission.BLUETOOTH_SCAN);
request.add(Manifest.permission.BLUETOOTH_ADVERTISE);
request.add(Manifest.permission.BLUETOOTH_CONNECT);
}
if(request.size() != 0){
ActivityCompat.requestPermissions(MainActivity.this,request.toArray(new String[0]),0);
}
}
2.fragment说明
package com.example.smad;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
public class Info extends Fragment {
//fragment的创建
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
//创建视口 在xml获取相关代码并回调
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.info, container, false);
}
}
与之配套的xml文件 需要注意名字一致。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="光谱信息区域"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
我们的软件主要分为四个部分,扫描,信息展示区域,数据处理区域,以及结果区域。这就要求我们有四组这样的代码,再将之关联到mainactivity之中,而mainactivity的关联代码如下。
3.maintivity说明
private List<Fragment> mFragments; //存放视图
private ViewPager viewPager;
private TabLayout mTabLayout;
private List<String> mtitle; //存放底部标题
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {//初始化相关页面并展示。
mTabLayout = findViewById(R.id.tablayout);
viewPager = findViewById(R.id.viewpager);
mFragments=new ArrayList<>();
//如果需要新加页面 , 只需要新建一个fragment 并且添加到list里面即可。
mFragments.add(new Msg());
mFragments.add(new PeoPle());
mFragments.add(new Find());
mFragments.add(new Me());
mtitle=new ArrayList<String>();
//设置每个fragment的标题
mtitle.add("扫描");
mtitle.add("光谱信息");
mtitle.add("数据处理");
mtitle.add("鉴别结果");
//实例化适配器
MyAdapt adapt = new MyAdapt(getSupportFragmentManager(), mFragments, mtitle);
viewPager.setAdapter(adapt);
mTabLayout.setupWithViewPager(viewPager);//给tab设置一个viewpager
//viewpager的监听
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override //选中
public void onPageSelected(int position) {
switch (position){
case 0:
Toast.makeText(MainActivity.this, "开始扫描", Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(MainActivity.this, "本次扫描的光谱信息", Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(MainActivity.this, "数据处理过程", Toast.LENGTH_SHORT).show();
break;
case 3:
Toast.makeText(MainActivity.this, "鉴别结果展示", Toast.LENGTH_SHORT).show();
break;
}
}
@Override // //state的状态有三个,0表示什么都没做,1正在滑动,2滑动完毕
public void onPageScrollStateChanged(int state) {
//其中可以对页面转换时进行操作 0 1 2
}
});
}
maintivity的xml文件
布局说明:弹性布局里面嵌入线性布局,线性布局里面存在一些相关组件,比如按钮,文本框之类的.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="zb">
</TextView>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/tablayout" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="50dp">
</com.google.android.material.tabs.TabLayout>
</RelativeLayout>
以下即为基本的页面布局了,我们可以通过手机的开发者模式或者andriod studio 的SDK模拟器来运行文件.具体如下:
首先点击1处出现模拟器,选择需要的一个模拟器(模拟器根据软件运行的环境选择),选中运行即可.
那么现在我们就可以为我们的主页面scanner_X添加上一些相关的组件的,来丰富我们的页面
具体的代码请见本目录下的程序.
//由于我们是在fragment里面对光谱仪进行操作,所以我们在fragment里面动态申请蓝牙权限.
protected void checkPermission(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
request.add(Manifest.permission.BLUETOOTH_SCAN);
request.add(Manifest.permission.BLUETOOTH_ADVERTISE);
request.add(Manifest.permission.BLUETOOTH_CONNECT);
}
if(request.size() != 0){
ActivityCompat.requestPermissions(getActivity(),request.toArray(new String[0]),0);
}
}
每一个新建的fragment里面都有onCreate方法,在里面申请即可.
@Override
public void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
checkPermission();
}
由于我们是在fragment里面获取组件,所以我们不能单纯的使用findviewbyid了,前面需要添加上 getActivity().
并且与组件相关的声明或者点击事件要在onActivityCreated里面.
btn1 = (Button) getActivity().findViewById(R.id.button1);
接下来以btn1为例子点击事件初始化软件,获取相关权限,其余请在称许内部查看
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bleInit();
}
});
@SuppressLint("MissingPermission")
private void bleInit() {
// 当前的系统版本 < Android 4.3 API=18,目前市面大部分系统都在6.0了...这个判断几乎可以不用写了。可省略
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ToastUtil.show(getActivity(), "当前设备系统版本不支持BLE蓝牙功能!请升级系统版本到4.3以上");
return;
}
//1. 当前设备是否支持BLE蓝牙设备
if (BleUtil.checkDeviceSupportBleBlueTooth(getActivity())) {
// 2.判断蓝牙设备是否打开了
if (checkBlueIsOpen()) {
// 3.断设备的api是否需要开启定位权限
checkGPS();
} else { // 没有打开,跳转到系统蓝牙页面
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
} else {
ToastUtil.show(getActivity(), "当前设备不支持BLE蓝牙功能!");
}
}
在对蓝牙进行操作时,我们要获取蓝牙适配器来操作蓝牙
searchHandler = new Handler(Looper.getMainLooper());
BluetoothManager bluetoothManager = (BluetoothManager) getActivity().getSystemService
(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
由于我们现在大部分手机都是andriod以上,所以我们在对安卓低版本以及蓝牙低版本的兼容则是不需要了.
最终软件的主页面即是现在这样.
现在通过这几个按钮即可实现初始化等相关功能,获取凭证即可进行下一步.
4.凭证获取
联系厂家进行获取凭证,在这部分进行替换
之后进行扫描设备,在控制台找到获取的光谱设备,找到地址,进行地址替换
四、其他
好了现在就可以进行设备连接了
总结一下流程
- 阅读手册
- 按照要求创建设备
- 对蓝牙、文件读写进行动态配置权限
- 将页面拆分成合适的界面,我们这里使用的是fragement*4
- 获取凭证
那么既然凭证获取到了,那么我们就可以启动设备了,首先的话,我们需要检查初始状态,也即是查看GPS以及蓝牙是否开启,不然是不可以进行下一步操作的,之后进行连接设备,获取参数,以及扫描样品。
在扫描后,也就是运行到这里
@Override
public void getFormattedSpectrum(double[] doubles, int[] ints, int i) {
// LogUtil.d("getFormattedSpectrum: " + Integer.toString(ints[0]));
LogUtil.d("已获取光谱数据" );
// excelPort(doubles,ints,i);
Log.d(TAG, "getFormattedSpectrum: 导出成功");
EventBus.getDefault().post(new MessageEvent(doubles , ints , i));
}
这里主要是获取到强度以及光谱横坐标,也就是ints,doubles,这里的i是长度,即228。我们这里获取后主要是传递到info的fragement,这里是通过eventbus组件进行传递的,改组件具体使用流程为EventBus
在传递后,info界面则是使用github上的开源组件进行绘制图像折线图
我们这里可以进行绘制强度,反射率,吸光度
计算公式为反射率=样品 / 参考数据 、吸光度 = -lg反射率
注意:参考样品即为白板数据
我们这里可以进行导出到excel,原代码已经注释
之后就可以进行参数预处理,我们选用的是平滑,处理完之后即可调用模型进行判别,在页面显示即可
SVM模型判别,首先进行训练,使用c=11.2332 、g=6.5994 、p= 0.0100
训练完后,将模型存放到项目,由于项目将被压缩,所以软件运行中,找不到res>model文件路径,我们解决办法是,将项目存放到assets路径下,在第一次运行时,将文件复制到手机中,之后就可以进行读取了,在预测时,主要参数有,检测数据,模型位置,结果位置,其中检测数据由预处理后进行导出为txt,另外两个已经处理好,运行即可。
上的开源组件进行绘制图像折线图
我们这里可以进行绘制强度,反射率,吸光度
计算公式为反射率=样品 / 参考数据 、吸光度 = -lg反射率
注意:参考样品即为白板数据
我们这里可以进行导出到excel,原代码已经注释
之后就可以进行参数预处理,我们选用的是平滑,处理完之后即可调用模型进行判别,在页面显示即可
SVM模型判别,首先进行训练,使用c=11.2332 、g=6.5994 、p= 0.0100
训练完后,将模型存放到项目,由于项目将被压缩,所以软件运行中,找不到res>model文件路径,我们解决办法是,将项目存放到assets路径下,在第一次运行时,将文件复制到手机中,之后就可以进行读取了,在预测时,主要参数有,检测数据,模型位置,结果位置,其中检测数据由预处理后进行导出为txt,另外两个已经处理好,运行即可。