XM-01/资料/算法文件/mcu_controller.c

836 lines
25 KiB
C

/**
* MCU Mattress Controller - C Implementation
*
* This program implements sleep posture detection and mattress control for MCUs.
* It uses TensorFlow Lite for Microcontrollers for inference.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
// If model data is included as a C array
#include "model_tflite.h" // Replace with your model header
// Constants for mode definitions
#define MODE_MANUAL 0
#define MODE_ADAPTIVE 1
#define MODE_PREVENTION 2
#define MODE_ABAB 3
#define MODE_TURNING 4
#define MODE_FLOATING 5
// Command codes from remote controller
#define CMD_STOP 0x00
#define CMD_ADAPTIVE_MODE 0x01
#define CMD_PREVENTION_MODE 0x02
#define CMD_ABAB_MODE 0x03
#define CMD_TURNING_MODE 0x04
#define CMD_FLOATING_MODE 0x05
#define CMD_DETECT_POSTURE 0x06
// Posture definitions
#define POSTURE_SUPINE 0 // Supine (back)
#define POSTURE_LEFT 1 // Left side
#define POSTURE_RIGHT 2 // Right side
// Serial port defines - platform specific
#define MATTRESS_PORT "/dev/ttyS1"
#define SENSOR_PORT "/dev/ttyS2"
#define REMOTE_PORT "/dev/ttyS0"
// Data structures
typedef struct {
int posture_index;
float confidence;
bool success;
} PostureResult;
typedef struct {
// Serial handles
void* mattress_ser;
void* sensor_ser;
void* remote_ser;
// Thread management
pthread_t running_threads[5]; // Support up to 5 concurrent threads
int thread_count;
pthread_mutex_t thread_mutex;
bool stop_event;
// TFLite model
tflite::MicroErrorReporter error_reporter;
tflite::AllOpsResolver resolver;
const tflite::Model* model;
tflite::MicroInterpreter* interpreter;
TfLiteTensor* input_tensor;
TfLiteTensor* output_tensor;
bool model_loaded;
bool is_quantized;
// Device ID bytes for mattress commands
uint8_t id_bytes[4];
// Current posture tracking
int current_posture;
float confidence;
// Memory buffer for TensorFlow Lite arena
uint8_t tensor_arena[128 * 1024]; // 128KB tensor arena
} MCUController;
// Forward declarations for threading
void* adaptive_thread_func(void* arg);
void* prevention_thread_func(void* arg);
void* abab_thread_func(void* arg);
void* turning_thread_func(void* arg);
void* floating_thread_func(void* arg);
// Serial communication stubs (platform-specific implementations needed)
bool serial_open(void** handle, const char* port, int baudrate);
void serial_close(void* handle);
bool serial_write(void* handle, const uint8_t* data, size_t length);
int serial_read(void* handle, uint8_t* buffer, size_t max_length);
int serial_available(void* handle);
/**
* Initialize the controller
*/
bool controller_init(MCUController* controller) {
memset(controller, 0, sizeof(MCUController));
// Initialize mutex
pthread_mutex_init(&controller->thread_mutex, NULL);
// Initialize device ID bytes
controller->id_bytes[0] = 0x01;
controller->id_bytes[1] = 0x02;
controller->id_bytes[2] = 0x03;
controller->id_bytes[3] = 0x04;
// Load TFLite model
controller->model = tflite::GetModel(g_model_tflite); // Use the model data from header
if (controller->model->version() != TFLITE_SCHEMA_VERSION) {
printf("Model schema version mismatch!\n");
return false;
}
// Create interpreter
static tflite::MicroInterpreter static_interpreter(
controller->model,
controller->resolver,
controller->tensor_arena,
sizeof(controller->tensor_arena),
&controller->error_reporter);
controller->interpreter = &static_interpreter;
// Allocate tensors
TfLiteStatus allocate_status = controller->interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
printf("Failed to allocate tensors!\n");
return false;
}
// Get input and output tensors
controller->input_tensor = controller->interpreter->input(0);
controller->output_tensor = controller->interpreter->output(0);
// Check if model is quantized
controller->is_quantized = controller->input_tensor->type == kTfLiteInt8;
printf("Model loaded successfully\n");
printf("Model is %s\n", controller->is_quantized ? "quantized" : "floating-point");
controller->model_loaded = true;
return true;
}
/**
* Connect to mattress
*/
bool connect_mattress(MCUController* controller, const char* port, int baudrate) {
// Close existing connection if any
if (controller->mattress_ser) {
serial_close(controller->mattress_ser);
controller->mattress_ser = NULL;
}
// Open new connection
if (serial_open(&controller->mattress_ser, port, baudrate)) {
printf("Connected to mattress on %s\n", port);
return true;
} else {
printf("Failed to connect to mattress on %s\n", port);
return false;
}
}
/**
* Connect to sensor
*/
bool connect_sensor(MCUController* controller, const char* port, int baudrate) {
// Close existing connection if any
if (controller->sensor_ser) {
serial_close(controller->sensor_ser);
controller->sensor_ser = NULL;
}
// Open new connection
if (serial_open(&controller->sensor_ser, port, baudrate)) {
printf("Connected to sensor on %s\n", port);
return true;
} else {
printf("Failed to connect to sensor on %s\n", port);
return false;
}
}
/**
* Disconnect from all devices
*/
void disconnect_all(MCUController* controller) {
// Stop all running threads
stop_current_mode(controller);
// Close serial connections
if (controller->mattress_ser) {
serial_close(controller->mattress_ser);
controller->mattress_ser = NULL;
}
if (controller->sensor_ser) {
serial_close(controller->sensor_ser);
controller->sensor_ser = NULL;
}
if (controller->remote_ser) {
serial_close(controller->remote_ser);
controller->remote_ser = NULL;
}
}
/**
* Read pressure data from sensor
*/
bool read_pressure_data(MCUController* controller, float* data, size_t data_size) {
if (!controller->sensor_ser) {
printf("Sensor not connected\n");
return false;
}
// Send command to request data
uint8_t cmd[] = {0xAA, 0x01, 0xBB};
serial_write(controller->sensor_ser, cmd, sizeof(cmd));
// Wait for response
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100000000; // 100ms
nanosleep(&ts, NULL);
// Calculate expected data size (1024 float values = 4096 bytes)
size_t expected_bytes = data_size * sizeof(float);
// Check if enough data is available
if (serial_available(controller->sensor_ser) >= expected_bytes) {
// Read raw bytes
uint8_t raw_data[expected_bytes];
size_t bytes_read = serial_read(controller->sensor_ser, raw_data, expected_bytes);
if (bytes_read == expected_bytes) {
// Convert raw bytes to float array
memcpy(data, raw_data, expected_bytes);
return true;
} else {
printf("Incomplete data: got %zu bytes, expected %zu\n", bytes_read, expected_bytes);
}
} else {
printf("Insufficient data: %d bytes available\n", serial_available(controller->sensor_ser));
}
return false;
}
/**
* Preprocess the raw sensor data
*/
void preprocess_data(const float* raw_data, float* processed_data, size_t data_size) {
const float clip_min = 0.0f;
const float clip_max = 300.0f;
const float epsilon = 1e-6f;
// Calculate mean and std_dev
float sum = 0.0f;
float sum_sq = 0.0f;
// First pass: calculate mean
for (size_t i = 0; i < data_size; i++) {
// Apply clipping
float value = raw_data[i];
if (value < clip_min) value = clip_min;
if (value > clip_max) value = clip_max;
// Sqrt(x+1)
value = sqrtf(value + 1.0f);
// Accumulate for mean calculation
sum += value;
sum_sq += value * value;
}
float mean = sum / data_size;
float variance = (sum_sq / data_size) - (mean * mean);
float std_dev = sqrtf(variance) + epsilon;
// Second pass: apply normalization, tanh, and scaling
for (size_t i = 0; i < data_size; i++) {
// Apply clipping
float value = raw_data[i];
if (value < clip_min) value = clip_min;
if (value > clip_max) value = clip_max;
// Sqrt(x+1)
value = sqrtf(value + 1.0f);
// Normalize
value = (value - mean) / std_dev;
// Apply tanh
value = tanhf(value);
// Scale to [0,1]
value = (value + 1.0f) / 2.0f;
processed_data[i] = value;
}
}
/**
* Detect sleep posture from sensor data
*/
PostureResult detect_posture(MCUController* controller) {
PostureResult result = {0};
result.success = false;
if (!controller->model_loaded) {
printf("Model not loaded\n");
return result;
}
// Read pressure data
float raw_data[1024];
if (!read_pressure_data(controller, raw_data, 1024)) {
printf("Failed to read pressure data\n");
return result;
}
// Preprocess data
float processed_data[1024];
preprocess_data(raw_data, processed_data, 1024);
// Copy data to input tensor
if (controller->is_quantized) {
// For quantized model, quantize the input
int8_t* input_data = controller->input_tensor->data.int8;
float input_scale = controller->input_tensor->params.scale;
int input_zero_point = controller->input_tensor->params.zero_point;
for (int i = 0; i < 1024; i++) {
input_data[i] = (int8_t)(processed_data[i] / input_scale + input_zero_point);
}
} else {
// For float model, copy directly
float* input_data = controller->input_tensor->data.f;
memcpy(input_data, processed_data, 1024 * sizeof(float));
}
// Run inference
TfLiteStatus invoke_status = controller->interpreter->Invoke();
if (invoke_status != kTfLiteOk) {
printf("Invoke failed\n");
return result;
}
// Get output
float output_values[3]; // Assuming 3 classes
if (controller->is_quantized) {
// Dequantize output for quantized model
int8_t* output_data = controller->output_tensor->data.int8;
float output_scale = controller->output_tensor->params.scale;
int output_zero_point = controller->output_tensor->params.zero_point;
for (int i = 0; i < 3; i++) {
output_values[i] = (output_data[i] - output_zero_point) * output_scale;
}
} else {
// Copy output for float model
float* output_data = controller->output_tensor->data.f;
memcpy(output_values, output_data, 3 * sizeof(float));
}
// Find predicted class and confidence
int predicted_class = 0;
float max_confidence = output_values[0];
for (int i = 1; i < 3; i++) {
if (output_values[i] > max_confidence) {
max_confidence = output_values[i];
predicted_class = i;
}
}
// Set result
result.posture_index = predicted_class;
result.confidence = max_confidence * 100.0f; // Convert to percentage
result.success = true;
// Store current posture and confidence
controller->current_posture = predicted_class;
controller->confidence = result.confidence;
// Map class to posture name for debugging
const char* posture_names[] = {"Supine", "Left Side", "Right Side"};
printf("Detected posture: %s (confidence: %.1f%%)\n",
posture_names[predicted_class], result.confidence);
return result;
}
/**
* Send command to the mattress controller
*/
bool send_mattress_command(MCUController* controller, uint8_t mode_byte, uint8_t* args, size_t args_length) {
if (!controller->mattress_ser) {
printf("Mattress not connected\n");
return false;
}
// Create command packet
uint8_t packet[20]; // Allocate enough space for command
size_t packet_index = 0;
// Start byte
packet[packet_index++] = 0xAA;
// Mode byte
packet[packet_index++] = mode_byte;
// Parameter bytes
for (size_t i = 0; i < args_length && packet_index < 14; i++) {
packet[packet_index++] = args[i];
}
// Pad to fixed length if needed
while (packet_index < 14) {
packet[packet_index++] = 0x00;
}
// Add ID bytes
for (int i = 0; i < 4; i++) {
packet[packet_index++] = controller->id_bytes[i];
}
// End byte
packet[packet_index++] = 0xBB;
// Send packet
if (serial_write(controller->mattress_ser, packet, packet_index)) {
printf("Sent mattress command: mode=%d\n", mode_byte);
return true;
} else {
printf("Failed to send mattress command\n");
return false;
}
}
/**
* Stop the current operating mode
*/
bool stop_current_mode(MCUController* controller) {
// Set stop event
controller->stop_event = true;
// Wait for threads to finish (simple implementation)
// In a real-time system, you would use proper thread joining with timeout
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
// Reset thread count and stop event
pthread_mutex_lock(&controller->thread_mutex);
controller->thread_count = 0;
controller->stop_event = false;
pthread_mutex_unlock(&controller->thread_mutex);
// Send stop command to mattress
if (controller->mattress_ser) {
uint8_t args[1] = {0};
send_mattress_command(controller, MODE_MANUAL, args, 0);
}
return true;
}
/**
* Run adaptive mode based on posture detection
*/
bool run_adaptive_mode(MCUController* controller, int detection_interval, int runtime) {
if (!controller->mattress_ser || !controller->sensor_ser) {
printf("Both mattress and sensor must be connected\n");
return false;
}
// Stop any current mode
stop_current_mode(controller);
// Create thread arguments (allocated on heap to avoid stack issues)
typedef struct {
MCUController* controller;
int detection_interval;
int runtime;
} AdaptiveArgs;
AdaptiveArgs* args = (AdaptiveArgs*)malloc(sizeof(AdaptiveArgs));
args->controller = controller;
args->detection_interval = detection_interval;
args->runtime = runtime;
// Start adaptive thread
pthread_mutex_lock(&controller->thread_mutex);
if (controller->thread_count >= 5) {
printf("Too many threads running\n");
pthread_mutex_unlock(&controller->thread_mutex);
free(args);
return false;
}
pthread_t thread;
if (pthread_create(&thread, NULL, adaptive_thread_func, args) != 0) {
printf("Failed to create adaptive thread\n");
pthread_mutex_unlock(&controller->thread_mutex);
free(args);
return false;
}
controller->running_threads[controller->thread_count++] = thread;
pthread_mutex_unlock(&controller->thread_mutex);
printf("Started adaptive mode\n");
return true;
}
// Implementation of adaptive thread function
void* adaptive_thread_func(void* arg) {
typedef struct {
MCUController* controller;
int detection_interval;
int runtime;
} AdaptiveArgs;
AdaptiveArgs* args = (AdaptiveArgs*)arg;
MCUController* controller = args->controller;
int detection_interval = args->detection_interval;
int runtime = args->runtime;
// Free args struct - we've copied the data
free(args);
printf("Adaptive thread starting (interval=%d, runtime=%d)\n", detection_interval, runtime);
int adjustment_count = 0;
time_t start_time = time(NULL);
while (!controller->stop_event) {
// Check if runtime exceeded
time_t current_time = time(NULL);
int elapsed_time = (int)(current_time - start_time);
if (elapsed_time >= runtime) {
printf("Adaptive mode completed after %d seconds\n", elapsed_time);
break;
}
// Calculate progress percentage
int progress = (elapsed_time * 100) / runtime;
printf("Adaptive mode progress: %d%%\n", progress);
// Detect current posture
PostureResult result = detect_posture(controller);
if (result.success) {
int posture_index = result.posture_index;
float confidence = result.confidence;
// Only adjust if confidence is high enough
if (confidence >= 70.0f) {
uint8_t args[4];
// Adjust mattress based on posture
if (posture_index == POSTURE_SUPINE) {
// Supine: neutral position
args[0] = 50;
args[1] = 50;
args[2] = 50;
args[3] = 50;
} else if (posture_index == POSTURE_LEFT) {
// Left side: adjust for comfort
args[0] = 70;
args[1] = 40;
args[2] = 70;
args[3] = 40;
} else if (posture_index == POSTURE_RIGHT) {
// Right side: adjust for comfort
args[0] = 40;
args[1] = 70;
args[2] = 40;
args[3] = 70;
}
send_mattress_command(controller, MODE_MANUAL, args, 4);
adjustment_count++;
const char* posture_names[] = {"Supine", "Left Side", "Right Side"};
printf("Adjusted mattress for %s (adjustment #%d)\n",
posture_names[posture_index], adjustment_count);
} else {
printf("Low confidence (%.1f%%), no adjustment made\n", confidence);
}
} else {
printf("Posture detection failed, no adjustment made\n");
}
// Wait for next detection interval or until stopped
for (int i = 0; i < detection_interval && !controller->stop_event; i++) {
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
}
}
// Reset to manual mode when finished
if (!controller->stop_event) {
uint8_t args[1] = {0};
send_mattress_command(controller, MODE_MANUAL, args, 0);
printf("Adaptive mode completed\n");
} else {
printf("Adaptive mode stopped\n");
}
return NULL;
}
/**
* Process a command from the remote controller
*/
bool process_remote_command(MCUController* controller, uint8_t command_byte, uint8_t param1, uint8_t param2) {
printf("Processing remote command: cmd=%d, param1=%d, param2=%d\n", command_byte, param1, param2);
switch (command_byte) {
case CMD_STOP:
return stop_current_mode(controller);
case CMD_ADAPTIVE_MODE: {
// param1: interval in minutes, param2: runtime in hours
int interval = param1 > 0 ? param1 * 60 : 300;
int runtime = param2 > 0 ? param2 * 3600 : 3600;
return run_adaptive_mode(controller, interval, runtime);
}
case CMD_PREVENTION_MODE: {
// Implementation would be similar to adaptive mode
printf("Prevention mode not fully implemented\n");
return false;
}
case CMD_ABAB_MODE: {
// Implementation would be similar to adaptive mode
printf("ABAB mode not fully implemented\n");
return false;
}
case CMD_TURNING_MODE: {
// Implementation would be similar to adaptive mode
printf("Turning mode not fully implemented\n");
return false;
}
case CMD_FLOATING_MODE: {
// Implementation would be similar to adaptive mode
printf("Floating mode not fully implemented\n");
return false;
}
case CMD_DETECT_POSTURE: {
// Just detect and report posture
PostureResult result = detect_posture(controller);
return result.success;
}
default:
printf("Unknown command: %d\n", command_byte);
return false;
}
}
/**
* Listen for remote controller commands
*/
void* remote_listener_thread(void* arg) {
MCUController* controller = (MCUController*)arg;
// Buffer for received bytes
uint8_t buffer[16];
int buffer_index = 0;
while (1) {
// Check if data is available
if (serial_available(controller->remote_ser) > 0) {
// Read one byte
uint8_t byte;
if (serial_read(controller->remote_ser, &byte, 1) == 1) {
buffer[buffer_index++] = byte;
// Check for complete command (3 bytes)
if (buffer_index >= 3) {
// Process command
uint8_t cmd = buffer[0];
uint8_t param1 = buffer[1];
uint8_t param2 = buffer[2];
printf("Received command: %d %d %d\n", cmd, param1, param2);
// Process the command
process_remote_command(controller, cmd, param1, param2);
// Clear buffer
buffer_index = 0;
}
}
}
// Small delay to prevent CPU overuse
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 10000000; // 10ms
nanosleep(&ts, NULL);
}
return NULL;
}
/**
* Start remote controller listener
*/
bool start_remote_listener(MCUController* controller, const char* port, int baudrate) {
// Connect to remote controller
if (controller->remote_ser) {
serial_close(controller->remote_ser);
controller->remote_ser = NULL;
}
if (!serial_open(&controller->remote_ser, port, baudrate)) {
printf("Failed to connect to remote controller on %s\n", port);
return false;
}
printf("Connected to remote controller on %s\n", port);
// Start listener thread
pthread_t thread;
if (pthread_create(&thread, NULL, remote_listener_thread, controller) != 0) {
printf("Failed to create remote listener thread\n");
serial_close(controller->remote_ser);
controller->remote_ser = NULL;
return false;
}
// Make thread detached so it cleans up automatically
pthread_detach(thread);
return true;
}
/**
* Main entry point
*/
int main() {
// Create controller
MCUController controller;
printf("Initializing MCU Mattress Controller...\n");
// Initialize controller
if (!controller_init(&controller)) {
printf("Failed to initialize controller\n");
return 1;
}
// Connect to devices
connect_mattress(&controller, MATTRESS_PORT, 115200);
connect_sensor(&controller, SENSOR_PORT, 115200);
// Start remote controller listener
start_remote_listener(&controller, REMOTE_PORT, 9600);
// Main loop
printf("Controller running...\n");
// In a real implementation, you might need some form of
// application main loop here. For simplicity, we just sleep.
while (1) {
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
}
// Clean up (unreachable in this simple example)
disconnect_all(&controller);
return 0;
}
// Platform-specific serial communication implementations would go here
// These are just stubs and would need to be implemented for your specific platform
bool serial_open(void** handle, const char* port, int baudrate) {
// Implementation depends on your platform (UART, USART, etc.)
printf("Opening serial port %s at %d baud\n", port, baudrate);
*handle = malloc(1); // Dummy handle
return true;
}
void serial_close(void* handle) {
// Implementation depends on your platform
if (handle) free(handle);
}
bool serial_write(void* handle, const uint8_t* data, size_t length) {
// Implementation depends on your platform
printf("Writing %zu bytes to serial port\n", length);
return true;
}
int serial_read(void* handle, uint8_t* buffer, size_t max_length) {
// Implementation depends on your platform
return 0; // Return number of bytes read
}
int serial_available(void* handle) {
// Implementation depends on your platform
return 0; // Return number of bytes available
}