π― Π€ΠΈΠ½Π°Π»ΡΠ½ΡΠΉ ΠΏΡΠΎΠ΅ΠΊΡ: ΠΠ±ΡΠ΅Π΄ΠΈΠ½ΡΠ΅ΠΌ Π²ΡΠ΅ ΠΈΠ·ΡΡΠ΅Π½Π½ΡΠ΅ ΡΠ΅Ρ
Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ
β Π Π΅Π·ΡΠ»ΡΡΠ°Ρ: Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠΌΠ½ΠΎΠ³ΠΎ ΡΠΎΠ±ΠΎΡΠ° Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΠΌΠΈ Π΄Π°ΡΡΠΈΠΊΠ°ΠΌΠΈ
π¨βπ« Π£ΡΠΈΡΠ΅Π»Ρ: ΠΡ
ΠΌΠ΅ΡΠΎΠ² Π ΡΡΡΠ°ΠΌ
π« Π¨ΠΊΠΎΠ»Π°: ΠΠΠΠ£ β 1362
π
ΠΠ°ΡΠ°: 2025-06-14
β° ΠΡΠ΅ΠΌΡ: 105 ΠΌΠΈΠ½ΡΡ
Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠΎΠ±ΠΎΡΠ°-ΡΠ½ΠΈΠ²Π΅ΡΡΠ°Π»Π°, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΌΠ΅Π΅Ρ:
ΠΠ°Ρ ΡΠΎΠ±ΠΎΡ Π΄ΠΎΠ»ΠΆΠ΅Π½:
πͺ ΠΡΠΎ Π½Π°ΡΡΠΎΡΡΠΈΠΉ Π²ΡΠ·ΠΎΠ² Π΄Π»Ρ ΠΈΠ½ΠΆΠ΅Π½Π΅ΡΠΎΠ²-ΡΠΎΠ±ΠΎΡΠΎΡΠ΅Ρ Π½ΠΈΠΊΠΎΠ²!
ΠΠ°ΡΠΈ “Π³Π»Π°Π·Π° ΠΈ ΡΡΠΈ”:
ΠΠΎΠ³ΠΈΠΊΠ° ΠΏΡΠΈΠ½ΡΡΠΈΡ ΡΠ΅ΡΠ΅Π½ΠΈΠΉ:
1. ΠΠΠΠΠΠΠ‘ΠΠΠ‘Π’Π¬ (Π²ΡΡΡΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ)
β³ ΠΡΡΡ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅? β ΠΠ±Ρ
ΠΎΠ΄ΠΈΠΌ!
2. ΠΠΠΠΠΠΠ¦ΠΠ― (ΡΡΠ΅Π΄Π½ΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ)
β³ ΠΠΎΡΠ΅ΡΡΠ»ΠΈ Π»ΠΈΠ½ΠΈΡ? β ΠΡΠ΅ΠΌ!
3. Π‘ΠΠΠΠΠΠΠΠΠ (Π±Π°Π·ΠΎΠ²ΡΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ)
β³ ΠΠΈΠ΄ΠΈΠΌ Π»ΠΈΠ½ΠΈΡ? β ΠΠ΄Π΅ΠΌ ΠΏΠΎ Π½Π΅ΠΉ!
ΠΡΠΈΠ½ΡΠΈΠΏ: ΠΠ΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΡ Π²Π°ΠΆΠ½Π΅Π΅ ΡΠΊΠΎΡΠΎΡΡΠΈ, ΡΠΎΡΠ½ΠΎΡΡΡ Π²Π°ΠΆΠ½Π΅Π΅ ΡΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΠΈ!
ΠΡΠΈΠ½ΡΠΈΠΏ ΡΠ°Π±ΠΎΡΡ:
ΠΠ°ΡΠ΅ΠΌΠ°ΡΠΈΠΊΠ° Π΄Π°ΡΡΠΈΠΊΠ°:
\[\text{ΠΡΡΠ°ΠΆΠ΅Π½ΠΈΠ΅} = \frac{\text{ΠΠ½ΡΠ΅Π½ΡΠΈΠ²Π½ΠΎΡΡΡ ΠΎΡΡΠ°ΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ ΡΠ²Π΅ΡΠ°}}{\text{ΠΠ½ΡΠ΅Π½ΡΠΈΠ²Π½ΠΎΡΡΡ ΠΈΠ·Π»ΡΡΠ΅Π½Π½ΠΎΠ³ΠΎ ΡΠ²Π΅ΡΠ°}}\]ΠΠΎΡΠΎΠ³ΠΎΠ²ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ:
ΠΡΠΈΠ½ΡΠΈΠΏ ΡΠ°Π±ΠΎΡΡ:
Π€ΠΎΡΠΌΡΠ»Π° ΡΠ°ΡΡΠ΅ΡΠ° ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΡ:
\[d = \frac{v \cdot t}{2}\]Π³Π΄Π΅:
ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ°ΡΡΠ΅Ρ:
\[d_{ΡΠΌ} = \frac{t_{ΠΌΠΈΠΊΡΠΎΡΠ΅ΠΊΡΠ½Π΄Ρ}}{58}\]ΠΠ±ΡΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ ΠΎΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ Π΄Π°ΡΡΠΈΠΊΠΎΠ²:
Weighted fusion Π΄Π»Ρ Π΄Π°ΡΡΠΈΠΊΠΎΠ² Π»ΠΈΠ½ΠΈΠΈ:
\[\text{ΠΠΎΠ·ΠΈΡΠΈΡ} = w_L \cdot S_L + w_R \cdot S_R\]Π³Π΄Π΅ $w_L$ ΠΈ $w_R$ - Π²Π΅ΡΠ° Π»Π΅Π²ΠΎΠ³ΠΎ ΠΈ ΠΏΡΠ°Π²ΠΎΠ³ΠΎ Π΄Π°ΡΡΠΈΠΊΠΎΠ², $S_L$ ΠΈ $S_R$ - ΠΈΡ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΈΡ
ΠΠΎΠ³ΠΈΠΊΠ° ΠΏΡΠΈΠ½ΡΡΠΈΡ ΡΠ΅ΡΠ΅Π½ΠΈΠΉ:
\[\text{ΠΠ΅ΠΉΡΡΠ²ΠΈΠ΅} = \begin{cases} \text{STOP} & \text{if } d < d_{ΠΊΡΠΈΡΠΈΡΠ΅ΡΠΊΠΎΠ΅} \\ \text{AVOID} & \text{if } d < d_{Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΠ΅} \\ \text{FOLLOW} & \text{if } \text{line detected} \\ \text{SEARCH} & \text{otherwise} \end{cases}\]ΠΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ ΡΠΈΠ»ΡΡΡΠ°ΡΠΈΡ ΡΡΠΌΠΎΠ²:
\[S_{filtered}(t) = \alpha \cdot S_{raw}(t) + (1-\alpha) \cdot S_{filtered}(t-1)\]Π³Π΄Π΅ Ξ± β 0.3 (ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ ΡΠ³Π»Π°ΠΆΠΈΠ²Π°Π½ΠΈΡ)
ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ ΡΠΎΠ±ΠΎΡΠ°:
ΠΡΠΈΠ½ΡΠΈΠΏΡ ΡΠ°Π·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ²:
ΠΠΏΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠ°ΡΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅:
[Π ΠΎΠ±ΠΎΡ ΡΠ²Π΅ΡΡ
Ρ]
[O] [O] β ΠΠΎΠ»Π΅ΡΠ°
| |
[β] [β] β ΠΠ°ΡΡΠΈΠΊΠΈ Π»ΠΈΠ½ΠΈΠΈ
\ /
\ /
[βββ] β Π§Π΅ΡΠ½Π°Ρ Π»ΠΈΠ½ΠΈΡ
Π Π°ΡΡΡΠΎΡΠ½ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρ Π΄Π°ΡΡΠΈΠΊΠ°ΠΌΠΈ:
\[d_{sensors} = 1.5 \times \text{ΡΠΈΡΠΈΠ½Π° Π»ΠΈΠ½ΠΈΠΈ}\]ΠΠ»Ρ ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΠΎΠΉ Π»ΠΈΠ½ΠΈΠΈ 20 ΠΌΠΌ: ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΠ΅ β 30 ΠΌΠΌ
ΠΡΠ΅ΠΏΠ»Π΅Π½ΠΈΠ΅ Π½Π° ΠΏΠ΅ΡΠ΅Π΄Π½Π΅ΠΉ ΡΠ°ΡΡΠΈ:
// ΠΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄Π°ΡΡΠΈΠΊΠΎΠ² ΠΊ Arduino
const int TRIG_PIN = 7; // Π£Π»ΡΡΡΠ°Π·Π²ΡΠΊ - trigger
const int ECHO_PIN = 6; // Π£Π»ΡΡΡΠ°Π·Π²ΡΠΊ - echo
const int LINE_LEFT_PIN = A0; // ΠΠ΅Π²ΡΠΉ Π΄Π°ΡΡΠΈΠΊ Π»ΠΈΠ½ΠΈΠΈ
const int LINE_RIGHT_PIN = A1; // ΠΡΠ°Π²ΡΠΉ Π΄Π°ΡΡΠΈΠΊ Π»ΠΈΠ½ΠΈΠΈ
const int MOTOR_LEFT_A = 3; // ΠΠ΅Π²ΡΠΉ ΠΌΠΎΡΠΎΡ - Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
const int MOTOR_LEFT_B = 4; // ΠΠ΅Π²ΡΠΉ ΠΌΠΎΡΠΎΡ - Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
const int MOTOR_RIGHT_A = 5; // ΠΡΠ°Π²ΡΠΉ ΠΌΠΎΡΠΎΡ - Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
const int MOTOR_RIGHT_B = 8; // ΠΡΠ°Π²ΡΠΉ ΠΌΠΎΡΠΎΡ - Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
ΠΡΡΠΎΠ΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΈΠ½ΡΠΈΠΏΡ: ΠΠ°ΠΆΠ΅ Π΄Π»Ρ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΡΠΎΠ±ΠΎΡΠ° ΡΠΎΡΠΌΠ° ΠΈΠΌΠ΅Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅:
Π¦Π΅Π½ΡΡ ΠΌΠ°ΡΡ ΠΈ ΡΡΡΠΎΠΉΡΠΈΠ²ΠΎΡΡΡ:
\[h_{max} = \frac{b \cdot g}{2 \cdot a_{max}}\]Π³Π΄Π΅:
ΠΠΎΠΌΠ΅Π½Ρ ΠΈΠ½Π΅ΡΡΠΈΠΈ Π΄Π»Ρ ΠΏΠΎΠ²ΠΎΡΠΎΡΠΎΠ²:
\[I = m \cdot r_{gyration}^2\]ΠΠΎΠΌΠΏΠ°ΠΊΡΠ½Π°Ρ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΡ ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΡΡΡ Π±ΡΡΡΡΠ΅Π΅!
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ:
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌΡ
void setup() {
Serial.begin(9600);
initializeSensors();
initializeMotors();
calibrateSensors();
}
// ΠΠ»Π°Π²Π½ΡΠΉ ΡΠΈΠΊΠ»
void loop() {
updateSensors(); // Π§ΠΈΡΠ°Π΅ΠΌ Π²ΡΠ΅ Π΄Π°ΡΡΠΈΠΊΠΈ
makeDecision(); // ΠΡΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅
executeAction(); // ΠΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅
delay(20); // 50 Hz update rate
}
// Π‘ΡΡΡΠΊΡΡΡΠ° Π΄Π»Ρ Ρ
ΡΠ°Π½Π΅Π½ΠΈΡ Π΄Π°Π½Π½ΡΡ
Π΄Π°ΡΡΠΈΠΊΠΎΠ²
struct SensorData {
int leftLine; // ΠΠ΅Π²ΡΠΉ Π΄Π°ΡΡΠΈΠΊ Π»ΠΈΠ½ΠΈΠΈ (0-1023)
int rightLine; // ΠΡΠ°Π²ΡΠΉ Π΄Π°ΡΡΠΈΠΊ Π»ΠΈΠ½ΠΈΠΈ (0-1023)
float distance; // Π Π°ΡΡΡΠΎΡΠ½ΠΈΠ΅ Π² ΡΠΌ
bool lineDetected; // ΠΠ±Π½Π°ΡΡΠΆΠ΅Π½Π° Π»ΠΈ Π»ΠΈΠ½ΠΈΡ
bool obstacleAhead; // ΠΡΡΡ Π»ΠΈ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅ Π²ΠΏΠ΅ΡΠ΅Π΄ΠΈ
};
SensorData sensors;
void updateSensors() {
// Π§ΠΈΡΠ°Π΅ΠΌ Π΄Π°ΡΡΠΈΠΊΠΈ Π»ΠΈΠ½ΠΈΠΈ
sensors.leftLine = analogRead(LINE_LEFT_PIN);
sensors.rightLine = analogRead(LINE_RIGHT_PIN);
// Π§ΠΈΡΠ°Π΅ΠΌ ΡΠ»ΡΡΡΠ°Π·Π²ΡΠΊΠΎΠ²ΠΎΠΉ Π΄Π°ΡΡΠΈΠΊ
sensors.distance = readUltrasonic();
// ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ
sensors.lineDetected = (sensors.leftLine < 300) || (sensors.rightLine < 300);
sensors.obstacleAhead = sensors.distance < 20.0; // 20 ΡΠΌ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½Π°Ρ Π·ΠΎΠ½Π°
}
float readUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
return duration * 0.034 / 2; // ΠΠ΅ΡΠ΅Π²ΠΎΠ΄ Π² ΡΠ°Π½ΡΠΈΠΌΠ΅ΡΡΡ
}
Π‘ΠΎΡΡΠΎΡΠ½ΠΈΡ ΡΠΎΠ±ΠΎΡΠ°:
enum RobotState {
FOLLOWING_LINE, // Π‘Π»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎ Π»ΠΈΠ½ΠΈΠΈ
AVOIDING_OBSTACLE, // ΠΠ±Ρ
ΠΎΠ΄ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΡ
SEARCHING_LINE, // ΠΠΎΠΈΡΠΊ ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΠΎΠΉ Π»ΠΈΠ½ΠΈΠΈ
STOPPED // ΠΡΡΠ°Π½ΠΎΠ²ΠΊΠ° (Π°Π²Π°ΡΠΈΠΉΠ½Π°Ρ ΡΠΈΡΡΠ°ΡΠΈΡ)
};
RobotState currentState = FOLLOWING_LINE;
void makeDecision() {
switch (currentState) {
case FOLLOWING_LINE:
if (sensors.obstacleAhead) {
currentState = AVOIDING_OBSTACLE;
Serial.println("Switching to OBSTACLE AVOIDANCE");
} else if (!sensors.lineDetected) {
currentState = SEARCHING_LINE;
Serial.println("LINE LOST - Searching...");
}
break;
case AVOIDING_OBSTACLE:
if (!sensors.obstacleAhead && sensors.lineDetected) {
currentState = FOLLOWING_LINE;
Serial.println("Obstacle cleared - Following line");
}
break;
case SEARCHING_LINE:
if (sensors.lineDetected) {
currentState = FOLLOWING_LINE;
Serial.println("Line found - Following");
}
break;
case STOPPED:
// Π’ΡΠ΅Π±ΡΠ΅ΡΡΡ ΡΡΡΠ½ΠΎΠ΅ Π²ΠΌΠ΅ΡΠ°ΡΠ΅Π»ΡΡΡΠ²ΠΎ
break;
}
}
void executeAction() {
switch (currentState) {
case FOLLOWING_LINE:
followLine();
break;
case AVOIDING_OBSTACLE:
avoidObstacle();
break;
case SEARCHING_LINE:
searchLine();
break;
case STOPPED:
stopMotors();
break;
}
}
void followLine() {
int leftSensor = sensors.leftLine;
int rightSensor = sensors.rightLine;
// PID-ΡΠ΅Π³ΡΠ»ΡΡΠΎΡ Π΄Π»Ρ ΡΠΎΡΠ½ΠΎΠ³ΠΎ ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΡ
int error = leftSensor - rightSensor;
static int lastError = 0;
static int integral = 0;
float Kp = 0.5; // ΠΡΠΎΠΏΠΎΡΡΠΈΠΎΠ½Π°Π»ΡΠ½ΡΠΉ ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ
float Ki = 0.01; // ΠΠ½ΡΠ΅Π³ΡΠ°Π»ΡΠ½ΡΠΉ ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ
float Kd = 0.1; // ΠΠΈΡΡΠ΅ΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΡΠΉ ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ
integral += error;
int derivative = error - lastError;
int correction = Kp * error + Ki * integral + Kd * derivative;
int baseSpeed = 150; // ΠΠ°Π·ΠΎΠ²Π°Ρ ΡΠΊΠΎΡΠΎΡΡΡ
int leftSpeed = baseSpeed + correction;
int rightSpeed = baseSpeed - correction;
// ΠΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΠΊΠΎΡΠΎΡΡΠΈ
leftSpeed = constrain(leftSpeed, 0, 255);
rightSpeed = constrain(rightSpeed, 0, 255);
setMotorSpeeds(leftSpeed, rightSpeed);
lastError = error;
}
ΠΠ΄Π°ΠΏΡΠΈΠ²Π½ΡΠΉ PID-ΡΠ΅Π³ΡΠ»ΡΡΠΎΡ:
class AdaptivePID {
private:
float kp, ki, kd;
float integral, lastError;
float maxIntegral = 1000;
public:
AdaptivePID(float p, float i, float d) : kp(p), ki(i), kd(d) {
integral = 0;
lastError = 0;
}
float calculate(float error, float speed) {
// ΠΠ΄Π°ΠΏΡΠ°ΡΠΈΡ ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½ΡΠΎΠ² Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΡΠΊΠΎΡΠΎΡΡΠΈ
float adaptiveKp = kp * (1.0 + speed / 255.0);
float adaptiveKd = kd * (1.0 + speed / 255.0);
integral += error;
integral = constrain(integral, -maxIntegral, maxIntegral);
float derivative = error - lastError;
float output = adaptiveKp * error + ki * integral + adaptiveKd * derivative;
lastError = error;
return output;
}
};
ΠΡΠ΅Π΄ΠΈΠΊΡΠΈΠ²Π½ΠΎΠ΅ ΠΈΠ·Π±Π΅Π³Π°Π½ΠΈΠ΅ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠΉ:
float predictCollisionTime(float distance, float speed) {
if (speed <= 0) return INFINITY;
return distance / speed;
}
void smartObstacleAvoidance() {
float currentSpeed = getCurrentSpeed();
float timeToCollision = predictCollisionTime(sensors.distance, currentSpeed);
if (timeToCollision < 2.0) { // 2 ΡΠ΅ΠΊΡΠ½Π΄Ρ Π΄ΠΎ ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΡ
// ΠΠ°ΡΠΈΠ½Π°Π΅ΠΌ ΠΌΠ°Π½Π΅Π²Ρ Π·Π°ΡΠ°Π½Π΅Π΅
executeAvoidanceManeuver();
}
}
ΠΠ°Π·ΠΎΠ²ΡΠΉ Π°Π»Π³ΠΎΡΠΈΡΠΌ:
void basicLineFollowing() {
bool leftOnLine = (sensors.leftLine < 300);
bool rightOnLine = (sensors.rightLine < 300);
if (leftOnLine && rightOnLine) {
// ΠΠ±Π° Π΄Π°ΡΡΠΈΠΊΠ° Π½Π° Π»ΠΈΠ½ΠΈΠΈ - Π΅Π΄Π΅ΠΌ ΠΏΡΡΠΌΠΎ
setMotorSpeeds(150, 150);
} else if (leftOnLine && !rightOnLine) {
// ΠΠΈΠ½ΠΈΡ ΡΠ»Π΅Π²Π° - ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌ Π²Π»Π΅Π²ΠΎ
setMotorSpeeds(100, 200);
} else if (!leftOnLine && rightOnLine) {
// ΠΠΈΠ½ΠΈΡ ΡΠΏΡΠ°Π²Π° - ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌ Π²ΠΏΡΠ°Π²ΠΎ
setMotorSpeeds(200, 100);
} else {
// ΠΠΈΠ½ΠΈΡ ΠΏΠΎΡΠ΅ΡΡΠ½Π° - ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌΡΡ
setMotorSpeeds(0, 0);
currentState = SEARCHING_LINE;
}
}
ΠΠ»Π³ΠΎΡΠΈΡΠΌ ΠΏΡΠ°Π²ΠΎΠΉ ΡΡΠΊΠΈ:
void avoidObstacle() {
static int avoidanceStep = 0;
static unsigned long stepStartTime = 0;
switch (avoidanceStep) {
case 0: // ΠΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
stopMotors();
delay(500);
avoidanceStep = 1;
stepStartTime = millis();
break;
case 1: // ΠΠΎΠ²ΠΎΡΠΎΡ Π²Π»Π΅Π²ΠΎ Π½Π° 90Β°
setMotorSpeeds(-150, 150); // ΠΠΎΠ²ΠΎΡΠΎΡ Π½Π° ΠΌΠ΅ΡΡΠ΅
if (millis() - stepStartTime > 800) { // ~90Β°
avoidanceStep = 2;
stepStartTime = millis();
}
break;
case 2: // ΠΠ²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π²ΠΏΠ΅ΡΠ΅Π΄
setMotorSpeeds(150, 150);
if (millis() - stepStartTime > 1000) { // ΠΡΠΎΠ΅Π·ΠΆΠ°Π΅ΠΌ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅
avoidanceStep = 3;
stepStartTime = millis();
}
break;
case 3: // ΠΠΎΠ²ΠΎΡΠΎΡ Π²ΠΏΡΠ°Π²ΠΎ Π½Π° 90Β°
setMotorSpeeds(150, -150);
if (millis() - stepStartTime > 800) {
avoidanceStep = 4;
stepStartTime = millis();
}
break;
case 4: // ΠΠ²ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎ Π»ΠΈΠ½ΠΈΠΈ
setMotorSpeeds(150, 150);
if (sensors.lineDetected) {
avoidanceStep = 5;
stepStartTime = millis();
}
break;
case 5: // ΠΠΎΠ²ΠΎΡΠΎΡ Π²ΠΏΡΠ°Π²ΠΎ Π΄Π»Ρ Π²ΡΡ
ΠΎΠ΄Π° Π½Π° Π»ΠΈΠ½ΠΈΡ
setMotorSpeeds(150, -150);
if (millis() - stepStartTime > 800) {
avoidanceStep = 0; // Π‘Π±ΡΠΎΡ
currentState = FOLLOWING_LINE;
}
break;
}
}
Π‘ΠΏΠΈΡΠ°Π»ΡΠ½ΡΠΉ ΠΏΠΎΠΈΡΠΊ:
void searchLine() {
static unsigned long searchStartTime = 0;
static bool searchInitialized = false;
static int searchRadius = 100; // ΠΌΡ ΠΏΠΎΠ²ΠΎΡΠΎΡΠ°
static int searchDirection = 1; // 1 = ΠΏΡΠ°Π²ΠΎ, -1 = Π»Π΅Π²ΠΎ
if (!searchInitialized) {
searchStartTime = millis();
searchInitialized = true;
}
// ΠΠΎΠ²ΠΎΡΠΎΡ Ρ ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°ΡΡΠΈΠΌΡΡ ΡΠ°Π΄ΠΈΡΡΠΎΠΌ
if (searchDirection > 0) {
setMotorSpeeds(150, -150); // ΠΠΎΠ²ΠΎΡΠΎΡ Π²ΠΏΡΠ°Π²ΠΎ
} else {
setMotorSpeeds(-150, 150); // ΠΠΎΠ²ΠΎΡΠΎΡ Π²Π»Π΅Π²ΠΎ
}
if (millis() - searchStartTime > searchRadius) {
searchDirection *= -1; // ΠΠ΅Π½ΡΠ΅ΠΌ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅
searchRadius += 50; // Π£Π²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΠ°Π΄ΠΈΡΡ ΠΏΠΎΠΈΡΠΊΠ°
searchStartTime = millis();
// ΠΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ Π²ΡΠ΅ΠΌΡ ΠΏΠΎΠΈΡΠΊΠ°
if (searchRadius > 2000) {
currentState = STOPPED; // ΠΠΈΠ½ΠΈΡ Π±Π΅Π·Π½Π°Π΄Π΅ΠΆΠ½ΠΎ ΠΏΠΎΡΠ΅ΡΡΠ½Π°
Serial.println("Line search failed - STOPPING");
}
}
// ΠΡΠ»ΠΈ Π½Π°ΡΠ»ΠΈ Π»ΠΈΠ½ΠΈΡ
if (sensors.lineDetected) {
searchInitialized = false;
searchRadius = 100; // Π‘Π±ΡΠΎΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
currentState = FOLLOWING_LINE;
Serial.println("Line found!");
}
}
Q-Learning Π΄Π»Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ:
class QLearningRobot {
private:
float qTable[NUM_STATES][NUM_ACTIONS];
float learningRate = 0.1;
float discountFactor = 0.9;
float explorationRate = 0.1;
enum State {
ON_LINE_CLEAR = 0,
ON_LINE_OBSTACLE = 1,
OFF_LINE_CLEAR = 2,
OFF_LINE_OBSTACLE = 3
};
enum Action {
MOVE_FORWARD = 0,
TURN_LEFT = 1,
TURN_RIGHT = 2,
REVERSE = 3
};
public:
int selectAction(int state) {
if (random(100) < explorationRate * 100) {
return random(NUM_ACTIONS); // ΠΡΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅
} else {
return getBestAction(state); // ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ Π·Π½Π°Π½ΠΈΠΉ
}
}
void updateQ(int state, int action, float reward, int nextState) {
float maxQ = getMaxQ(nextState);
float oldQ = qTable[state][action];
qTable[state][action] = oldQ + learningRate *
(reward + discountFactor * maxQ - oldQ);
}
float calculateReward() {
float reward = 0;
if (sensors.lineDetected) reward += 10; // ΠΠ°Π³ΡΠ°Π΄Π° Π·Π° ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ Π»ΠΈΠ½ΠΈΠΈ
if (sensors.obstacleAhead) reward -= 50; // Π¨ΡΡΠ°Ρ Π·Π° ΡΠΈΡΠΊ ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΡ
if (getCurrentSpeed() > 100) reward += 1; // ΠΠ°Π³ΡΠ°Π΄Π° Π·Π° ΡΠΊΠΎΡΠΎΡΡΡ
return reward;
}
};
ΠΡΠ°ΠΏ 1: Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π΄Π°ΡΡΠΈΠΊΠΎΠ²
void testSensors() {
Serial.println("=== SENSOR TEST ===");
// Π’Π΅ΡΡ Π΄Π°ΡΡΠΈΠΊΠΎΠ² Π»ΠΈΠ½ΠΈΠΈ
Serial.print("Left Line: ");
Serial.print(analogRead(LINE_LEFT_PIN));
Serial.print(" | Right Line: ");
Serial.println(analogRead(LINE_RIGHT_PIN));
// Π’Π΅ΡΡ ΡΠ»ΡΡΡΠ°Π·Π²ΡΠΊΠ°
Serial.print("Distance: ");
Serial.print(readUltrasonic());
Serial.println(" cm");
delay(500);
}
ΠΡΠ°ΠΏ 2: Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΌΠΎΡΠΎΡΠΎΠ²
void testMotors() {
Serial.println("Testing motors...");
// ΠΠΏΠ΅ΡΠ΅Π΄
setMotorSpeeds(150, 150);
delay(1000);
// ΠΠΎΠ²ΠΎΡΠΎΡ Π²Π»Π΅Π²ΠΎ
setMotorSpeeds(100, 200);
delay(500);
// ΠΠΎΠ²ΠΎΡΠΎΡ Π²ΠΏΡΠ°Π²ΠΎ
setMotorSpeeds(200, 100);
delay(500);
// Π‘ΡΠΎΠΏ
stopMotors();
}
ΠΡΠ°ΠΏ 3: ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
void integrationTest() {
Serial.println("Integration test starting...");
// Π’Π΅ΡΡ 1: Π’ΠΎΠ»ΡΠΊΠΎ ΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎ Π»ΠΈΠ½ΠΈΠΈ (Π±Π΅Π· ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠΉ)
testLineFollowing();
// Π’Π΅ΡΡ 2: Π’ΠΎΠ»ΡΠΊΠΎ ΠΎΠ±Ρ
ΠΎΠ΄ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠΉ (Π±Π΅Π· Π»ΠΈΠ½ΠΈΠΈ)
testObstacleAvoidance();
// Π’Π΅ΡΡ 3: ΠΠΎΠΌΠ±ΠΈΠ½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΡΠ΅ΡΡ
testComplexBehavior();
}
void calibrateSensors() {
Serial.println("Calibrating sensors...");
Serial.println("Place robot on WHITE surface and press button");
waitForButtonPress();
// ΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π½Π° Π±Π΅Π»ΠΎΠΌ ΡΠΎΠ½Π΅
int whiteLeft = 0, whiteRight = 0;
for (int i = 0; i < 100; i++) {
whiteLeft += analogRead(LINE_LEFT_PIN);
whiteRight += analogRead(LINE_RIGHT_PIN);
delay(10);
}
whiteLeft /= 100;
whiteRight /= 100;
Serial.println("Now place on BLACK line and press button");
waitForButtonPress();
// ΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π½Π° ΡΠ΅ΡΠ½ΠΎΠΉ Π»ΠΈΠ½ΠΈΠΈ
int blackLeft = 0, blackRight = 0;
for (int i = 0; i < 100; i++) {
blackLeft += analogRead(LINE_LEFT_PIN);
blackRight += analogRead(LINE_RIGHT_PIN);
delay(10);
}
blackLeft /= 100;
blackRight /= 100;
// ΠΡΡΠΈΡΠ»ΡΠ΅ΠΌ ΠΏΠΎΡΠΎΠ³ΠΎΠ²ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ
int thresholdLeft = (whiteLeft + blackLeft) / 2;
int thresholdRight = (whiteRight + blackRight) / 2;
Serial.print("Thresholds - Left: ");
Serial.print(thresholdLeft);
Serial.print(", Right: ");
Serial.println(thresholdRight);
}
ΠΡΠΈΡΠ΅ΡΠΈΠΈ:
Π‘ΠΈΡΡΠ΅ΠΌΠ° Π±Π°Π»Π»ΠΎΠ²:
ΠΡΠΈΡΠ΅ΡΠΈΠΈ:
Π‘ΠΈΡΡΠ΅ΠΌΠ° Π±Π°Π»Π»ΠΎΠ²:
ΠΡΠΈΡΠ΅ΡΠΈΠΈ:
Π€ΠΎΡΠΌΡΠ»Π° ΠΈΡΠΎΠ³ΠΎΠ²ΠΎΠ³ΠΎ Π±Π°Π»Π»Π°: \[\text{ΠΠ°Π»Π»} = \frac{\text{ΠΠ°Π»Π»Ρ Π·Π° ΡΠΎΡΠ½ΠΎΡΡΡ} \times 1000}{\text{ΠΡΠ΅ΠΌΡ Π² ΡΠ΅ΠΊΡΠ½Π΄Π°Ρ }}\]
ΠΠΎΠ½ΡΡΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅:
ΠΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅:
Π‘ΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ΅ ΠΌΡΡΠ»Π΅Π½ΠΈΠ΅:
ΠΠ½ΠΆΠ΅Π½Π΅ΡΠ½ΡΠΉ ΠΏΠΎΠ΄Ρ ΠΎΠ΄:
“Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠΌΠ½ΠΎΠ³ΠΎ ΡΠΎΠ±ΠΎΡΠ° - ΡΡΠΎ Π½Π΅ ΠΏΡΠΎΡΡΠΎ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Π΄Π°ΡΡΠΈΠΊΠΎΠ² ΠΈ ΠΌΠΎΡΠΎΡΠΎΠ². ΠΡΠΎ ΠΈΡΠΊΡΡΡΡΠ²ΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠΈΡΡΠ΅ΠΌΡ, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΌΠΎΠΆΠ΅Ρ ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡ ΡΠ°Π·ΡΠΌΠ½ΡΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΡ Π² ΡΠ»ΠΎΠΆΠ½ΠΎΠΌ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΠΈΠ²ΠΎΠΌ ΠΌΠΈΡΠ΅!”
1. Π’Π΅Ρ Π½ΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΎΡΡΠ΅Ρ Π‘ΠΎΡΡΠ°Π²ΡΡΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠΉ ΠΎΡΡΠ΅Ρ ΠΎ Π²Π°ΡΠ΅ΠΌ ΡΠΎΠ±ΠΎΡΠ΅:
2. ΠΠ½Π°Π»ΠΈΠ· ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ ΠΠΏΠΈΡΠΈΡΠ΅, ΠΊΠ°ΠΊ Π²Π°Ρ ΡΠΎΠ±ΠΎΡ ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ ΡΠ΅ΡΠ΅Π½ΠΈΡ Π² ΡΠ»Π΅Π΄ΡΡΡΠΈΡ ΡΠΈΡΡΠ°ΡΠΈΡΡ :
3. Π Π°ΡΡΠΈΡΠ΅Π½Π½Π°Ρ ΡΠ΅Π½ΡΠΎΡΠ½Π°Ρ ΡΠΈΡΡΠ΅ΠΌΠ° Π‘ΠΏΡΠΎΠ΅ΠΊΡΠΈΡΡΠΉΡΠ΅ ΡΠΎΠ±ΠΎΡΠ° Ρ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠΌΠΈ Π΄Π°ΡΡΠΈΠΊΠ°ΠΌΠΈ:
4. ΠΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠΎΠ² ΠΡΡΠ»Π΅Π΄ΡΠΉΡΠ΅ ΠΈ ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠΈΡΠ΅ ΡΠ»ΡΡΡΠ΅Π½ΠΈΡ:
5. Π‘ΠΈΠΌΡΠ»ΡΡΠΈΡ Π² Π²ΠΈΡΡΡΠ°Π»ΡΠ½ΠΎΠΉ ΡΡΠ΅Π΄Π΅ Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΠΊΠΎΠΌΠΏΡΡΡΠ΅ΡΠ½ΡΡ ΠΌΠΎΠ΄Π΅Π»Ρ Π²Π°ΡΠ΅Π³ΠΎ ΡΠΎΠ±ΠΎΡΠ°:
6. ΠΡΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΏΡΠΎΠ΅ΠΊΡ ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΠΎΠ΄Π½Ρ ΠΈΠ· ΡΠ΅ΠΌ Π΄Π»Ρ ΡΠ³Π»ΡΠ±Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΈΠ·ΡΡΠ΅Π½ΠΈΡ:
π§ ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π½Π°Π²ΡΠΊΠΈ:
π§ Π’Π΅ΠΎΡΠ΅ΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ Π·Π½Π°Π½ΠΈΡ:
π ΠΠ½ΠΆΠ΅Π½Π΅ΡΠ½ΠΎΠ΅ ΠΌΡΡΠ»Π΅Π½ΠΈΠ΅:
ΠΠ΄Π΅ ΠΏΡΠΈΠΌΠ΅Π½ΠΈΡΡ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠ΅ Π·Π½Π°Π½ΠΈΡ:
Π‘Π»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠ°Π³ΠΈ Π² ΠΎΠ±ΡΡΠ΅Π½ΠΈΠΈ:
ΠΡ Π·Π°Π²Π΅ΡΡΠΈΠ»ΠΈ ΠΊΡΡΡ ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΠΎΠΉ ΡΠΎΠ±ΠΎΡΠΎΡΠ΅Ρ Π½ΠΈΠΊΠΈ!
Π‘Π΅Π³ΠΎΠ΄Π½Ρ Π²Ρ ΡΠΎΠ·Π΄Π°Π»ΠΈ ΡΠΎΠ±ΠΎΡΠ°, ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΠΎΠΆΠ΅Ρ:
ΠΡΠΎ Π½Π΅ ΠΊΠΎΠ½Π΅Ρ, Π° ΡΠΎΠ»ΡΠΊΠΎ Π½Π°ΡΠ°Π»ΠΎ Π²Π°ΡΠ΅Π³ΠΎ ΠΏΡΡΠΈ Π² ΠΌΠΈΡΠ΅ ΡΠΎΠ±ΠΎΡΠΎΡΠ΅Ρ Π½ΠΈΠΊΠΈ!
ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠ°ΠΉΡΠ΅ ΠΈΠ·ΡΡΠ°ΡΡ, ΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°ΡΡ ΠΈ ΡΠΎΠ·Π΄Π°Π²Π°ΡΡ. ΠΡΠ΄ΡΡΠ΅Π΅ ΡΠ΅Ρ Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ Π² Π²Π°ΡΠΈΡ ΡΡΠΊΠ°Ρ ! π€β¨
ΠΠ½Π»Π°ΠΉΠ½-ΠΊΡΡΡΡ:
ΠΠ½ΠΈΠ³ΠΈ Π΄Π»Ρ ΡΠ³Π»ΡΠ±Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΈΠ·ΡΡΠ΅Π½ΠΈΡ:
Π‘ΠΈΠΌΡΠ»ΡΡΠΎΡΡ:
Π€ΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΈ:
ΠΡΠ·Ρ Ρ ΡΠΈΠ»ΡΠ½ΡΠΌΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ°ΠΌΠΈ ΠΏΠΎ ΡΠΎΠ±ΠΎΡΠΎΡΠ΅Ρ Π½ΠΈΠΊΠ΅:
ΠΠ»ΠΈΠΌΠΏΠΈΠ°Π΄Ρ ΠΈ ΡΠΎΡΠ΅Π²Π½ΠΎΠ²Π°Π½ΠΈΡ:
Π£Π΄Π°ΡΠΈ Π² Π²Π°ΡΠΈΡ Π±ΡΠ΄ΡΡΠΈΡ ΡΠΎΠ±ΠΎΡΠΎΡΠ΅Ρ Π½ΠΈΡΠ΅ΡΠΊΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡΠ°Ρ ! ππ€β¨