一种双灰度传感器巡黑线方案:完美解决小车循迹时左右摇摆的问题

背景

在使用灰度传感器制作巡迹小车时,传统循迹方法通常采用三灰度循迹,即在小车车体的左中右三个位置分别放置三个灰度传感器,根据传感器是否触发或是传感器返回的数值大小来判断车体的三种位置状态:中正、偏左、偏右,但是这样的循迹方式有一个很大的缺点,当车体发生一次歪斜之后,继续巡线过程中车体左右摇摆严重,非常影响小车前进速度以及前进方向的稳定。
本文提出一种双灰度传感器巡黑线的方案,经过实际验证,该方案很好的解决了上述问题。

传统的三灰度循迹传感器放置方案

注意: 在下面的图中,棕色的方形块和四个蓝色的圆柱表示一个小车的车体,眼睛表示灰度传感器,黑色的半透明阴影表示黑色的循迹线。
图1.直行状态(从底部向上看)
在这里插入图片描述
图2.左偏状态(从底部向上看)
通常情况下,当车体歪斜一个不小的角度时,小车右边的传感器才能检测到黑色,然后执行右转的代码,使车体回正,如下图所示;
在这里插入图片描述
图3.执行回正代码后小车状态(从底部向上看)
在回正的过程中,代码逻辑是要小车转向直到中间传感器检测到黑色,完成转向后,小车的状态通常是这样;此时中间传感器检测到黑线,判断为中正,保持直行,但是这样走一小段距离,小车左边传感器就检测到了黑线,又要往左转向,然后车体再次歪斜,再往右转向;如此周而复始,就造成了小车循迹过程中左右摇摆的现象。
在这里插入图片描述
图4.传统循迹方法下的小车轨迹
在这里插入图片描述

尝试新的传感器放置方案

为解决上述问题,我想了很多种方案,比如:

  • 在车体前部两侧放置两个传感器
    图5.双灰度并排两侧放置(从底部向上看)
    在这里插入图片描述
  • 在车体前部放置五个传感器
    图6.五灰度并排放置(从底部向上看)
    在这里插入图片描述
  • 在车体前部中间放一个传感器,后面两侧放两个传感器
    图7.三灰度三角形放置(从底部向上看)
    在这里插入图片描述
  • 在车体前部中间放一个传感器,中部两侧放两个传感器,后部中间放一个传感器
    图8.四灰度四边形放置(从底部向上看)
    在这里插入图片描述
    以上方案经过测试,都没有很好的解决左右摇摆的问题。
    最终在测试中,发现了下面的双灰度传感器并排中置方案,经过实际测试,发现巡线效果大幅提升,车体运行十分平稳。

双灰度传感器并排中置方案

注意: 在下面的图中,棕色的方形块和四个蓝色的圆柱表示一个小车的车体,两个眼睛表示两个灰度传感器,黑色的半透明阴影表示黑色的线。

  • 如图9所示,两个灰度传感器均能检测到黑色,此时车体中正,保持直行。
    图9.直行状态(从底部向上看)
    在这里插入图片描述
    图10:对应的俯视图
    在这里插入图片描述
  • 如图11所示,小车右边的传感器(对应仰视图的左边)检测不到黑线,而左边传感器(对应仰视图的右边)检测到了黑线,此时小车向右偏,应该左转。
    图11.左转状态(从底部向上看)
    在这里插入图片描述
    图12.对应的俯视图
    在这里插入图片描述
  • 如图13所示,小车左边的传感器(对应仰视图的右边)检测不到黑线,而右边传感器(对应仰视图的左边)检测到了黑线,此时小车向左偏,应该右转。
    图13.右转状态(从底部向上看)
    在这里插入图片描述
    图14.对应的俯视图
    在这里插入图片描述
    由于两传感器距离黑线边界较近,且传感器位置放置在车头处,车体稍稍发生一点偏斜,就立即触发转向程序,因而能及时纠正车体位置,不至于当车体偏离很大的角度后才触发转向,避免了左右摇摆的现象。
    图15.循迹效果示意
    在这里插入图片描述

2019.11.23 完善
注: 这里其实只提出了一个思路,因为是软硬件结合的,不同的硬件结构的代码也完全不一样,在实际操作的时候还是会遇到很多问题;
比如这种循迹方法只能巡直线和弯度不大的转弯,如果要过直角弯还需要增加两个灰度传感器进行辅助判断;
反正,就是具体问题具体解决嘛。。。
这里给出我的代码,仅供参考:) 希望能帮到你~

#include <Servo.h>

int sensor_pin[4] = {A0,A4,A2,A3};
int sensor_value[4] = {0,0,0,0};
const int LEFT = 0;//normal left
const int RIGHT = 1;//normal right
const int LEFT_RightAngle = 2;//right angle left
const int RIGHT_RightAngle = 3;//right angle right
const int STRAIGHT = 4;
const int BlackLimit_ML = 680;
const int BlackLimit_MR = 550;
const int BlackLimit_L = 500;
const int BlackLimit_R = 500;
const int HIGH_RIGHT = 234;
Servo myservo;
boolean isobstacle = 0;

void setup(){
  pinMode(5,OUTPUT);//motor out
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);//motor out
  pinMode(10,OUTPUT);
  pinMode(8,INPUT);//touch sensor
  
  myservo.attach(11);
  Serial.begin(9600);
}

//judge whether an obstacle in front of us,return true when it is 
boolean is_obstacle(){
  isobstacle = digitalRead(8);
  return isobstacle;
}

//read sensor value
void read_value(){
  for(int i = 0;i < 4;i++){
    sensor_value[i] = analogRead(sensor_pin[i]);
  }
}

//turn to a direction
void turn(const int direction_){
  switch(direction_){
    case LEFT:
      analogWrite(5,LOW);
      digitalWrite(6,HIGH_RIGHT);//right wheel
      digitalWrite(9,LOW);
      digitalWrite(10,LOW);//left wheel
      while(sensor_value[2] > BlackLimit_MR){//sensor_value[1] > BlackLimit_M     
        read_value();
        delay(5);
      }
      break;
    case RIGHT:
      digitalWrite(5,LOW);
      analogWrite(6,LOW);//right wheel
      digitalWrite(9,LOW);
      digitalWrite(10,HIGH);//left wheel
      while(sensor_value[1] > BlackLimit_ML){//sensor_value[1] > BlackLimit_M        
        read_value();
        delay(5);
      }
      break;
    case LEFT_RightAngle:
      digitalWrite(5,LOW);
      analogWrite(6,HIGH_RIGHT);//right wheel
      digitalWrite(9,HIGH);
      digitalWrite(10,LOW);//left wheel
      while(sensor_value[1] > BlackLimit_ML){//sensor_value[1] > BlackLimit_M        
        read_value();
        if(sensor_value[3] < BlackLimit_R)
           break;
        delay(5);
      }
      break;
    case RIGHT_RightAngle:
      digitalWrite(5,HIGH_RIGHT);
      analogWrite(6,LOW);//right wheel
      digitalWrite(9,LOW);
      digitalWrite(10,HIGH);//left wheel
      while(sensor_value[2] > BlackLimit_MR){//sensor_value[1] > BlackLimit_M        
        read_value();
        if(sensor_value[0] < BlackLimit_L)
           break;
        delay(5);
      }
      break;
    case STRAIGHT:
      digitalWrite(5,LOW);
      analogWrite(6,HIGH_RIGHT);//right wheel
      digitalWrite(9,LOW);
      digitalWrite(10,HIGH);//left wheel
      break;
    default:
      digitalWrite(5,LOW);
      analogWrite(6,HIGH_RIGHT);//right wheel
      digitalWrite(9,LOW);
      digitalWrite(10,HIGH);//left wheel
      break;
  }
}

//judge what direction to turn with sensor value
void judge_turn(){
  read_value();
  if(sensor_value[0] < BlackLimit_L && sensor_value[3] > BlackLimit_R){//turn left right tangle
    Serial.println(LEFT_RightAngle); 
    turn(LEFT_RightAngle);
  }
  else if(sensor_value[0] > BlackLimit_L && sensor_value[3] < BlackLimit_R){//turn right right tangle
    Serial.println(RIGHT_RightAngle);
    turn(RIGHT_RightAngle);
  }
  else if(sensor_value[1] > BlackLimit_ML && sensor_value[2] < BlackLimit_MR){
    Serial.println(RIGHT); 
    turn(RIGHT);
  }
  else if(sensor_value[1] < BlackLimit_ML && sensor_value[2] > BlackLimit_MR){
    Serial.println(LEFT); 
    turn(LEFT);
  }
  else{
    Serial.println(STRAIGHT);
    turn(STRAIGHT);   
  }
}

void run(){
   judge_turn();
   delay(10);
}

void loop(){
 run();  
}
<think>嗯,用户想了解新手如何使用灰度传感器循迹。首先,我得确保自己理解灰度传感器的工作原理。灰度传感器通常是通过发射红外光并接收反射回来的光强度来检测地面颜色的深浅,对吧?这样可以根据反射光的强弱来判断是否在轨迹线上。 接下来,可能需要分步骤来解释。首先,用户可能需要了解硬件连接,比如如何将灰度传感器连接到控制器,比如Arduino或者单片机。然后是关于校准传感器的部分,因为不同地面的反射情况可能不同,校准能帮助确定阈值。 然后是关于编程的部分,可能需要用简单的条件判断,比如如果传感器读数大于阈值,说明在轨迹外,需要调整电机方向;反之则在轨迹上,保持直行。不过,如果用户使用的是多个传感器,比如左右两个,逻辑会更复杂一些,可能需要根据哪边的传感器检测到黑线来调整转向。 另外,用户可能作为新手,对PID控制还不熟悉,所以是否需要提到简单的比例控制?或者先介绍基本的开关控制,再逐步进阶?可能需要先确保基础部分讲清楚,避免信息过载。 还有可能遇到的实际问题,比如环境光干扰,可能需要建议使用固定的光照条件或者屏蔽罩。或者传感器安装高度的问题,太高或太低都会影响读数,需要调整到合适的位置。 测试和调试也是重要的步骤,用户可能需要知道如何通过串口监视器查看传感器数值,调整阈值,然后进行实际路测,根据小车的反应进一步优化参数。 另外,可能需要提醒常见的错误,比如阈值设置不当导致小车摇摆,或者响应速度过快导致震荡。这候可能需要引入延或者更复杂的控制算法,但作为新手,可能先从简单的开始。 总结一下,回答的结构应该是:介绍原理,硬件连接,校准,编程逻辑,调试优化,常见问题解决。这样步骤清晰,用户能一步步跟着操作。同要确保用中文,避免专业术语过多,必要解释概念,比如阈值是什么,如何确定。 最后检查一下是否需要补充示例代码片段,或者示意图,但用户要求的是文字回答,所以用文字描述清楚即可。注意LaTeX格式的应用,比如变量用$...$,公式用$$...$$,但根据系统指令,行内变量要用$,比如传感器读数为$x$,阈值$T$等。确保语法正确,结构清晰,分点说明。</think>好的,我将分步骤为您讲解灰度传感器循迹的实现方法,包含硬件准备、代码逻辑和调试技巧。 一、硬件基础 1. 传感器原理 灰度传感器通过发射红外光并检测反射强度(通常返回$0\sim4095$的模拟量),数值越小表示反射面颜色越深。例如: - 黑线区域反射值:$V_{black} \approx 800$ - 白底区域反射值:$V_{white} \approx 3000$ 2. 接线方式 建议至少使用3个传感器(左/中/右),典型接线: ``` 传感器 | Arduino引脚 左 → A0 中 → A1 右 → A2 ``` 二、关键参数校准 1. 阈值确定 $$ T = \frac{V_{white} + V_{black}}{2} $$ 例:当$V_{white}=3000$, $V_{black}=800$,$T=1900$ 2. 安装高度测试 通过实验找到最佳距离(通常$2\sim5cm$),确保: $$ |V_{white} - V_{black}| > 500 $$ 三、基础循迹算法 1. 三传感器逻辑判断 ```arduino int left = analogRead(A0); int center = analogRead(A1); int right = analogRead(A2); if(center < T){ // 中线检测 goStraight(); } else if(left < T){ // 左偏修正 turnRight(); } else if(right < T){ // 右偏修正 turnLeft(); } ``` 2. 进阶PID控制(简化版) ```arduino float Kp = 0.3; // 比例系数 int error = (left - right); // 偏差计算 int adjust = Kp * error; setMotorSpeed(leftBaseSpeed - adjust, rightBaseSpeed + adjust); ``` 四、调试技巧 1. 实监控数据 使用串口打印传感器值: ```arduino Serial.print("L:"); Serial.print(left); Serial.print(" C:"); Serial.print(center); Serial.print(" R:"); Serial.println(right); ``` 2. 动态阈值调整 对于复杂环境,可加入自动校准: ```arduino void autoCalibrate(){ int black = min(left, min(center, right)); int white = max(left, max(center, right)); T = (black + white) / 2; } ``` 五、常见问题解决 1. 抖动严重 → 降低Kp值或增加延 2. 丢失路径 → 增大传感器间距(建议$2\sim3cm$) 3. 响应迟钝 → 检查供电电压是否稳定(推荐5V±5%) 建议先用低速(PWM值约80~120)进行测试,稳定后再逐步提升速度。实际应用中可以结合编码器实现速度闭环控制。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值