步态分析
Android设备步态分析软件
1. 概述
手机上的运行效果图如下(实现思路1.编写界面和事件处理;2.收集加速度数据写入到文件中;3.使用libSVM训练Model;4.把model应用于真实的数据识别) 源码
2. 环境准备
- 操作系统:Windows、Mac OS、 Linux均可
- 安装JDK,Android开发工具需要有JDK环境才能运行,所有要先安装JDK(下载地址) 下载JDK8
- 安装Android Studio(Android开发工具,可以编写Android程序并在设备上运行)(下载地址) 下载Android Studio 3.0
- 安装下载好的软件
3. 创建项目
打开Android Stuido,点击Start a new Android Studio project,然后按照下图一步一步操作就可以完成Android项目的创建。





4. 创建虚拟机
按照下图操作创建Android虚拟设备并运行






5. 在虚拟机上运行项目
按照下图操作把项目在模拟器或真实设备上运行起来


6. 把项目分享到Github上








7. 布局代码
<!-- 布局代码 -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical"
tools:context="cn.zhaoliang5156.svmprjo.MainActivity">
<TextView
android:id="@+id/tv_train_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="采集样本数量"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/rg_train_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_train_num_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="30" />
<RadioButton
android:id="@+id/rb_train_num_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="50" />
<RadioButton
android:id="@+id/rb_train_num_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100" />
</RadioGroup>
<TextView
android:id="@+id/tv_lable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="标记动作"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/rg_lable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_lable_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="静止(0)" />
<RadioButton
android:id="@+id/rb_lable_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="走路(1)" />
<RadioButton
android:id="@+id/rb_lable_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跑步(2)" />
</RadioGroup>
<TextView
android:id="@+id/tv_acc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_collection_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="已经采集:" />
<TextView
android:id="@+id/tv_accuracy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="准确率" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<ImageButton
android:id="@+id/btn_collection"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@mipmap/sample_off" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="采集" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<ImageButton
android:id="@+id/btn_train"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@mipmap/train_off" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="训练" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<ImageButton
android:id="@+id/btn_understand"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@mipmap/test_off" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="识别" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:id="@+id/iv_still"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/gait_still_off" />
<ImageView
android:id="@+id/iv_walk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/gait_walk_off" />
<ImageView
android:id="@+id/iv_run"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/gait_run_off" />
</LinearLayout>
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="清空目录"
android:textSize="20sp" />
</LinearLayout>
8. 逻辑代码
package cn.zhaoliang5156.svmprjo
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import cn.zhaoliang5156.svmprjo.svm.svm_predict
import cn.zhaoliang5156.svmprjo.svm.svm_scale
import cn.zhaoliang5156.svmprjo.svm.svm_train
import cn.zhaoliang5156.svmprjo.util.Util
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import java.io.*
/**
* 步态分析
*/
class MainActivity : AppCompatActivity() {
lateinit var mSensorManager: SensorManager // 传感器管理类
lateinit var mAccSensor: Sensor // 传感器
var mHz = (1000.0 * 1000.0 / 32).toInt() // 拿数据的时间
var lable = 0 // 要写入文件的lable标记
var trainNum = 0 // 采集样本数量
var currentCollectionTrainNum = 0 // 当前采集样本数据
var isStartCollection = false // 是否开始采集的标记
var isStartUnderStand: Boolean = false
// 采集的加速度监听器类
val sensorListener: SensorEventListener = object : SensorEventListener {
var accArr = DoubleArray(128)
var currentIndex = 0
override fun onSensorChanged(event: SensorEvent) {
var x = event.values[0]
var y = event.values[1]
var z = event.values[2]
val sqrt = Math.sqrt((x * x + y * y + z * z).toDouble())
tv_acc.text = "${sqrt}"
// 当不够128个数据的时候继续收集数据,够128个数据的时候写入文件
if (currentIndex >= 128) {
val features = Util.dataToFeatures(accArr, mHz)
if (isStartCollection) { // 如果是开始收集数据走到这里,就存起来
Util.writeToFile("${filesDir}/train", lable, features)
currentCollectionTrainNum++
tv_collection_num.text = currentCollectionTrainNum.toString()
if (currentCollectionTrainNum >= trainNum) { // 已经采集够了
collection(isStartCollection)
currentCollectionTrainNum = 0
tv_collection_num.text = "已经采集:${currentCollectionTrainNum}"
}
} else { // 否则则是识别
val code = Util.predictUnScaleData(features)
result(code.toInt())
}
currentIndex = 0
} else {
accArr[currentIndex++] = sqrt
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 选择采集样本数量
rg_train_num.setOnCheckedChangeListener { radioGroup, i ->
when (i) {
R.id.rb_train_num_one -> trainNum = 30
R.id.rb_train_num_two -> trainNum = 50
R.id.rb_train_num_three -> trainNum = 100
}
tv_train_num.text = "采集样本数量${trainNum}"
}
rg_train_num.check(R.id.rb_train_num_one) // 设置默认选中30
// 选中标记lable
rg_lable.setOnCheckedChangeListener { radioGroup, i ->
when (i) {
R.id.rb_lable_one -> lable = 0
R.id.rb_lable_two -> lable = 1
R.id.rb_lable_three -> lable = 2
}
tv_lable.text = "lable:${lable}"
}
rg_lable.check(R.id.rb_lable_one)
mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager // 获取传感器管理类
mAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) // 获取加速度传感器
btn_collection.setOnClickListener {
collection(isStartCollection)
}
// 开始训练按钮点击调用
btn_train.setOnClickListener {
btn_train.setBackgroundResource(R.mipmap.train)
doAsync {
createScaleFile(arrayOf("-l", "0", "-u", "1", "-s", "${filesDir}/range", "${filesDir}/train"))
createModelFile(arrayOf("-s", "0", "-c", "128.0", "-t", "2", "-g", "8.0", "-e", "0.1", "${filesDir}/scale", "${filesDir}/model"))
createPredictFile(arrayOf("${filesDir}/scale", "${filesDir}/model", "${filesDir}/predict"))
runOnUiThread {
var reader: BufferedReader = BufferedReader(InputStreamReader(FileInputStream("${filesDir}/accuracy")))
val line = reader.readLine()
tv_accuracy.text = line.replace("Accuracy", "准确率")
btn_train.setBackgroundResource(R.mipmap.train_off)
}
}
}
// 开始识别按钮点击调用
btn_understand.setOnClickListener {
understand(isStartUnderStand)
}
// 删除文件
btn_delete.setOnClickListener {
doAsync {
val file = File("${filesDir}")
for (item in file.list()) {
File("${filesDir}/${item}").delete()
}
runOnUiThread {
toast("删除成功!")
}
}
}
}
/**
* 创建归一化文件
*/
fun createScaleFile(args: Array<String>) {
val out = System.out
var outScaleFile = PrintStream("${filesDir}/scale")
System.setOut(outScaleFile)
svm_scale.main(args)
System.setOut(out)
}
/**
* 创建model文件
*/
fun createModelFile(args: Array<String>) {
svm_train.main(args)
}
/**
* 创建Predict文件
*/
fun createPredictFile(args: Array<String>) {
val out = System.out
var outAccuracy = PrintStream("${filesDir}/accuracy")
System.setOut(outAccuracy)
svm_predict.main(args)
System.setOut(out)
}
/**
* 采集数据
*/
private fun collection(b: Boolean) {
if (!b) { // 开始采集
mSensorManager.registerListener(sensorListener, mAccSensor, mHz)
btn_collection.setBackgroundResource(R.mipmap.sample)
} else { // 停止采集
mSensorManager.unregisterListener(sensorListener)
btn_collection.setBackgroundResource(R.mipmap.sample_off)
}
isStartCollection = !isStartCollection
}
/**
* 识别结果
*/
private fun understand(b: Boolean) {
if (!b) { // 开始识别
Util.loadFile("${filesDir}/range", "${filesDir}/model")
mSensorManager.registerListener(sensorListener, mAccSensor, mHz)
btn_understand.setBackgroundResource(R.mipmap.test)
} else { // 停止识别
mSensorManager.unregisterListener(sensorListener)
btn_understand.setBackgroundResource(R.mipmap.test_off)
result(-1)
}
isStartUnderStand = !isStartUnderStand
}
fun result(code: Int) {
iv_still.setBackgroundResource(R.mipmap.gait_still_off)
iv_walk.setBackgroundResource(R.mipmap.gait_walk_off)
iv_run.setBackgroundResource(R.mipmap.gait_run_off)
when (code) {
0 -> iv_still.setBackgroundResource(R.mipmap.gait_still)
1 -> iv_walk.setBackgroundResource(R.mipmap.gait_walk)
2 -> iv_run.setBackgroundResource(R.mipmap.gait_run)
}
}
}