单片机互通调试初步完成
This commit is contained in:
242
esp32_robot_firmware/navigation.cpp
Normal file
242
esp32_robot_firmware/navigation.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "navigation.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
// ----------- 内部状态变量 -----------
|
||||
static NavigationState current_nav_state = NAV_IDLE;
|
||||
static int detected_spot_count = 0;
|
||||
static bool side_spot_debounce = false; // 用于侧向超声波检测去抖
|
||||
static unsigned long last_side_spot_detect_time = 0;
|
||||
const unsigned long SPOT_DEBOUNCE_TIME_MS = 2000; // 2秒去抖时间,避免重复计数同一个桩
|
||||
|
||||
// 循迹PID相关 (如果使用)
|
||||
static float pid_error = 0;
|
||||
static float pid_last_error = 0;
|
||||
static float pid_integral = 0;
|
||||
static float pid_derivative = 0;
|
||||
|
||||
// 目标车位ID的映射 (从config.h中获取)
|
||||
const char* SPOT_IDS[NUM_CHARGING_SPOTS] = {SPOT_ID_1, SPOT_ID_2, SPOT_ID_3};
|
||||
|
||||
// 将字符串的 spot_id (如 "001") 转换为索引 (0, 1, 2)
|
||||
int get_spot_index(const char* target_spot_uid) {
|
||||
if (!target_spot_uid) return -1;
|
||||
for (int i = 0; i < NUM_CHARGING_SPOTS; ++i) {
|
||||
if (strcmp(target_spot_uid, SPOT_IDS[i]) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // 未找到
|
||||
}
|
||||
|
||||
void navigation_setup() {
|
||||
current_nav_state = NAV_IDLE;
|
||||
detected_spot_count = 0;
|
||||
side_spot_debounce = false;
|
||||
// 初始化PID变量 (如果使用)
|
||||
pid_error = 0; pid_last_error = 0; pid_integral = 0; pid_derivative = 0;
|
||||
Serial.println("导航系统已初始化。");
|
||||
}
|
||||
|
||||
void navigation_reset_spot_counter() {
|
||||
detected_spot_count = 0;
|
||||
side_spot_debounce = false;
|
||||
Serial.println("车位计数器已重置。");
|
||||
}
|
||||
|
||||
int navigation_get_current_spot_count() {
|
||||
return detected_spot_count;
|
||||
}
|
||||
|
||||
NavigationState navigation_get_state() {
|
||||
return current_nav_state;
|
||||
}
|
||||
|
||||
void navigation_follow_line_once() {
|
||||
LineFollowValues line_values = get_line_follow_values();
|
||||
int base_speed = 150; // 基础循迹速度,需要调整
|
||||
int turn_speed_diff = 80; // 转向时的速度差或舵机调整量,需要调整
|
||||
int servo_turn_angle_small = 15; // 小幅调整舵机角度 (例如10-20度)
|
||||
int servo_turn_angle_large = 30; // 大幅调整舵机角度
|
||||
|
||||
// 这是一个非常基础的循迹逻辑,需要根据实际传感器和机器人进行大幅调整
|
||||
// 您可能需要更复杂的PID控制或其他算法
|
||||
|
||||
if (line_values.left_on_line && line_values.right_on_line) {
|
||||
// 1. 都在线上 (或者在两条线的中间),直行
|
||||
servo_center();
|
||||
motor_move_forward(base_speed);
|
||||
// Serial.println("Nav: Straight");
|
||||
} else if (line_values.left_on_line && !line_values.right_on_line) {
|
||||
// 2. 左边在线,右边不在:车体偏右,需要向左调整
|
||||
// motor_set_raw_speeds(base_speed - turn_speed_diff, base_speed + turn_speed_diff); // 左轮慢,右轮快
|
||||
servo_set_angle(90 - servo_turn_angle_small); // 舵机向左
|
||||
motor_move_forward(base_speed * 0.8); // 调整时可以稍微减速
|
||||
// Serial.println("Nav: Turn Left (correcting rightward drift)");
|
||||
} else if (!line_values.left_on_line && line_values.right_on_line) {
|
||||
// 3. 右边在线,左边不在:车体偏左,需要向右调整
|
||||
// motor_set_raw_speeds(base_speed + turn_speed_diff, base_speed - turn_speed_diff); // 左轮快,右轮慢
|
||||
servo_set_angle(90 + servo_turn_angle_small); // 舵机向右
|
||||
motor_move_forward(base_speed * 0.8);
|
||||
// Serial.println("Nav: Turn Right (correcting leftward drift)");
|
||||
} else {
|
||||
// 4. 两边都不在线:可能偏离太远,或者到达线路末端/交叉口
|
||||
// 简单处理:停止或尝试最后的方向转动
|
||||
motor_stop(); // 或者执行更复杂的搜索线路逻辑
|
||||
Serial.println("Nav: Both sensors off line! Stopping.");
|
||||
// current_nav_state = NAV_ERROR; // 可以设置错误状态
|
||||
}
|
||||
// PID循迹逻辑 (示例,未完全集成)
|
||||
/*
|
||||
if (line_values.left_on_line && !line_values.right_on_line) pid_error = -1; // 偏右
|
||||
else if (!line_values.left_on_line && line_values.right_on_line) pid_error = 1; // 偏左
|
||||
else if (line_values.left_on_line && line_values.right_on_line) pid_error = 0; // 居中
|
||||
else { // 都脱离,根据上次误差方向大幅调整或停车
|
||||
pid_error = pid_last_error * 2; // 放大上次误差
|
||||
}
|
||||
pid_integral += pid_error;
|
||||
pid_derivative = pid_error - pid_last_error;
|
||||
float motor_adjustment = KP * pid_error + KI * pid_integral + KD * pid_derivative;
|
||||
pid_last_error = pid_error;
|
||||
int left_motor_speed = base_speed - motor_adjustment;
|
||||
int right_motor_speed = base_speed + motor_adjustment;
|
||||
servo_set_angle(90 - (int)(motor_adjustment * 10)); // 假设舵机调整与motor_adjustment相关
|
||||
motor_set_raw_speeds(left_motor_speed, right_motor_speed);
|
||||
*/
|
||||
}
|
||||
|
||||
bool navigation_check_obstacle() {
|
||||
long dist = get_front_obstacle_distance();
|
||||
if (dist > 0 && dist < 20) { // 假设前方小于20cm为障碍物
|
||||
Serial.print("Nav: Obstacle detected at "); Serial.print(dist); Serial.println(" cm");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void navigation_handle_obstacle() {
|
||||
Serial.println("Nav: Handling obstacle...");
|
||||
motor_stop();
|
||||
// 简单处理:可以尝试后退一点,然后再次尝试,或者等待一段时间
|
||||
// 更复杂的避障逻辑(如转向绕过)需要更多传感器和算法
|
||||
// For now, just stop and set state to allow intervention or timeout
|
||||
current_nav_state = NAV_AVOIDING_OBSTACLE; // 更新导航内部状态
|
||||
// 在实际应用中,这里可能需要通知主循环机器人状态为FAULTED或需要帮助
|
||||
}
|
||||
|
||||
// 返回true表示到达目标,false表示未到达或已过目标
|
||||
bool navigation_check_spot_arrival(const char* target_spot_uid) {
|
||||
long side_dist = get_side_spot_distance();
|
||||
int target_spot_index = get_spot_index(target_spot_uid);
|
||||
|
||||
if (target_spot_index < 0) {
|
||||
Serial.print("Nav Error: Invalid target_spot_uid for arrival check: "); Serial.println(target_spot_uid);
|
||||
return false; // 无效目标
|
||||
}
|
||||
|
||||
if (side_dist > 0 && side_dist < SIDE_ULTRASONIC_SPOT_THRESHOLD_CM) {
|
||||
if (!side_spot_debounce) {
|
||||
detected_spot_count++;
|
||||
side_spot_debounce = true; // 进入去抖状态
|
||||
last_side_spot_detect_time = millis();
|
||||
Serial.print("Nav: Detected spot candidate. Count: "); Serial.println(detected_spot_count);
|
||||
|
||||
// 检查是否到达了目标充电桩 (目标是第 target_spot_index + 1 个桩)
|
||||
if (detected_spot_count == (target_spot_index + 1)) {
|
||||
Serial.print("Nav: TARGET SPOT ["
|
||||
Serial.println("Nav: TARGET SPOT [" + String(target_spot_uid) + "] REACHED (Count matched)!");
|
||||
return true; // 到达目标
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果距离大于阈值,且距离上次检测到满足条件已经超过去抖时间,则解除去抖
|
||||
if (side_spot_debounce && (millis() - last_side_spot_detect_time > SPOT_DEBOUNCE_TIME_MS)) {
|
||||
side_spot_debounce = false;
|
||||
Serial.println("Nav: Side spot debounce period ended.");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果已经计数的桩超过了目标桩的索引+1,说明可能错过了,或者目标逻辑有问题
|
||||
if (detected_spot_count > (target_spot_index + 1)) {
|
||||
Serial.println("Nav Warning: Detected spot count exceeds target. Possible overshoot or error.");
|
||||
// current_nav_state = NAV_ERROR; // 可以设置为错误状态
|
||||
// return false; // 或者根据业务逻辑,这里也视为一种失败
|
||||
}
|
||||
|
||||
return false; // 未到达目标
|
||||
}
|
||||
|
||||
|
||||
bool navigation_move_to_spot(const char* target_spot_uid, RobotStatus* current_robot_status_ptr, String* current_location_ptr) {
|
||||
if (!target_spot_uid || !current_robot_status_ptr || !current_location_ptr) {
|
||||
Serial.println("Nav Error: Invalid arguments to navigation_move_to_spot.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.print("Nav: Starting move_to_spot for target: "); Serial.println(target_spot_uid);
|
||||
*current_robot_status_ptr = MOVING;
|
||||
*current_location_ptr = "EN_ROUTE_TO_" + String(target_spot_uid);
|
||||
current_nav_state = NAV_FOLLOWING_LINE;
|
||||
// navigation_reset_spot_counter(); // 在开始新的导航任务时重置计数器
|
||||
// 注意: 重置计数器的时机很重要。如果机器人从任意位置启动,则应重置。
|
||||
// 如果是连续任务的一部分,则可能不需要。
|
||||
// 假设从基站出发或新任务开始,这里重置。
|
||||
// 但如果是在多个桩之间移动,这个逻辑需要调整。
|
||||
// 为了简单,我们假设每次MOVE指令都是一个新的完整寻路。
|
||||
if (detected_spot_count != 0) { // 如果不是从0开始,说明上次任务可能未清理或逻辑错误
|
||||
Serial.println("Nav Warning: Spot counter not zero at start of new navigation. Resetting.");
|
||||
navigation_reset_spot_counter();
|
||||
}
|
||||
|
||||
|
||||
unsigned long nav_start_time = millis();
|
||||
unsigned long MAX_NAV_TIME_MS = 120000; // 例如,最大导航时间2分钟,防止卡死
|
||||
|
||||
while (current_nav_state != NAV_TARGET_REACHED && current_nav_state != NAV_ERROR) {
|
||||
if (millis() - nav_start_time > MAX_NAV_TIME_MS) {
|
||||
Serial.println("Nav Error: Navigation timeout!");
|
||||
motor_stop();
|
||||
current_nav_state = NAV_ERROR;
|
||||
*current_robot_status_ptr = FAULTED; // 更新主状态
|
||||
*current_location_ptr = "LOST_OR_TIMEOUT";
|
||||
break;
|
||||
}
|
||||
|
||||
if (navigation_check_obstacle()) {
|
||||
navigation_handle_obstacle();
|
||||
*current_robot_status_ptr = MOVING; // 仍然是移动中,但可能需要人工介入或等待
|
||||
*current_location_ptr = "OBSTACLE_AVOIDANCE";
|
||||
// 在简单避障后,可能需要重新进入 NAV_FOLLOWING_LINE 状态
|
||||
// motor_stop(); // 确保在避障时机器人停止,或执行避障动作
|
||||
// delay(1000); // 等待
|
||||
// current_nav_state = NAV_FOLLOWING_LINE; // 尝试继续 (这部分逻辑需要完善)
|
||||
// 目前简单处理是停止,并依赖外部超时或指令取消
|
||||
Serial.println("Nav: Obstacle detected, stopping and awaiting further logic/timeout.");
|
||||
motor_stop();
|
||||
// break; // 或者跳出循环,让外部处理
|
||||
// 暂不break,允许其他状态检查或MQTT消息进来中断
|
||||
}
|
||||
|
||||
if (current_nav_state == NAV_FOLLOWING_LINE || current_nav_state == NAV_AT_SPOT_CANDIDATE) {
|
||||
navigation_follow_line_once();
|
||||
if (navigation_check_spot_arrival(target_spot_uid)) {
|
||||
current_nav_state = NAV_TARGET_REACHED;
|
||||
*current_location_ptr = String(target_spot_uid); // 更新位置为目标车位
|
||||
}
|
||||
}
|
||||
|
||||
// (这里可以加入MQTT消息的检查,如果收到STOP指令,可以中断导航)
|
||||
// mqtt_loop(); // 不建议在阻塞函数中频繁调用mqtt_loop,应由主循环处理
|
||||
|
||||
delay(50); // 短暂延时,控制循环频率
|
||||
}
|
||||
|
||||
motor_stop(); // 确保在函数结束时电机停止
|
||||
|
||||
if (current_nav_state == NAV_TARGET_REACHED) {
|
||||
Serial.print("Nav: Successfully reached target "); Serial.println(target_spot_uid);
|
||||
return true;
|
||||
} else {
|
||||
Serial.print("Nav: Failed to reach target "); Serial.println(target_spot_uid);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user