初次测试单片机循迹避障

This commit is contained in:
2025-06-08 16:46:52 +08:00
parent aac3a383ce
commit e5bf852d1b
88 changed files with 240 additions and 248 deletions

View File

@@ -1,99 +1,249 @@
//使用了SR04超声波传感器可收发超声波测距0~450cm // 使用了SR04超声波传感器、TRCT5000循迹传感器、SG90舵机和直流电机进行循迹避障测试
//通过串口显示距离 // 逻辑: 第一次检测到障碍物后开始循迹,第二次检测到障碍物后停止。
#include <Servo.h> #include <Servo.h>
// --- Pin Definitions ---
// Servo
Servo myServo; Servo myServo;
const int servoPin = 9; const int servoPin = 9;
//设定SR04连接的Arduino引脚
const int TrigPin = 8; //C语言的基本数据类型中的一种const int定义的是一个常量它的值不可以更改。
const int EchoPin = 6;
const int TrigPin2 = 2;
const int EchoPin2 = 3;
const int trct5000_01=A0;
float distance; //C语言的基本数据类型中的一种表示单精度浮点数。
float distance2;
int flag;
void setup() // Ultrasonic Sensor - Front (for start/stop control)
{ const int frontTrigPin = 2;
Serial.begin(9600); //初始化串口通信 const int frontEchoPin = 3;
myServo.attach(servoPin,500, 2500);
pinMode(TrigPin, OUTPUT); //设置4TrigPin脚为输出状态
// 要检测引脚上输入的脉冲宽度,需要先设置为输入状态
pinMode(EchoPin, INPUT); //设置6EchoPin脚为输入状态
pinMode(TrigPin2, OUTPUT); //设置4TrigPin脚为输出状态 // Ultrasonic Sensor - Side (for pausing)
// 要检测引脚上输入的脉冲宽度,需要先设置为输入状态 const int sideTrigPin = 8;
pinMode(EchoPin2, INPUT); //设置6EchoPin脚为输入状态 const int sideEchoPin = 6;
Serial.println("Ultrasonic sensor:"); //串口监视器显示Ultrasonic sensor:
pinMode(A0,INPUT);
pinMode(4, OUTPUT); //EA // LED Pin
pinMode(7, OUTPUT); //EB const int ledPin = A2;
pinMode(10, OUTPUT); //A1
pinMode(5, OUTPUT); //A1
pinMode(11, OUTPUT); //B1
pinMode(12, OUTPUT); //B2
digitalWrite(4,HIGH);
digitalWrite(7,HIGH);
myServo.write(90);
// Line Following Sensors
const int trct5000_02 = A1; // 左循迹传感器
const int trct5000_01 = A0; // 右循迹传感器
// Motor Control Pins (assuming L298N-like driver)
// Right Motor (User identified pin 5 as right motor)
const int ENA = 4; // 使能A
const int IN1 = 5; // 输入1 (速度)
const int IN2 = 10; // 输入2 (方向)
// Left Motor
const int ENB = 7; // 使能B
const int IN3 = 12; // 输入3 (速度)
const int IN4 = 11; // 输入4 (方向)
// --- Global Variables & Constants ---
float frontDistance;
float sideDistance;
const int OBSTACLE_THRESHOLD = 20; // 障碍物检测阈值 (cm)
int obstacleDetections = 0;
bool obstaclePreviouslySeen = false;
bool ignoreSideSensor = false; // 新增:用于在恢复后忽略侧方传感器
int sideObstacleCounter = 0; // 新增:用于过滤侧方传感器的抖动
int frontStopCounter = 0; // 新增:用于最终停止的防抖
// 定义状态机
enum State {
IDLE,
LINE_FOLLOWING,
PAUSED_BY_SIDE_OBSTACLE, // 新增:被侧方障碍物暂停的状态
STOPPED
};
State currentState = IDLE;
// --- Function Prototypes ---
void motorStop();
void motorForward(int speed);
void setup() {
Serial.begin(9600);
myServo.attach(servoPin, 500, 2500);
// 初始化超声波传感器引脚
pinMode(frontTrigPin, OUTPUT);
pinMode(frontEchoPin, INPUT);
pinMode(sideTrigPin, OUTPUT);
pinMode(sideEchoPin, INPUT);
// 初始化LED引脚
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH); // 初始为高电平,灯灭
// 初始化循迹传感器引脚
pinMode(trct5000_01, INPUT);
pinMode(trct5000_02, INPUT);
// 初始化电机控制引脚
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
// 设置初始状态
motorStop();
myServo.write(90); // 舵机居中
Serial.println("System Initialized. State: IDLE");
Serial.println("Waiting 3 seconds for sensors to stabilize...");
delay(3000); // 增加延时以确保传感器稳定
} }
void loop()
{
analogWrite(5, 200); void loop() {
analogWrite(10, 50); // 1. 读取前方传感器 (用于主流程控制)
Serial.println(digitalRead(trct5000_01)); digitalWrite(frontTrigPin, LOW);
analogWrite(11, 50); delayMicroseconds(2);
analogWrite(12, 200); digitalWrite(frontTrigPin, HIGH);
digitalWrite(TrigPin, LOW); //设置数字引脚4为低电平 delayMicroseconds(10);
delayMicroseconds(2); //产生一个2us延迟“delayMicroseconds();”为微秒级延时。 digitalWrite(frontTrigPin, LOW);
digitalWrite(TrigPin, HIGH); //设置数字引脚4为高电平 frontDistance = pulseIn(frontEchoPin, HIGH) / 58.00;
delayMicroseconds(10); //产生一个10us的高脉冲去触发TrigPin至少10us的高电平信号才能触发模块的测距功能。
digitalWrite(TrigPin, LOW); //设置数字引脚4为低电平
distance = pulseIn(EchoPin, HIGH) / 58.00; //检测脉冲宽度,并计算出距离
digitalWrite(TrigPin2, LOW); //设置数字引脚4为低电平 // 2. 状态机主要逻辑
delayMicroseconds(2); //产生一个2us延迟“delayMicroseconds();”为微秒级延时。 switch (currentState) {
digitalWrite(TrigPin2, HIGH); //设置数字引脚4为高电平 case IDLE:
delayMicroseconds(10); //产生一个10us的高脉冲去触发TrigPin至少10us的高电平信号才能触发模块的测距功能。 motorStop();
digitalWrite(TrigPin2, LOW); //设置数字引脚4为低电平 Serial.println("State: IDLE");
distance2 = pulseIn(EchoPin2, HIGH) / 58.00; //检测脉冲宽度,并计算出距离 // 检测前方障碍物以启动
if (frontDistance < OBSTACLE_THRESHOLD && frontDistance > 0 && !obstaclePreviouslySeen) {
obstacleDetections++;
Serial.print("Obstacle detected! Count: ");
Serial.println(obstacleDetections);
currentState = LINE_FOLLOWING;
Serial.println("State change -> LINE_FOLLOWING");
}
break;
case LINE_FOLLOWING:
// 检查前方障碍物以进入最终停止状态 (增加防抖逻辑)
if (frontDistance < OBSTACLE_THRESHOLD && frontDistance > 0) {
frontStopCounter++;
} else {
frontStopCounter = 0;
}
if(distance < 10) if (frontStopCounter >= 5) { // 要求连续5次检测到才触发最终停止
{ currentState = STOPPED;
flag = 1; Serial.println("State change -> STOPPED");
digitalWrite(4,LOW); break; // 退出 switch 语句
digitalWrite(7,LOW); }
// 检查侧方障碍物以进入暂停状态 (增加防抖逻辑)
if (!ignoreSideSensor) {
digitalWrite(sideTrigPin, LOW);
delayMicroseconds(2);
digitalWrite(sideTrigPin, HIGH);
delayMicroseconds(10);
digitalWrite(sideTrigPin, LOW);
sideDistance = pulseIn(sideEchoPin, HIGH) / 58.00;
if (sideDistance < OBSTACLE_THRESHOLD && sideDistance > 0) {
sideObstacleCounter++; // 如果检测到计数器加1
} else {
sideObstacleCounter = 0; // 如果没检测到,计数器清零
}
if (sideObstacleCounter >= 3) { // 要求连续3次检测到才触发暂停
currentState = PAUSED_BY_SIDE_OBSTACLE;
Serial.println("State change -> PAUSED_BY_SIDE_OBSTACLE");
sideObstacleCounter = 0; // 重置计数器
break;
}
}
// 执行循迹
motorForward(90); // 采用用户设定的速度
// 读取循迹传感器
bool leftOnLine = digitalRead(trct5000_02);
bool rightOnLine = digitalRead(trct5000_01);
// 使用舵机进行方向控制 (采用用户设定的角度)
if (leftOnLine && !rightOnLine) {
myServo.write(75);
} else if (!leftOnLine && rightOnLine) {
myServo.write(105);
}
break;
case PAUSED_BY_SIDE_OBSTACLE:
motorStop();
digitalWrite(ledPin, LOW); // 亮灯
Serial.println("State: PAUSED");
// 在暂停状态下,只检测前方障碍物以恢复运动
if (frontDistance < OBSTACLE_THRESHOLD && frontDistance > 0) {
digitalWrite(ledPin, HIGH); // 灭灯
ignoreSideSensor = true; // 设置标志位,永久忽略侧方传感器
currentState = LINE_FOLLOWING;
Serial.println("Resuming, ignoring side sensor from now on.");
// 为了防止这次检测被错误地计为第二次停止指令,短暂延时并重置检测标志
delay(200);
obstaclePreviouslySeen = true;
}
break;
case STOPPED:
motorStop();
break;
} }
else
{
flag = 0;
}
if(flag == 0)
{
if(distance2 < 20)
{
myServo.write(110);
digitalWrite(4,LOW);
digitalWrite(7,LOW);
}
else
{
digitalWrite(4,HIGH);
digitalWrite(7,HIGH);
}
}
//注意此语句中58.00前的“/”符号为算术运算符,表示除以。
Serial.print(distance); //串口监视器显示distance所测距离
Serial.println(); //串口监视器打印一行回车
Serial.print(distance2); //串口监视器显示distance所测距离
Serial.print("cm"); //串口监视器显示“cm”
Serial.println(); //串口监视器打印一行回车
delay(100); //延迟1秒1000毫秒
// 更新前方障碍物的检测标志位,用于识别"新"的障碍物事件
obstaclePreviouslySeen = (frontDistance < OBSTACLE_THRESHOLD && frontDistance > 0);
delay(10); // 采用用户设定的循环延时
}
// --- Motor Control Functions ---
// 电机停止 (根据L298N标准将输入引脚设为LOW实现刹车)
void motorStop() {
// 将方向控制引脚都设为LOW使电机刹车
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
// 为保险起见也将使能引脚的PWM输出设为0
analogWrite(ENA, 0);
analogWrite(ENB, 0);
}
// 电机前进 (实现"kick-start"以在低速下启动)
void motorForward(int speed) {
// 定义电机启动阈值和"kick"参数
const int START_THRESHOLD = 140; // 电机启动的最小PWM值
const int KICK_SPEED = 180; // 用于克服惯性的瞬时启动速度
const int KICK_DURATION = 20; // 启动脉冲的持续时间 (ms)
// 1. 设置电机方向为前进 (根据用户最新配置)
// 右电机
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
// 左电机 (用户已反转)
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
// 2. 使用PWM在Enable引脚上设置速度, 对低速进行"kick-start"
if (speed > 0 && speed < START_THRESHOLD) {
// 先用一个较高的速度"kick"一下来克服静摩擦力
analogWrite(ENA, KICK_SPEED);
analogWrite(ENB, KICK_SPEED);
delay(KICK_DURATION);
// 然后迅速降低到目标低速来维持运动
analogWrite(ENA, speed);
analogWrite(ENB, speed);
} else if (speed >= START_THRESHOLD) {
// 对于高于阈值的速度,直接设置
analogWrite(ENA, speed);
analogWrite(ENB, speed);
} else { // speed == 0
// 如果目标速度为0则调用刹车函数
motorStop();
}
} }

View File

@@ -0,0 +1,6 @@
## 2024-05-22 充电状态详情页跳转与参数修正 by AI助手
- 修正dashboard页面"查看详细状态"按钮,跳转链接由/charging-status改为/charging-status?id=activeSession.id确保跳转时带上会话ID。
- charging-status页面支持通过url参数id获取会话详情优先用/session/get?id=xxx否则用/session/my/active。
- 这样用户可以直接查看当前活跃会话详情也可以通过id参数查看历史会话详情。
- 代码高内聚低耦合,未影响其他业务逻辑。

View File

@@ -1,57 +0,0 @@
{
"groups": [
{
"name": "mqtt",
"type": "com.yupi.project.config.properties.MqttProperties",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
}
],
"properties": [
{
"name": "mqtt.broker-url",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.client-id-prefix",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.command-topic-base",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.connection-timeout",
"type": "java.lang.Integer",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.default-qos",
"type": "java.lang.Integer",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.keep-alive-interval",
"type": "java.lang.Integer",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.password",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.status-topic-base",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
},
{
"name": "mqtt.username",
"type": "java.lang.String",
"sourceType": "com.yupi.project.config.properties.MqttProperties"
}
],
"hints": []
}

View File

@@ -1,6 +0,0 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/my_db
username: root
password: 123456

View File

@@ -1,65 +0,0 @@
spring:
application:
name: mqtt-charging-system
# DataSource Config
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://yuyun-us1.stormrain.cn:3306/mqtt_power?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: mysql_a4MQ4P
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
# session 失效时间(秒)
session:
timeout: 86400
server:
port: 7529
servlet:
context-path: /api
session:
timeout: 86400 # 设置session的过期时间单位为秒这里设置为1天
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: isDelete # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
# Logging configuration
logging:
level:
# Set root logger level (e.g., INFO, WARN, ERROR, DEBUG)
root: INFO
# Set specific package levels
com.yupi.project: DEBUG # Example: Set your project's base package to DEBUG
org.springframework.web: INFO # Set Spring Web logging level
org.springframework.security: DEBUG # Enable Spring Security DEBUG logging
org.mybatis: INFO # Set MyBatis logging level
# ... other specific loggers
#file:
#name: logs/application.log # Log file name
#path: ./logs # Log file path
#pattern:
#console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
#file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
# ===================================================================
# MQTT Configurations
# ===================================================================
mqtt:
broker-url: tcp://broker.emqx.io:1883
username: # Public broker, no credentials specified for connection
password: # Public broker, no credentials specified for connection
client-id-prefix: backend-yupi-mqtt-power- # Unique client ID prefix for our project
default-qos: 1 # Default Quality of Service (0, 1, 2)
connection-timeout: 30 # Connection timeout in seconds
keep-alive-interval: 60 # Keep alive interval in seconds
command-topic-base: yupi_mqtt_power_project/robot/command # Prefixed base topic for sending commands
status-topic-base: yupi_mqtt_power_project/robot/status # Prefixed base topic for receiving status
task: # Task specific configurations
timeoutSeconds: 300 # Default 300 seconds (5 minutes) for a task to be considered timed out
timeoutCheckRateMs: 60000 # Default 60000 ms (1 minute) for how often to check for timed out tasks

View File

@@ -1 +0,0 @@
我的项目 by 程序员鱼皮 https://github.com/liyupi

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yupi.project.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.yupi.project.model.entity.User">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="userName" column="userName" jdbcType="VARCHAR"/>
<result property="userAccount" column="userAccount" jdbcType="VARCHAR"/>
<result property="userAvatar" column="userAvatar" jdbcType="VARCHAR"/>
<result property="gender" column="gender" jdbcType="TINYINT"/>
<result property="userRole" column="userRole" jdbcType="VARCHAR"/>
<result property="userPassword" column="userPassword" jdbcType="VARCHAR"/>
<result property="createTime" column="createTime" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="updateTime" jdbcType="TIMESTAMP"/>
<result property="isDelete" column="isDelete" jdbcType="TINYINT"/>
</resultMap>
<sql id="Base_Column_List">
id,userName,userAccount,
userAvatar,gender,userRole,
userPassword,createTime,updateTime,
isDelete
</sql>
<update id="updateUserBalance">
UPDATE user
SET balance = balance + #{amountChange}
WHERE id = #{userId}
<if test="amountChange.signum() == -1">
AND balance >= #{amountChange.abs()}
</if>
</update>
</mapper>