Урок 27 | Проектная деятельность в робототехнике
От механических конструкций к мыслящим системам — процесс программирования, где:
🤖 Робот = Набор взаимодействующих модулей
Аналогия с биологией:
Сложность монолитной системы: $C_{mono} = n^2$
Сложность модульной системы: $C_{mod} = n \log n$
где $n$ — количество функций
Пример: При 16 функциях:
Выигрыш в 4 раза!
| 🎭 Актер (Модуль) | 🎬 Роль | 📝 Сценарий (Функции) |
|---|---|---|
| 🚗 Locomotion | Движение | move(), turn(), stop() |
| 👁️ Perception | Восприятие | scan(), detect(), measure() |
| 🤖 Action | Действие | grab(), manipulate(), release() |
| 📡 Communication | Общение | send(), receive(), broadcast() |
| 🧠 Coordination | Координация | plan(), synchronize(), resolve() |
| 🎛️ Control | Управление | initialize(), monitor(), shutdown() |
class LocomotionModule {
private:
Motor leftMotor;
Motor rightMotor;
Encoder leftEncoder;
Encoder rightEncoder;
IMU orientationSensor;
float currentSpeed = 0;
float currentDirection = 0;
Position currentPosition;
public:
// Базовые функции движения
void moveForward(float speed, float distance = 0);
void moveBackward(float speed, float distance = 0);
void turnLeft(float angle);
void turnRight(float angle);
void stop();
// Продвинутые функции
void moveToPosition(Position target);
void followPath(Path trajectory);
void maintainFormation(Robot* neighbors[], int count);
// Сенсорные функции
Position getCurrentPosition();
float getCurrentSpeed();
float getCurrentDirection();
};
где:
void moveToPosition(float targetX, float targetY) {
// Вычисляем расстояние и угол к цели
float dx = targetX - currentPosition.x;
float dy = targetY - currentPosition.y;
float distance = sqrt(dx*dx + dy*dy);
float targetAngle = atan2(dy, dx) * 180.0 / PI;
// Поворачиваемся к цели
float angleError = targetAngle - currentPosition.angle;
while (abs(angleError) > 180) {
angleError += (angleError > 0) ? -360 : 360;
}
if (abs(angleError) > 5) { // Допуск 5 градусов
if (angleError > 0) {
turnLeft(abs(angleError));
} else {
turnRight(abs(angleError));
}
}
// Движемся к цели
moveForward(CRUISE_SPEED, distance);
}
struct Position {
float x, y; // Координаты в метрах
float angle; // Ориентация в градусах
float distanceTo(Position other) {
float dx = x - other.x;
float dy = y - other.y;
return sqrt(dx*dx + dy*dy);
}
float angleTo(Position other) {
float dx = other.x - x;
float dy = other.y - y;
return atan2(dy, dx) * 180.0 / PI;
}
};
class NavigationSystem {
private:
Position home = {0, 0, 0};
Position current = {0, 0, 0};
public:
void updatePosition() {
// Одометрия по энкодерам
float leftDistance = leftEncoder.getDistance();
float rightDistance = rightEncoder.getDistance();
float deltaDistance = (leftDistance + rightDistance) / 2.0;
float deltaAngle = (rightDistance - leftDistance) / WHEEL_BASE;
current.x += deltaDistance * cos(current.angle * PI / 180.0);
current.y += deltaDistance * sin(current.angle * PI / 180.0);
current.angle += deltaAngle * 180.0 / PI;
// Нормализация угла
while (current.angle > 180) current.angle -= 360;
while (current.angle < -180) current.angle += 360;
}
};
class PerceptionModule {
private:
UltrasonicSensor frontSensor;
UltrasonicSensor leftSensor;
UltrasonicSensor rightSensor;
Camera visionSystem;
IMU inertialSensor;
struct SensorData {
float distances[3]; // front, left, right
bool obstacleDetected;
float brightness;
int objectsDetected;
float acceleration[3];
unsigned long timestamp;
};
SensorData currentData;
public:
void updateSensors();
bool isPathClear(float direction, float distance);
Position findNearestObstacle();
Robot* detectNearbyRobots();
bool recognizeObject(ObjectType type);
};
class SensorFilter {
private:
static const int FILTER_SIZE = 5;
float buffer[FILTER_SIZE];
int index = 0;
bool filled = false;
public:
float filter(float newValue) {
buffer[index] = newValue;
index = (index + 1) % FILTER_SIZE;
if (index == 0) filled = true;
if (!filled) return newValue;
// Медианная фильтрация
float sorted[FILTER_SIZE];
memcpy(sorted, buffer, sizeof(buffer));
// Простая сортировка
for (int i = 0; i < FILTER_SIZE-1; i++) {
for (int j = i+1; j < FILTER_SIZE; j++) {
if (sorted[i] > sorted[j]) {
float temp = sorted[i];
sorted[i] = sorted[j];
sorted[j] = temp;
}
}
}
return sorted[FILTER_SIZE/2]; // Медиана
}
};
Robot* PerceptionModule::detectNearbyRobots() {
static Robot detectedRobots[MAX_ROBOTS];
int robotCount = 0;
// Сканируем 360 градусов
for (int angle = 0; angle < 360; angle += 30) {
// Поворачиваем сенсор (если есть сервопривод)
sensorServo.write(angle);
delay(100); // Время стабилизации
float distance = frontSensor.getDistance();
// Проверяем, подходит ли расстояние для робота
if (distance > 10 && distance < 200) { // 10см - 2м
// Отправляем ping другому роботу
communication.sendPing(angle);
// Ждем ответ
if (communication.waitForResponse(1000)) { // 1 сек таймаут
Robot newRobot;
newRobot.distance = distance;
newRobot.angle = angle;
newRobot.id = communication.getLastResponderId();
detectedRobots[robotCount++] = newRobot;
if (robotCount >= MAX_ROBOTS) break;
}
}
}
// Возвращаем сенсор в исходное положение
sensorServo.write(90);
return (robotCount > 0) ? detectedRobots : nullptr;
}
// Определение типов сообщений
enum MessageType {
PING = 0x01, // Проверка связи
PONG = 0x02, // Ответ на ping
COMMAND = 0x10, // Команда выполнения
DATA = 0x20, // Передача данных
STATUS = 0x30, // Статус робота
ERROR = 0x40, // Сообщение об ошибке
SYNC = 0x50, // Синхронизация времени
ACK = 0x60 // Подтверждение получения
};
// Структура сообщения (32 байта)
struct RobotMessage {
uint8_t startByte = 0xAA; // Маркер начала
uint8_t senderID; // ID отправителя
uint8_t receiverID; // ID получателя
uint8_t messageType; // Тип сообщения
uint16_t sequenceNumber; // Номер последовательности
uint16_t payloadLength; // Длина полезной нагрузки
uint8_t payload[20]; // Полезная нагрузка
uint16_t checksum; // Контрольная сумма
uint8_t endByte = 0x55; // Маркер конца
};
class ReliableCommunication {
private:
static const int MAX_RETRIES = 3;
static const int ACK_TIMEOUT = 1000; // миллисекунды
uint16_t lastSequenceNumber = 0;
public:
bool sendMessageReliable(uint8_t receiverID, MessageType type,
uint8_t* data, uint8_t dataLength) {
RobotMessage msg;
msg.senderID = MY_ROBOT_ID;
msg.receiverID = receiverID;
msg.messageType = type;
msg.sequenceNumber = ++lastSequenceNumber;
msg.payloadLength = dataLength;
if (dataLength <= 20) {
memcpy(msg.payload, data, dataLength);
}
msg.checksum = calculateChecksum(msg);
// Попытки отправки с повтором
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
// Отправляем сообщение
bluetooth.write((uint8_t*)&msg, sizeof(msg));
// Ждем подтверждение
unsigned long startTime = millis();
while (millis() - startTime < ACK_TIMEOUT) {
if (checkForAcknowledgment(msg.sequenceNumber)) {
return true; // Успешно отправлено
}
delay(10);
}
Serial.print("Retry attempt ");
Serial.println(attempt + 1);
}
Serial.println("Message delivery failed");
return false; // Не удалось отправить
}
private:
uint16_t calculateChecksum(const RobotMessage& msg) {
uint16_t sum = 0;
uint8_t* ptr = (uint8_t*)&msg;
for (int i = 0; i < sizeof(msg) - 2; i++) { // -2 для самой суммы
sum += ptr[i];
}
return sum;
}
};
class LinkQualityMonitor {
private:
struct LinkStats {
int messagesSent = 0;
int messagesReceived = 0;
int messagesFailed = 0;
unsigned long totalLatency = 0;
unsigned long lastActivity = 0;
};
LinkStats stats[MAX_ROBOTS];
public:
float getPacketLossRate(uint8_t robotID) {
LinkStats& s = stats[robotID];
if (s.messagesSent == 0) return 0.0;
return (float)s.messagesFailed / s.messagesSent * 100.0;
}
float getAverageLatency(uint8_t robotID) {
LinkStats& s = stats[robotID];
if (s.messagesReceived == 0) return 0.0;
return (float)s.totalLatency / s.messagesReceived;
}
bool isLinkAlive(uint8_t robotID) {
return (millis() - stats[robotID].lastActivity) < 5000; // 5 сек
}
void printNetworkStatus() {
Serial.println("=== Network Status ===");
for (int i = 0; i < MAX_ROBOTS; i++) {
if (stats[i].messagesSent > 0) {
Serial.print("Robot ");
Serial.print(i);
Serial.print(": Loss=");
Serial.print(getPacketLossRate(i));
Serial.print("%, Latency=");
Serial.print(getAverageLatency(i));
Serial.print("ms, Alive=");
Serial.println(isLinkAlive(i) ? "Yes" : "No");
}
}
}
};
class TaskAuctionSystem {
private:
struct Task {
int taskID;
Position location;
int priority;
int estimatedTime;
bool assigned = false;
uint8_t assignedRobot = 255;
};
std::vector<Task> taskQueue;
public:
void startAuction(Task newTask) {
// Добавляем задачу в очередь
taskQueue.push_back(newTask);
// Запрашиваем ставки от всех роботов
AuctionMessage auctionMsg;
auctionMsg.taskID = newTask.taskID;
auctionMsg.taskX = newTask.location.x;
auctionMsg.taskY = newTask.location.y;
auctionMsg.priority = newTask.priority;
// Отправляем всем роботам
for (int robotID = 1; robotID <= MAX_ROBOTS; robotID++) {
if (robotID != MY_ROBOT_ID && isRobotOnline(robotID)) {
communication.sendMessage(robotID, AUCTION_REQUEST,
(uint8_t*)&auctionMsg, sizeof(auctionMsg));
}
}
// Ждем ставки и выбираем лучшую
collectBidsAndAssign(newTask.taskID);
}
private:
void collectBidsAndAssign(int taskID) {
struct Bid {
uint8_t robotID;
float cost; // Меньше = лучше
bool received = false;
};
Bid bids[MAX_ROBOTS];
unsigned long auctionStart = millis();
const unsigned long AUCTION_TIMEOUT = 3000; // 3 секунды
// Собираем ставки
while (millis() - auctionStart < AUCTION_TIMEOUT) {
RobotMessage msg;
if (communication.receiveMessage(msg) &&
msg.messageType == AUCTION_BID) {
BidMessage* bid = (BidMessage*)msg.payload;
if (bid->taskID == taskID) {
bids[msg.senderID].robotID = msg.senderID;
bids[msg.senderID].cost = bid->bidCost;
bids[msg.senderID].received = true;
Serial.print("Bid from robot ");
Serial.print(msg.senderID);
Serial.print(": cost=");
Serial.println(bid->bidCost);
}
}
delay(10);
}
// Выбираем лучшую ставку (минимальная стоимость)
float bestCost = 99999;
uint8_t winnerID = 255;
for (int i = 0; i < MAX_ROBOTS; i++) {
if (bids[i].received && bids[i].cost < bestCost) {
bestCost = bids[i].cost;
winnerID = bids[i].robotID;
}
}
// Назначаем задачу победителю
if (winnerID != 255) {
assignTaskToRobot(taskID, winnerID);
notifyAuctionResult(taskID, winnerID);
}
}
};
class SynchronizedAction {
private:
unsigned long synchronizedTime = 0;
bool timeSynchronized = false;
public:
void scheduleSynchronizedAction(unsigned long delayMs, ActionType action) {
// Время выполнения = текущее время + задержка
unsigned long executionTime = getSynchronizedTime() + delayMs;
// Отправляем команду синхронизации всем роботам
SyncActionMessage syncMsg;
syncMsg.actionType = action;
syncMsg.executionTime = executionTime;
syncMsg.coordinatorID = MY_ROBOT_ID;
broadcast(SYNC_ACTION, (uint8_t*)&syncMsg, sizeof(syncMsg));
// Сами тоже ждем этого времени
waitUntilTime(executionTime);
executeAction(action);
}
void synchronizeTime() {
if (isCoordinator()) {
// Координатор рассылает свое время
SyncTimeMessage timeMsg;
timeMsg.currentTime = millis();
timeMsg.coordinatorID = MY_ROBOT_ID;
broadcast(TIME_SYNC, (uint8_t*)&timeMsg, sizeof(timeMsg));
synchronizedTime = timeMsg.currentTime;
timeSynchronized = true;
}
}
void waitUntilTime(unsigned long targetTime) {
while (getSynchronizedTime() < targetTime) {
delay(1);
// Обрабатываем входящие сообщения во время ожидания
processIncomingMessages();
}
}
unsigned long getSynchronizedTime() {
if (!timeSynchronized) return millis();
// Возвращаем локальное время относительно синхронизированной точки
return synchronizedTime + millis();
}
};
class CollisionAvoidance {
private:
struct Velocity {
float x, y;
Velocity operator+(const Velocity& other) const {
return {x + other.x, y + other.y};
}
Velocity operator*(float scalar) const {
return {x * scalar, y * scalar};
}
float magnitude() const {
return sqrt(x*x + y*y);
}
};
struct Robot {
Position position;
Velocity velocity;
float radius;
uint8_t id;
};
std::vector<Robot> nearbyRobots;
public:
Velocity computeSafeVelocity(Velocity preferredVelocity, Position myPosition,
float myRadius, float maxSpeed) {
// Если нет соседей, движемся с предпочтительной скоростью
if (nearbyRobots.empty()) {
return preferredVelocity;
}
std::vector<Velocity> candidateVelocities;
// Генерируем кандидатов скоростей
for (float angle = 0; angle < 2*PI; angle += PI/8) {
for (float speed = 0; speed <= maxSpeed; speed += maxSpeed/5) {
Velocity candidate = {
speed * cos(angle),
speed * sin(angle)
};
if (isVelocitySafe(candidate, myPosition, myRadius)) {
candidateVelocities.push_back(candidate);
}
}
}
// Выбираем ближайшую к предпочтительной скорости
Velocity bestVelocity = {0, 0};
float minDifference = 99999;
for (const auto& candidate : candidateVelocities) {
float diff = sqrt(pow(candidate.x - preferredVelocity.x, 2) +
pow(candidate.y - preferredVelocity.y, 2));
if (diff < minDifference) {
minDifference = diff;
bestVelocity = candidate;
}
}
return bestVelocity;
}
private:
bool isVelocitySafe(Velocity velocity, Position myPosition, float myRadius) {
const float TIME_HORIZON = 3.0; // секунды в будущее
for (const auto& robot : nearbyRobots) {
// Предсказываем позиции через TIME_HORIZON секунд
Position myFuturePos = {
myPosition.x + velocity.x * TIME_HORIZON,
myPosition.y + velocity.y * TIME_HORIZON,
myPosition.angle
};
Position robotFuturePos = {
robot.position.x + robot.velocity.x * TIME_HORIZON,
robot.position.y + robot.velocity.y * TIME_HORIZON,
robot.position.angle
};
// Проверяем расстояние между предсказанными позициями
float futureDistance = myFuturePos.distanceTo(robotFuturePos);
float minSafeDistance = myRadius + robot.radius + 0.2; // +20см запас
if (futureDistance < minSafeDistance) {
return false; // Небезопасная скорость
}
}
return true; // Безопасная скорость
}
};
class SystemTester {
private:
enum TestLevel {
UNIT_TEST, // Тестирование отдельных функций
MODULE_TEST, // Тестирование модулей
INTEGRATION_TEST, // Тестирование взаимодействия модулей
SYSTEM_TEST // Тестирование всей системы
};
public:
bool runAllTests() {
Serial.println("=== Starting System Tests ===");
// Уровень 1: Тестирование базовых функций
if (!runUnitTests()) {
Serial.println("FAIL: Unit tests failed");
return false;
}
// Уровень 2: Тестирование модулей
if (!runModuleTests()) {
Serial.println("FAIL: Module tests failed");
return false;
}
// Уровень 3: Тестирование интеграции
if (!runIntegrationTests()) {
Serial.println("FAIL: Integration tests failed");
return false;
}
// Уровень 4: Системное тестирование
if (!runSystemTests()) {
Serial.println("FAIL: System tests failed");
return false;
}
Serial.println("SUCCESS: All tests passed!");
return true;
}
private:
bool runUnitTests() {
Serial.println("Running unit tests...");
// Тест движения
TEST_ASSERT(testMovementFunctions(), "Movement functions");
// Тест сенсоров
TEST_ASSERT(testSensorFunctions(), "Sensor functions");
// Тест коммуникации
TEST_ASSERT(testCommunicationFunctions(), "Communication functions");
return true;
}
bool testMovementFunctions() {
// Тест базового движения
locomotion.moveForward(50, 100); // 50% скорость, 100мм
delay(2000);
Position newPos = navigation.getCurrentPosition();
float distance = abs(newPos.distanceTo(Position{0, 0, 0}));
return (distance > 80 && distance < 120); // Допуск ±20мм
}
bool testSensorFunctions() {
// Тест ультразвукового датчика
float distance1 = perception.frontSensor.getDistance();
delay(100);
float distance2 = perception.frontSensor.getDistance();
// Проверяем стабильность показаний
return (abs(distance1 - distance2) < 5); // Допуск 5мм
}
bool testCommunicationFunctions() {
// Тест отправки/получения сообщения
uint8_t testData[] = {0x12, 0x34, 0x56, 0x78};
bool sent = communication.sendMessage(BROADCAST_ID, DATA,
testData, sizeof(testData));
// Для самотестирования отправляем себе
return sent;
}
};
// Макрос для упрощения тестирования
#define TEST_ASSERT(condition, name) \
if (!(condition)) { \
Serial.print("FAIL: "); \
Serial.println(name); \
return false; \
} else { \
Serial.print("PASS: "); \
Serial.println(name); \
}
class DebugSystem {
private:
struct DebugMessage {
unsigned long timestamp;
uint8_t level; // INFO=0, WARNING=1, ERROR=2, CRITICAL=3
char message[100];
};
DebugMessage debugBuffer[50];
int bufferIndex = 0;
bool bufferFull = false;
public:
void log(uint8_t level, const char* format, ...) {
va_list args;
va_start(args, format);
DebugMessage& msg = debugBuffer[bufferIndex];
msg.timestamp = millis();
msg.level = level;
vsnprintf(msg.message, sizeof(msg.message), format, args);
// Вывод в Serial
const char* levelNames[] = {"INFO", "WARN", "ERR", "CRIT"};
Serial.print("[");
Serial.print(msg.timestamp);
Serial.print("] ");
Serial.print(levelNames[level]);
Serial.print(": ");
Serial.println(msg.message);
// Сохранение в буфер
bufferIndex = (bufferIndex + 1) % 50;
if (bufferIndex == 0) bufferFull = true;
va_end(args);
}
void printDebugHistory() {
Serial.println("=== Debug History ===");
int start = bufferFull ? bufferIndex : 0;
int count = bufferFull ? 50 : bufferIndex;
for (int i = 0; i < count; i++) {
int idx = (start + i) % 50;
DebugMessage& msg = debugBuffer[idx];
Serial.print("[");
Serial.print(msg.timestamp);
Serial.print("] ");
Serial.println(msg.message);
}
}
void monitorPerformance() {
static unsigned long lastCheck = 0;
static int lastLoopCount = 0;
static int currentLoopCount = 0;
currentLoopCount++;
if (millis() - lastCheck >= 1000) { // Каждую секунду
int loopsPerSecond = currentLoopCount - lastLoopCount;
log(INFO, "Performance: %d loops/sec, Free RAM: %d bytes",
loopsPerSecond, getFreeRAM());
if (loopsPerSecond < 50) {
log(WARNING, "Low performance detected!");
}
lastCheck = millis();
lastLoopCount = currentLoopCount;
}
}
private:
int getFreeRAM() {
extern int __heap_start, *__brkval;
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}
};
// Глобальная система отладки
DebugSystem debug;
// Удобные макросы
#define DEBUG_INFO(fmt, ...) debug.log(0, fmt, ##__VA_ARGS__)
#define DEBUG_WARNING(fmt, ...) debug.log(1, fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) debug.log(2, fmt, ##__VA_ARGS__)
#define DEBUG_CRITICAL(fmt, ...) debug.log(3, fmt, ##__VA_ARGS__)
class SystemMonitor {
private:
struct SystemStats {
unsigned long uptime;
float cpuUsage;
int freeMemory;
float batteryVoltage;
int messagesPerSecond;
int errorsCount;
struct ModuleStatus {
bool locomotion;
bool perception;
bool communication;
bool coordination;
} modules;
};
SystemStats currentStats;
public:
void updateStats() {
currentStats.uptime = millis();
currentStats.freeMemory = getFreeRAM();
currentStats.batteryVoltage = readBatteryVoltage();
currentStats.cpuUsage = calculateCPUUsage();
// Проверяем статус модулей
currentStats.modules.locomotion = testLocomotionModule();
currentStats.modules.perception = testPerceptionModule();
currentStats.modules.communication = testCommunicationModule();
currentStats.modules.coordination = testCoordinationModule();
}
void printSystemStatus() {
Serial.println("=== System Status ===");
Serial.print("Uptime: ");
Serial.print(currentStats.uptime / 1000);
Serial.println(" seconds");
Serial.print("Free RAM: ");
Serial.print(currentStats.freeMemory);
Serial.println(" bytes");
Serial.print("Battery: ");
Serial.print(currentStats.batteryVoltage);
Serial.println(" V");
Serial.print("CPU Usage: ");
Serial.print(currentStats.cpuUsage);
Serial.println("%");
Serial.println("Module Status:");
Serial.print(" Locomotion: ");
Serial.println(currentStats.modules.locomotion ? "OK" : "FAIL");
Serial.print(" Perception: ");
Serial.println(currentStats.modules.perception ? "OK" : "FAIL");
Serial.print(" Communication: ");
Serial.println(currentStats.modules.communication ? "OK" : "FAIL");
Serial.print(" Coordination: ");
Serial.println(currentStats.modules.coordination ? "OK" : "FAIL");
}
bool isSystemHealthy() {
return (currentStats.batteryVoltage > 6.5 && // Минимальное напряжение
currentStats.freeMemory > 500 && // Минимум свободной памяти
currentStats.modules.locomotion && // Все модули работают
currentStats.modules.perception &&
currentStats.modules.communication);
}
private:
float readBatteryVoltage() {
int rawValue = analogRead(BATTERY_PIN);
return rawValue * (5.0 / 1023.0) * VOLTAGE_DIVIDER_RATIO;
}
float calculateCPUUsage() {
static unsigned long lastMeasure = 0;
static unsigned long lastIdle = 0;
unsigned long currentTime = millis();
unsigned long idleTime = getIdleTime(); // Функция подсчета простоя
if (lastMeasure != 0) {
unsigned long timeDiff = currentTime - lastMeasure;
unsigned long idleDiff = idleTime - lastIdle;
return (1.0 - (float)idleDiff / timeDiff) * 100.0;
}
lastMeasure = currentTime;
lastIdle = idleTime;
return 0.0;
}
};
🧠 “Архитектура нашего решения” (90 сек)
🤖 “Живая демонстрация” (120 сек)
📊 “Метрики и анализ” (60 сек)
| Критерий | Максимум | Что оценивается |
|---|---|---|
| Модульность кода | 5 | Структурированность, читаемость, документирование |
| Функциональность | 5 | Выполнение всех заявленных функций |
| Коммуникация | 5 | Надежность, эффективность протокола обмена |
| Координация | 5 | Качество алгоритмов совместной работы |
| Отладка и тестирование | 3 | Полнота тестирования, обработка ошибок |
Итого: 23 балла максимум
Архитектурные решения:
Алгоритмические решения:
Технические детали:
🔍 Анализируемые аспекты:
📝 Формат обратной связи:
КОМАНДА: _________ РЕВЬЮЕР: _________
СИЛЬНЫЕ СТОРОНЫ:
✓ ________________________________
✓ ________________________________
ОБЛАСТИ ДЛЯ УЛУЧШЕНИЯ:
△ ________________________________
△ ________________________________
ВОПРОСЫ К КОДУ:
? ________________________________
? ________________________________
ОБЩАЯ ОЦЕНКА: ___/10
✅ Что удалось реализовать блестяще?
🐛 Какие проблемы обнаружили?
🔄 Планы по улучшению?
| Навык | До проекта | После урока | Прогресс |
|---|---|---|---|
| Модульное программирование | __/10 | __/10 | +__ |
| Алгоритмы координации | __/10 | __/10 | +__ |
| Сетевое программирование | __/10 | __/10 | +__ |
| Отладка и тестирование | __/10 | __/10 | +__ |
| Системное мышление | __/10 | __/10 | +__ |
| Коллективная разработка | __/10 | __/10 | +__ |
Общий прогресс: +___/60
Формат: Подробное техническое описание (4-5 страниц)
Содержание:
Требования к коду:
Направления развития:
Формат: Описание улучшений + рабочий код + тестирование
Software Team Lead:
Algorithm Specialist:
Communication Expert:
QA Engineer:
“Сегодня вы создали не просто программы — вы создали цифровой симбиоз, где роботы думают, общаются и действуют как единый организм”