def detect_cycle(data: List[List[float]]) -> List[np.ndarray]: """ 周期检测 :param data: 一个list,里面n个[x,y,z] :return: 一个list,里面m个list表示m个周期,每一个周期内包含若干个[x,y,z] """ def distance(list1, list2): assert len(list1) == len(list2), "比较欧式距离时两个向量长度应该相等" s = 0 for i in range(len(list1)): s = s + math.pow(list1[i] - list2[i], 2) return round(math.sqrt(s), 2) reference_length = 50 # 数据点模板长度,在50HZ的数据中,长度为50表示使用1S的模板且模板的位置选在了中间 dis = [] count = 0 # 这是用来划分走路周期的,在跟模板比较之后,根据波形的波谷进行划分,实际上是两个波谷才是一个完整的走路周期 result = [] temp = [] x2y2z2 = [i[0] * i[0] + i[1] * i[1] + i[2] * i[2] for i in data] for i in range(0, len(x2y2z2) - reference_length): dis.append( distance( x2y2z2[i:i + reference_length], x2y2z2[len(x2y2z2) // 2:len(x2y2z2) // 2 + reference_length])) for i in range(1, len(dis) - 1): temp.append(data[i]) if dis[i] < dis[i - 1] and dis[i] < dis[i + 1]: count = (count + 1) % 2 if count == 0: result.append(np.array(temp)) temp = [] return result
def load_data0_data(file_name: str) -> np.ndarray: """ 读data0的数据 :param file_name: :return: """ with open(file_name, "r", encoding="utf-8") as file: lines = file.readlines() lines = [[float(v) for v in line.split(" ")] for line in lines] return np.array(lines)
def get_gait_cycle(self, data: list) -> Union[np.ndarray, None]: """ 获取步态周期 :return: 步态周期 """ if len(data) == 0: return None validate_raw_data_with_timestamp(np.array(data)) first_cycle = self._find_first_gait_cycle(np.array(data)) if first_cycle is None: # TODO count_threshold_to_clear_template 必须保证比sensor中存的最大的点数要小,不然一直无法清除模板,这里暂时都是400,没有限制 # print(len(data)) if len(data) >= self.count_threshold_to_clear_template: self.template = None return None # first_cycle = self.transform(first_cycle) # if len(first_cycle) > 4: # 点的数量太少无法插值 # first_cycle = self.interpolate(first_cycle) # return [], None # interpolated_cycle = self.interpolate(transformed_cycle) interpolated_cycle_without_transform = self.interpolate(first_cycle) self.cycle_count += 1 if self.DEBUG: plt.plot(interpolated_cycle_without_transform[:, 1], "r", label="x") plt.plot(interpolated_cycle_without_transform[:, 2], "g", label="y") plt.plot(interpolated_cycle_without_transform[:, 3], "b", label="z") plt.title("interpolated_cycle") plt.legend() plt.show() if self.DEBUG: logger.debug("{0}:CYCYLE INDEX:{1}".format(self.data_type, self.cycle_count)) return np.concatenate( (np.array([self._mag(interpolated_cycle_without_transform[:, 1:]) ]).T, interpolated_cycle_without_transform[:, 1:]), axis=1)
def chazhi(data: list, point_number_per_cycle=100) -> np.ndarray: """ 对数据进行插值 :param point_number_per_cycle: 插值之后每个周期内的数据点个数 :param data: 一个list,里面是n个list,每个list里面是若干个[x,y,z] :return: 一个list,里面是n个list,每个list里面是:POINT_NUMBER_PER_CYCLE个插值完的[x,y,z] """ for i, data_i in enumerate(data): data_i = np.array(data_i) x_old, y_old, z_old = data_i[:, 0], data_i[:, 1], data_i[:, 2] x = np.linspace(0, len(data_i), len(data_i)) x_index = np.linspace(0, len(data_i), point_number_per_cycle) new_x = interpolate.interp1d(x, x_old, kind="quadratic")(x_index) new_y = interpolate.interp1d(x, y_old, kind="quadratic")(x_index) new_z = interpolate.interp1d(x, z_old, kind="quadratic")(x_index) temp = [] for j in range(len(new_x)): temp.append((new_x[j], new_y[j], new_z[j])) data[i] = np.array(temp) return np.array(data)
def _mag(self, data: np.ndarray) -> np.ndarray: """ 计算合加速度 :param data: :return: """ validate_raw_data_without_timestamp(data) result = np.array( [math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) for d in data]) # return self.lowpass(result) return result
def update(self): """ 更新曲线数据 :return: """ data = np.array(self.get_raw_data()) if data.any(): validate_raw_data_with_timestamp(data) t = list(range(len(data))) self.line_x.set_data(t, data[:, 1]) self.line_y.set_data(t, data[:, 2]) self.line_z.set_data(t, data[:, 3])
def visualize(self): """ 网络可视化 :return: """ """ _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) (None, 8, 200, 1) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 8, 191, 20) 220 _________________________________________________________________ conv2d_2 (Conv2D) (None, 5, 182, 40) 32040 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 2, 91, 40) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 7280) 0 _________________________________________________________________ dense_1 (Dense) (None, 40) 291240 _________________________________________________________________ dense_2 (Dense) (None, 10) 410 ================================================================= """ data, label = self._load_data() data = np.reshape(data, data.shape + (1, )) activation_model = Model( inputs=[self.model.input], outputs=[layer.output for layer in self.model.layers[1:3]]) activations = activation_model.predict(np.array([data[3]])) """ >>> len(activations) 2 # 两层 >>> activations[0].shape (1, 8, 191, 20) """ filter_count = 20 display_layer = 1 # 展示的第几层 display_mat = np.zeros( (filter_count * activations[display_layer][0].shape[0], activations[display_layer][0].shape[1])) for i in range(filter_count): channel_image = activations[display_layer][0, :, :, i] channel_image -= channel_image.mean() channel_image /= channel_image.std() channel_image *= 64 channel_image += 128 channel_image = np.clip(channel_image, 0, 255).astype("uint8") display_mat[i * activations[display_layer][0].shape[0]:(i + 1) * activations[display_layer][0].shape[0], : activations[display_layer][0].shape[1]] = channel_image plt.matshow(display_mat) plt.show()
def load_data0_cycle() -> Tuple[np.ndarray, np.ndarray]: """ 载入用data0生成的步态周期数据 :return: """ data_file_full_name = os.path.join(CYCLE_FILE_DIR, "data") if not os.path.exists(data_file_full_name): logger.debug("data0 cycle数据不存在,开始生成") data, label = [], [] from sensor.sensor import SensorManager from sensor.algorithm.data_pre_process import AccDataPreProcess, GyroDataPreProcess for i in range(10): sensor_manager = SensorManager(i) acc_data_pre_process = AccDataPreProcess(sensor_manager) gyro_data_pre_process = GyroDataPreProcess(sensor_manager) acc_cycles = [] gyro_cycles = [] while True: get_data_result = sensor_manager.update_display_raw_data() if not get_data_result: break sensor_manager.acc_to_display, acc_cycle = acc_data_pre_process.get_gait_cycle( sensor_manager.acc_to_display) sensor_manager.gyro_to_display, gyro_cycle = gyro_data_pre_process.get_gait_cycle( sensor_manager.gyro_to_display) if acc_cycle is not None: acc_cycles.append(acc_cycle) if gyro_cycle is not None: gyro_cycles.append(gyro_cycle) for acc_cycle, gyro_cycle in zip(acc_cycles, gyro_cycles): data.append(np.concatenate((acc_cycle, gyro_cycle), axis=1)) label.append(i) logger.debug("生成CNN数据:{0}".format(i)) with open(data_file_full_name, "wb") as file: file.write(pickle.dumps((np.array(data), np.array(label)))) with open(data_file_full_name, "rb") as file: data, label = pickle.loads(file.read()) assert isinstance(data, np.ndarray) and len( data.shape) == 3 and data.shape[1] == 200 and data.shape[2] == 8 return data.transpose((0, 2, 1)), label # 数据是200 * 8的,训练需要8 * 200
def _get_feature(self, data: np.ndarray) -> np.ndarray: """ 生成用于分类的特征 :param data: :return: """ assert len(data.shape) == 2 and data.shape[1] == 4, "one class分类器生成特征数据格式异常" result = np.array([]) # 平均值 result = np.append(result, np.average(data, axis=0)) # 标准差 result = np.append(result, np.std(data, axis=0)) return result
def split_data(data: np.ndarray, label: np.ndarray, ratio: tuple = (0.8, 0, 0.2), **kwargs): """ 把数据和标签分为训练集、验证集和测试集 :param data: :param label: :param ratio: :param kwargs: :normalization 表示归一化, :to_categorical表示将label进行one-hot编码 :return: """ data = pop_timestamp(data) data = np.array(data) label = np.array(label) if "normalization" in kwargs: mean = data.mean() data = data - mean std = data.std() data = data / std index = np.arange(len(data)) np.random.shuffle(index) data = data[index] label = label[index] if "to_categorical" in kwargs: label = to_categorical(label) train_data_number = int(len(data) * ratio[0]) validate_data_number = int(len(data) * ratio[1]) train_data, train_label = data[: train_data_number], label[: train_data_number] validate_data, validate_label = data[ train_data_number:train_data_number + validate_data_number], label[train_data_number:train_data_number + validate_data_number] test_data, test_label = data[train_data_number + validate_data_number:], label[ train_data_number + validate_data_number:] return train_data, train_label, validate_data, validate_label, test_data, test_label
def test_model(self): """ 随机挑几个数来测试模型 :return: """ data, label = load_data0_cycle() data = np.reshape(data, data.shape + (1, )) for i in range(10): index = random.choice(range(len(data))) predict_index = np.argmax( self.model.predict(np.array([data[index]]))) print("index:{0},预测值:{1},实际值:{2},预测成功:{3}".format( index, predict_index, label[index], bool(predict_index == label[index])))
def _load_data(self) -> Tuple[np.ndarray, np.ndarray]: acc_data_full_path = os.path.join(self.HHAR_DATA_PATH, "Watch_accelerometer") if os.path.isfile(acc_data_full_path): logger.info("{0}训练数据已经存在".format(self.network_name)) else: logger.info("{0}训练数据不存在".format(self.network_name)) data, label = [], [] with open(os.path.join(self.HHAR_DATA_PATH, "Watch_accelerometer.csv"), "r") as file: tmp_data = [] reader = csv.DictReader(file) last_label = None last_time = None for line in reader: cur_label = self.LABEL_MAP.get(line.get("gt")) cur_time = int(line.get("Arrival_Time")) if not last_time: last_time = cur_time elif cur_time - last_time < 20: continue else: last_time = cur_time if cur_label == last_label: tmp_data.append([float(line.get("x")), float(line.get("y")), float(line.get("z"))]) else: if not cur_label: continue cycles = detect_cycle(tmp_data) cycles = chazhi(cycles) data.extend(cycles) label.extend([last_label for _ in range(len(cycles))]) last_label = cur_label tmp_data.clear() with open(acc_data_full_path, "wb") as file: file.write(pickle.dumps((data, label))) with open(acc_data_full_path, "rb") as file: data = pickle.loads(file.read()) return np.array(data[0]), np.array(data[1])
def update_cycle_detect_result(self, is_walking): """ 更新步态检测结果,用于在GUI上显示历史记录 :param is_walking: :return: """ if self.acc_data_pre_process.last_cycle is not None: print(self.acc_one_class_svm.predict(np.array([self.acc_data_pre_process.last_cycle]))) if is_walking: if self.acc_data_pre_process.last_cycle is not None or self.gyro_data_pre_process.last_cycle is not None: self.cycle_detect_history[CycleDetectResult.CYCLE_DETECTED] += 1 else: self.cycle_detect_history[CycleDetectResult.WALK_BUT_NO_CYCLE] += 1 else: self.cycle_detect_history[CycleDetectResult.NOT_WALKING] += 1
def interpolate(self, data: np.ndarray) -> np.ndarray: """ 对数据进行插值 :param data: 一个list,里面是n个list,每个list里面是若干个[x,y,z] :return: 一个list,里面是n个list,每个list里面是:POINT_NUMBER_PER_CYCLE个插值完的[x,y,z] """ validate_raw_data_with_timestamp( data) # 这里也是有四列,不过第一列不是时间而是合成加速度,校验函数通用 mag_old, x_old, y_old, z_old = data[:, 0], data[:, 1], data[:, 2], data[:, 3] x = np.linspace(0, len(data), len(data)) x_index = np.linspace(0, len(data), self.point_count_per_cycle) new_mag = interpolate.interp1d(x, mag_old, kind="quadratic")(x_index) new_x = interpolate.interp1d(x, x_old, kind="quadratic")(x_index) new_y = interpolate.interp1d(x, y_old, kind="quadratic")(x_index) new_z = interpolate.interp1d(x, z_old, kind="quadratic")(x_index) return np.array([new_mag, new_x, new_y, new_z]).T
def _is_walking(self) -> bool: """ 判断当前是否在行走,直接阈值判断 :return: """ mag_interval = (20, 900) test_data = np.array(self._sensor_manager.acc_to_display[-100:]) if not len(test_data): return False mag = [d[1] * d[1] + d[2] * d[2] + d[3] * d[3] for d in test_data] is_mag_ok = min(mag) >= mag_interval[0] and max(mag) <= mag_interval[1] ok_threshold = 5 is_x_ok = max(test_data[:, 1]) - min(test_data[:, 1]) > ok_threshold is_y_ok = max(test_data[:, 2]) - min(test_data[:, 2]) > ok_threshold is_z_ok = max(test_data[:, 3]) - min(test_data[:, 3]) > ok_threshold is_walking = is_mag_ok and (is_x_ok or is_y_ok or is_z_ok) if not is_walking: # 没在走路的话去清空数据 self._sensor_manager.clear_data_to_detect_cycle() # self._sensor_manager.send_msg(bytes("行走:{0}".format(is_walking),encoding="utf-8")) return is_walking
def _find_new_template(self, data) -> Union[np.ndarray, None]: """ 初始化的时候或者找不到步态周期的时候需要重新寻找模板 step1:寻找第一个局部最小点 step2:在局部最小点周围1S内寻找最小点 step3:最小点周围1S作为模板 :param data: :return: """ validate_raw_data_with_timestamp(data) mags = np.array( [math.sqrt(d[1] * d[1] + d[2] * d[2] + d[3] * d[3]) for d in data]) for index in range(len(mags)): # step1 if 0 < index < len(mags) - 1 and mags[index] < min( mags[index - 1], mags[index + 1]): start, end = index, index while start >= 0 and end < len(mags) and data[end][0] - data[ start][0] < self.template_duration: start -= 1 end += 1 # step2 if start < 0 or end >= len(mags): continue window_around_local_minimum = mags[start:end] # step3 minimum_point_index = start + np.argmin( window_around_local_minimum) start, end = minimum_point_index, minimum_point_index while start >= 0 and end < len(data) and data[end][0] - data[ start][0] < self.template_duration: start -= 1 end += 1 if start < 0 or end >= len(data): continue # TODO 实时数据acc有问题是因为这里模板找的太长了 100 # print(end - start) # print("start end", data[start:end+1]) # print(end - start + 1) return self._mag(data[start:end][:, 1:]) return None
def get_current_activity(self) -> str: """ 获取当前的运动状态 :return: """ # 预测动作 if len(self._sensor_manager.acc_to_display) >= 100: predict_result = self.activity_recognition_network.predict( [np.array(self._sensor_manager.acc_to_display)[-100:, 1:]]) predict_number = int(np.argmax(predict_result[0])) # self._sensor_manager.conn.send(str(predict_number).encode("utf-8")) m = { 0: "骑车", 1: "静止", 2: "步行", 3: "上楼", 4: "下楼", } if self.is_walking: return "步行" return m.get(predict_number) else: return ""
def transform(self, matrix_a: np.ndarray) -> np.ndarray: """ 将步态周期进行坐标转换 :param matrix_a: 周期 :return: """ validate_raw_data_with_timestamp(matrix_a) matrix_a = matrix_a[:, 1:] vector_p_k = np.average(matrix_a, axis=0).T vector_n1 = vector_p_k / np.linalg.norm(vector_p_k) # 一撇撇 vector_a_n1 = np.dot(matrix_a, vector_n1) matrix_a_f = matrix_a - np.dot(vector_a_n1[:, np.newaxis], vector_n1[np.newaxis, :]) u = np.average(matrix_a_f, axis=0) matrix_a_norm_f = matrix_a_f - u sigma = np.dot(matrix_a_norm_f.T, matrix_a_norm_f) / (matrix_a.shape[0] - 1) eigenvalue, eigenvector = np.linalg.eig(sigma) vector_n2 = eigenvector[np.argmax(eigenvalue)] # 两撇撇 vector_n3 = np.cross(vector_n1, vector_n2) vector_a_n2 = np.dot(matrix_a, vector_n2) vector_a_n3 = np.dot(matrix_a, vector_n3) return np.array( [self._mag(matrix_a), vector_a_n1, vector_a_n2, vector_a_n3]).T
def predict(self, data: list): data = np.array(data) return self.model.predict(data)
def _find_first_gait_cycle(self, data: np.ndarray) -> Union[np.ndarray, None]: """ 检测寻找第一个步态周期 :param data: 原始数据 :return: 步态周期 """ validate_raw_data_with_timestamp(data) mags = self._mag(data[:, 1:]) if self.template is None: self.template = self._find_new_template(data) # print("template", self.template) # if self.template is not None: # print(len(self.template)) if self.template is None: return None cycle_index_points = [] corr_distance = [] for i in range(len(mags) - len(self.template) + 1): corr_distance.append( self._corr_distance(self.template, mags[i:i + len(self.template)])) corr_distance = self._lowpass(np.array(corr_distance)) for i in range(len(corr_distance)): if i >= 2 and corr_distance[i - 1] < min(corr_distance[i - 2], corr_distance[i]) and \ corr_distance[i - 1] < self.gait_cycle_threshold: cycle_index_points.append(i - 1) if len(cycle_index_points) == 2: # 如果找到的周期时间不够的话,就凑上下一个周期 self.cycle_duration = int( data[cycle_index_points[1]][0]) - int( data[cycle_index_points[0]][0]) if self.cycle_duration < self.expect_gait_cycle_duration[0]: del cycle_index_points[-1] continue elif self.cycle_duration > self.expect_gait_cycle_duration[ 1]: cycle_index_points[0] = cycle_index_points[1] del cycle_index_points[-1] continue if len(cycle_index_points) == 3: self.cycle_duration = int( data[cycle_index_points[2]][0]) - int( data[cycle_index_points[1]][0]) if self.cycle_duration < self.expect_gait_cycle_duration[0]: del cycle_index_points[-1] continue elif self.cycle_duration > self.expect_gait_cycle_duration[ 1]: cycle_index_points[0] = cycle_index_points[2] del cycle_index_points[-1] del cycle_index_points[-1] continue if len(cycle_index_points) == 4: self.cycle_duration = int( data[cycle_index_points[3]][0]) - int( data[cycle_index_points[2]][0]) if self.cycle_duration < self.expect_gait_cycle_duration[0]: del cycle_index_points[-1] continue elif self.cycle_duration > self.expect_gait_cycle_duration[ 1]: cycle_index_points[0] = cycle_index_points[3] del cycle_index_points[-1] del cycle_index_points[-1] del cycle_index_points[-1] continue if self.last_cycle_to_locate is None: cycle = data[ cycle_index_points[0]:cycle_index_points[2] + 1] self.last_cycle_to_locate = cycle if self.DEBUG: use_first_cycle = True else: cycle1 = data[ cycle_index_points[0]:cycle_index_points[2] + 1] cycle2 = data[ cycle_index_points[1]:cycle_index_points[3] + 1] if self.fast_dtw( self.last_cycle_to_locate[:, 3], cycle1[:, 3]) < self.fast_dtw( self.last_cycle_to_locate[:, 3], cycle2[:, 3]): # 使用4格 + z轴fastdtw来寻找周期 cycle = cycle1 if self.DEBUG: use_first_cycle = True else: cycle = cycle2 if self.DEBUG: use_first_cycle = False self.last_cycle_to_locate = self._update_last_cycle( cycle) if self.DEBUG: plt.cla() plt.plot(data[:, 1], "r", label="x") plt.plot(data[:, 2], "g", label="y") plt.plot(data[:, 3], "b", label="z") plt.plot(self._mag(data[:, 1:]), "black", label="mag") plt.plot(corr_distance, "y", label="dis") plt.axvline(cycle_index_points[0], color="r" if use_first_cycle else "b") plt.axvline(cycle_index_points[1], color="r" if not use_first_cycle else "b") plt.axvline(cycle_index_points[2], color="r" if use_first_cycle else "b") plt.axvline(cycle_index_points[3], color="r" if not use_first_cycle else "b") plt.legend() plt.title("search cycle") plt.show() self.template = self._update_template(cycle) return cycle return None