PX4:【sensor_combined】

本文介绍sensor_combined消息,它通过整合多个传感器数据并应用VoteSensorsUpdate机制,筛选出最可靠数据集,用于姿态与位置解算。发布内容包括陀螺仪与加速度计的测量值及时间戳。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

功能介绍

sensor_combined 是一个冗余传感器集合的消息,

通过订阅多个传感器的数据,将冗余的数据经过VoteSensorsUpdate投票机制,筛选出较为可靠的一组数据,用于状态估计模块的姿态解算和位置解算。

消息内容

sensor_combined.msg发布的数据包含:

uint64  timestamp ;
float gyro_rad[3]:上一采样周期内,陀螺仪在body系下xyz的测量平均值(rad/s)
uint32_t gyro_integral_dt :陀螺仪测量采样周期(us)
int32_t accelerometer_timesatmp_relative :  = accelerometer timestamp(加速度采样的时间戳)- timestamp(sensor_combined的时间戳)
float accelerometer_m_s2[3] :上一采样周期内,加速度计在body系下xyz的测量平均值(m/s2)
uint32_t accelerometer_integral_dt  :加速度计测量采用周期(us)
uint8_t accelerometer_clipping;
uint8_t _padding0[3]

sensor_combined 产生机制 & 代码流程

在这里插入图片描述

在这里插入图片描述

from flask import Flask, request, jsonify, send_from_directory from flask_cors import CORS import os import numpy as np import pandas as pd import logging import time import json import traceback from werkzeug.utils import secure_filename from new_algorithm import GasSensorDataAnalyzer, AlgorithmSelector, detect_dataset_type, extract_gas_type, extract_concentration, extract_sensor_type # 配置日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), # 输出到控制台 logging.FileHandler('app.log', encoding='utf-8') # 输出到文件 ] ) logger = logging.getLogger(__name__) app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB 文件大小限制 # 配置CORS允许Vue前端访问 CORS(app, resources={ r"/*": { "origins": "http://localhost:5177", # 指定Vue前端地址 "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization"], "supports_credentials": True } }) # 配置上传文件夹 UPLOAD_FOLDER = 'uploads' if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # 允许的文件扩展名 ALLOWED_EXTENSIONS = {'csv', 'xlsx', 'xls'} # 全局数据集存储 data_analyzer = GasSensorDataAnalyzer() X_combined = None y_combined = None gas_types = [] concentrations = [] sensor_types = [] last_activity = time.time() algorithm_selector = AlgorithmSelector(use_chinese=True) def allowed_file(filename): """检查文件扩展名是否合法""" return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def save_and_extract_file_info(file): """保存文件并提取气体信息""" try: filename = secure_filename(file.filename) file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(file_path) logger.info(f"Saved file: {file_path}") # 从文件名提取信息 sensor_type = extract_sensor_type(filename) gas_type = extract_gas_type(filename) concentration = extract_concentration(filename) return file_path, sensor_type, gas_type, concentration except Exception as e: logger.error(f"Error processing file {file.filename}: {str(e)}\n{traceback.format_exc()}") return None, None, None, None @app.route('/') def index(): """健康检查端点""" global last_activity status = { 'status': 'running', 'version': '1.0.0', 'last_activity': time.ctime(last_activity), 'endpoints': { '/upload': 'POST - Upload data files', '/analyze': 'POST - Analyze data', '/reset': 'POST - Reset data', '/columns': 'GET - Get dataset columns', '/status': 'GET - Service status' } } logger.info(f"Status request: {status}") return jsonify(status) @app.route('/status') def status(): """服务状态检查""" global X_combined, last_activity return jsonify({ 'status': 'active', 'timestamp': time.time(), 'dataset_loaded': X_combined is not None, 'dataset_shape': X_combined.shape if X_combined is not None else None, 'num_classes': len(np.unique(y_combined)) if y_combined is not None else 0 }) @app.route('/upload', methods=['POST']) def upload_files(): """处理文件上传""" global X_combined, y_combined, gas_types, concentrations, sensor_types, last_activity logger.info("Received upload request") # 检查是否有文件 if 'files' not in request.files: logger.error("No file part in request") return jsonify({'error': 'No file part'}), 400 files = request.files.getlist('files') if len(files) == 0 or files[0].filename == '': logger.error("No selected files") return jsonify({'error': 'No selected files'}), 400 # 过滤合法文件 valid_files = [f for f in files if allowed_file(f.filename)] if len(valid_files) < 2: logger.error("Need at least two files for analysis") return jsonify({'error': '需要至少两个文件进行分析'}), 400 # 保存文件并提取信息 file_paths = [] extracted_info = [] for file in valid_files: file_path, sensor_type, gas_type, concentration = save_and_extract_file_info(file) if file_path: file_paths.append(file_path) extracted_info.append({ 'sensor_type': sensor_type, 'gas_type': gas_type, 'concentration': concentration, 'filename': file.filename }) else: logger.error(f"Failed to process file: {file.filename}") if len(file_paths) < 2: logger.error("Failed to process enough files") return jsonify({ 'error': '文件处理失败,请检查文件格式', 'processed_files': [info['filename'] for info in extracted_info], 'details': extracted_info }), 500 # 准备数据加载参数 sensor_types = [info['sensor_type'] for info in extracted_info] gas_types = [info['gas_type'] for info in extracted_info] concentrations = [info['concentration'] for info in extracted_info] # 加载数据 try: X_combined, y_combined = data_analyzer.load_multiple_gas_data( file_paths, gas_types, concentrations, sensor_types ) if X_combined is None or len(X_combined) == 0: logger.error("Failed to load data from files") return jsonify({ 'error': '数据加载失败,请检查文件内容', 'file_details': extracted_info }), 500 logger.info(f"Loaded combined data: {len(X_combined)} samples, {X_combined.shape[1]} features, {len(np.unique(y_combined))} classes") # 获取多维度标签信息 label_info = [] for label in np.unique(y_combined): # 查找对应的标签信息 for key, label_id in data_analyzer.multi_dimension_labels.items(): if label_id == label: parts = key.split('_') if len(parts) >= 3: sensor = parts[0] gas = parts[1] conc = parts[2].replace('ppm', '') try: _, name_dict = data_analyzer.get_or_create_multi_dimension_label(sensor, gas, int(conc)) label_info.append({ 'id': int(label), 'sensor': sensor, 'gas': gas, 'concentration': conc, 'name_cn': name_dict['cn'], 'name_en': name_dict['en'] }) except Exception as e: logger.error(f"Error getting label info for {key}: {str(e)}") label_info.append({ 'id': int(label), 'sensor': sensor, 'gas': gas, 'concentration': conc, 'name_cn': f"未知标签 {label}", 'name_en': f"Unknown Label {label}" }) else: logger.warning(f"Invalid key format: {key}") # 更新最后活动时间 last_activity = time.time() # 返回成功响应 response = { 'message': f'成功上传并合并 {len(file_paths)} 个文件', 'sample_count': len(X_combined), 'feature_count': X_combined.shape[1], 'num_classes': len(np.unique(y_combined)), 'gas_types': gas_types, 'concentrations': concentrations, 'sensor_types': sensor_types, 'label_info': label_info } return jsonify(response), 200 except Exception as e: logger.error(f"Error loading data: {str(e)}\n{traceback.format_exc()}") return jsonify({ 'error': f'数据加载错误: {str(e)}', 'traceback': traceback.format_exc().splitlines() }), 500 @app.route('/analyze', methods=['POST']) def analyze_data(): """执行数据分析""" global X_combined, y_combined, algorithm_selector, last_activity logger.info("Received analyze request") # 检查数据是否已加载 if X_combined is None or y_combined is None: logger.error("No dataset available") return jsonify({'error': '没有可用数据,请先上传文件'}), 400 # 获取前端传递的算法参数 try: data = request.get_json() if not data or 'params' not in data: logger.error("Invalid request parameters") return jsonify({'error': '无效请求参数'}), 400 params = data.get('params', {}) # 设置算法参数 for algo_name, algo_params in params.items(): if algo_name in algorithm_selector.algorithms: logger.info(f"Setting parameters for {algo_name}: {algo_params}") algorithm_selector.set_algorithm_params(algo_name, algo_params) except Exception as e: logger.error(f"Error parsing JSON data: {str(e)}\n{traceback.format_exc()}") return jsonify({'error': 'JSON数据解析错误'}), 400 # 训练模型 try: logger.info(f"Starting model training with {len(X_combined)} samples and {len(np.unique(y_combined))} classes") results = algorithm_selector.train_models(X_combined, y_combined) logger.info("Algorithm training completed") except Exception as e: logger.error(f"Error training models: {str(e)}\n{traceback.format_exc()}") return jsonify({'error': f'模型训练错误: {str(e)}'}), 500 # 提取需要返回的结果(关键修复) response_results = {} for algo_name, result in results.items(): # 如果训练出错,记录错误信息 if 'error' in result: response_results[algo_name] = { 'name': result['name'], 'error': result['error'] } else: # 转换分类报告为字符串(如果存在) if 'classification_report' in result: report = result['classification_report'] if isinstance(report, dict): # 将分类报告字典转换为格式化的字符串 report_str = "\n".join([f"{key}: {value}" for key, value in report.items()]) else: report_str = str(report) else: report_str = "无分类报告" # 提取特征重要性(如果存在) feature_importances = None if 'feature_importances' in result and result['feature_importances'] is not None: # 确保特征重要性是字典格式 if isinstance(result['feature_importances'], dict): feature_importances = result['feature_importances'] elif hasattr(result['model'], 'feature_importances_'): # 尝试从模型中提取特征重要性 try: feature_importances = dict(zip( result['model'].feature_names_in_, result['model'].feature_importances_ )) except Exception as e: logger.warning(f"无法提取特征重要性: {str(e)}") feature_importances = None response_results[algo_name] = { 'name': result['name'], 'train_accuracy': result['train_accuracy'], 'test_accuracy': result['test_accuracy'], 'classification_report': report_str, 'feature_importances': feature_importances } # 更新最后活动时间 last_activity = time.time() return jsonify({ 'message': '分析成功完成', 'results': response_results, 'num_classes': len(np.unique(y_combined)), 'sample_count': len(X_combined) }), 200 @app.route('/reset', methods=['POST']) def reset_data(): """重置数据集""" global X_combined, y_combined, gas_types, concentrations, sensor_types, last_activity X_combined = None y_combined = None gas_types = [] concentrations = [] sensor_types = [] last_activity = time.time() logger.info("Data reset") return jsonify({'message': '数据已重置'}), 200 @app.route('/uploads/<filename>', methods=['GET']) def uploaded_file(filename): """访问上传的文件""" return send_from_directory(app.config['UPLOAD_FOLDER'], filename) if __name__ == '__main__': # 开发环境 - 允许Vue前端访问 app.run(host='0.0.0.0', port=5000, debug=True) 这是你上次修改的代码,上次的代码还能够上传数据,这个功能也要保留的同时,能够分析数据
最新发布
06-22
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值