FreeRTOS-Architektur¶
Beide MCU-Knoten verwenden dasselbe Dual-Core-Pattern:
Core 0 (Arduino loop()):
- micro-ROS Executor: spin_some() fuer Publisher und Subscriber
- Servo-I2C-Writes (nur Sensor-Knoten, PCA9685)
- ~500 Hz Zyklusrate
Core 1 (FreeRTOS Task):
- Drive-Knoten: PID-Regelung (50 Hz), Encoder-Polling, CAN-Sends
- Sensor-Knoten: IMU-Reads (50 Hz), Ultraschall (10 Hz), Cliff (20 Hz),
Batterie (2 Hz), CAN-Sends
- Inter-Core Heartbeat-Check (Watchdog)
Die Core-Zuordnung wird explizit festgelegt: -DARDUINO_RUNNING_CORE=0 in platformio.ini setzt Arduino loop() auf Core 0 (Board-Default waere Core 1). xTaskCreatePinnedToCore(..., 1) fixiert die Echtzeit-Tasks auf Core 1.
Drive-Knoten (Fahrkern)¶
| Core | Aufgabe | Rate |
|---|---|---|
| 0 | micro-ROS Executor: Sub /cmd_vel, /hardware, /battery_shutdown; Pub /odom | 20 Hz Pub |
| 0 | LED-Steuerung (MOSFET-PWM) | bei Aenderung |
| 1 | PID-Regelschleife, Encoder-Auswertung | 50 Hz |
| 1 | CAN-Send: Odom, Rad-Geschwindigkeit, PWM, Heartbeat | 1–20 Hz |
| 1 | CAN-Receive: Cliff-Stop (0x120), Battery-Shutdown (0x141) | non-blocking |
Sensor-Knoten (Sensor- und Sicherheitsbasis)¶
| Core | Aufgabe | Rate |
|---|---|---|
| 0 | micro-ROS Executor: Sub /servo_cmd, /hardware; Pub /range, /cliff, /imu, /battery | je nach Sensor |
| 0 | Servo-I2C-Write (PCA9685) | 10 Hz |
| 1 | Cliff-Sensor (MH-B IR) | 20 Hz |
| 1 | Ultraschall (HC-SR04, ISR) | 10 Hz |
| 1 | IMU (MPU6050, I2C) | 50 Hz |
| 1 | Batterie (INA260, I2C) | 2 Hz |
| 1 | CAN-Send: Range, Cliff, IMU, Batterie, Heartbeat | je nach Sensor |
Synchronisation¶
SharedData-Mutex¶
SharedData-Struct mit SemaphoreHandle_t schuetzt den Datenaustausch zwischen Core 0 und Core 1. Beide Cores verwenden xSemaphoreTake()/xSemaphoreGive() mit kurzem Timeout.
I2C-Mutex (nur Sensor-Knoten)¶
Drei Geraete teilen den I2C-Bus (400 kHz):
| Geraet | Adresse | Zugriff | Core |
|---|---|---|---|
| MPU6050 | 0x68 | Read (50 Hz) | Core 1 |
| INA260 | 0x40 | Read (2 Hz) | Core 1 |
| PCA9685 | 0x41 (A0-Loetbruecke) | Write (bei Aenderung) | Core 0 |
i2c_mutex mit 5 ms Timeout verhindert gleichzeitige Bus-Zugriffe.
Init-Reihenfolge: PCA9685 vor MPU6050 und INA260 initialisieren. delay(2000) vor Wire.begin().
Watchdog¶
Core 0 prueft periodisch core1_heartbeat (Zeitstempel). Bei Ausbleiben > 50 Zyklen (~1 s) ohne Core-1-Update wird ein Fehler gemeldet und die Motoren gestoppt.