Arduino智能小车——超声波避障

2023-10-27

Arduino智能小车——超声波避障

Arduino智能小车系列教程时空门:

  1. Arduino智能小车——拼装篇 点击跳转
  2. Arduino智能小车——测试篇 点击跳转
  3. Arduino智能小车——调速篇 点击跳转
  4. Arduino智能小车——超声波避障 点击跳转
  5. Arduino智能小车——蓝牙小车 点击跳转
  6. Arduino智能小车——循迹篇 点击跳转
  7. Arduino智能小车——小车测速 点击跳转

  经过前几篇的测试大家应该对小车有一定的认识了,在实际的操作过程中经常会由于操作不当各种碰壁吧?那这次我们将给小车装上一只“眼睛”,让小车看到障碍,躲避障碍。

准备材料

超声波模块HC-SR04

  在这里简单说下超声波测距的原理,相信大家也都知道。超声波发射装置发出超声波,它的根据是接收器接到超声波时的时间差,与雷达测距原理相似。 超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。

  1. 采用Trig引脚触发,给至少10us的高电平脉冲信号
  2. 模块自动发送8个40kHz的方波,自动检测是否有信号返回
  3. 有信号返回,通过Echo引脚输出一个高电平脉冲,高电平脉冲持续的时间就是超声波从发射到反射返回的时间。距离=(高电平脉冲时间*340)/2。(声音在空气中传播速度为340m/s)
主要技术参数
工作电压 DC5V
静态电流 <2mA
输出电平 0-5V
感应角度 ≤15度
探测距离 2cm-450cm
最高精度 0.3cm

###舵机
  在这里推荐9G舵机SG90,或者其他类似的舵机,这种舵机体积比较小,扭矩虽然不是大, 但是足够带动简易云台,很方便在小车上使用,大家购买时注意舵机的转动角度,有55度的,180度的等等,大家可以根据需要购买。

舵机固定架

  舵机固定架的购买一定要配合舵机,所以大家购买的时候注意尺寸哦!!~

舵机安装

  舵机在安装之前大家一定要记得校准,为什么要校准那,这个跟舵机的工作原理有关。控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。
  舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分,总间隔为2ms。以180度角度伺服为例,那么对应的控制关系是这样的:

高电平时间 对应位置
0.5ms 0度
1.0ms 45度
1.5ms 90度
2.0ms 135度
2.5ms 180度

  也就是说当对舵机输入相同控制信号时,舵机会运动到固定位置,他的动作不是做圆周运动,而是在运动范围内,每一个位置对应一个控制信号。

  因此我们需要在将舵机安装在固定架上之前,需要先将舵机初始化好,舵机一般为三根线:棕色——GND,红色——VCC,橙色——控制信号。因此我们将棕色线接到GND,红色线接到“+5V”引脚,橙色线接到“9”引脚,初始化程序如下:

#include <Servo.h>  //加入含有舵机控制库的头文件

#define PIN_SERVO 9  //舵机信号控制引脚
Servo myservo;  
  
void setup()  
{  
  myservo.attach(PIN_SERVO);  //舵机初始化
}  
  
void loop()  
{  
  myservo.write(90);  //PWM输出
} 

  在舵机初始化好之后将其安装在固定架上,然后装配在小车上,此时保证超声波模块超前。

超声波接线

  估计不少朋友早已经发现板子上的“+5V”和“GND”引脚已经不够用了,这个时候你们可以向我这样焊一个扩展板出来,上面固定两排排针,一排用来扩展“+5V”,一边用来扩展“GND”引脚。

  超声波模块有四个引脚,“VCC”接到Arduino UNO开发板的“+5V”引脚,“GND”接到开发板“GND”引脚,“Trig”引脚接到开发板“8”引脚,“Echo”引脚接到开发板“7”引脚

  线太乱了,真的没办法整理,我自己都没眼看。

代码测试

#include <Servo.h>

#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4

int leftMotor1 = 16;
int leftMotor2 = 17;
int rightMotor1 = 18;
int rightMotor2 = 19;

int leftPWM = 5;
int rightPWM = 6;

Servo myServo;  //舵机

int inputPin=7;   // 定义超声波信号接收接口
int outputPin=8;  // 定义超声波信号发出接口

void setup() {
  // put your setup code here, to run once:
  //串口初始化
  Serial.begin(9600); 
  //舵机引脚初始化
  myServo.attach(9);
  //测速引脚初始化
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
  pinMode(leftPWM, OUTPUT);
  pinMode(rightPWM, OUTPUT);
  //超声波控制引脚初始化
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  avoidance();
}
void motorRun(int cmd,int value)
{
  analogWrite(leftPWM, value);  //设置PWM输出,即设置速度
  analogWrite(rightPWM, value);
  switch(cmd){
    case FORWARD:
      Serial.println("FORWARD"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case BACKWARD:
      Serial.println("BACKWARD"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      Serial.println("STOP"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}
void avoidance()
{
  int pos;
  int dis[3];//距离
  motorRun(FORWARD,200);
  myServo.write(90);
  dis[1]=getDistance(); //中间
  
  if(dis[1]<30)
  {
    motorRun(STOP,0);
    for (pos = 90; pos <= 150; pos += 1) 
    {
      myServo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
    }
    dis[2]=getDistance(); //左边
    for (pos = 150; pos >= 30; pos -= 1) 
    {
      myServo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
      if(pos==90)
        dis[1]=getDistance(); //中间
    }
    dis[0]=getDistance();  //右边
    for (pos = 30; pos <= 90; pos += 1) 
    {
      myServo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
    }
    if(dis[0]<dis[2]) //右边距离障碍的距离比左边近
    {
      //左转
      motorRun(TURNLEFT,250);
      delay(500);
    }
    else  //右边距离障碍的距离比左边远
    {
      //右转
      motorRun(TURNRIGHT,250);
      delay(500);
    } 
  }
}
int getDistance()
{
  digitalWrite(outputPin, LOW); // 使发出发出超声波信号接口低电平2μs
  delayMicroseconds(2);
  digitalWrite(outputPin, HIGH); // 使发出发出超声波信号接口高电平10μs,这里是至少10μs
  delayMicroseconds(10);
  digitalWrite(outputPin, LOW); // 保持发出超声波信号接口低电平
  int distance = pulseIn(inputPin, HIGH); // 读出脉冲时间
  distance= distance/58; // 将脉冲时间转化为距离(单位:厘米)
  Serial.println(distance); //输出距离值
 
  if (distance >=50)
  {
    //如果距离小于50厘米返回数据
    return 50;
  }//如果距离小于50厘米小灯熄灭
  else
    return distance;
}

代码详解

“Trig”引脚控制超声波发出声波,对应int outputPin=8;
“Echo”引脚反应接收到返回声波,对应int inputPin=7;

int inputPin=7;   // 定义超声波信号接收接口
int outputPin=8;  // 定义超声波信号发出接口

int getDistance()函数解析

超声波发出引脚“Trig”为高时对外发出超声波,为保证发出10μs声波,因此在发送之前需要将该引脚拉低,并给他一定反应时间。

digitalWrite(outputPin, LOW); // 使发出发出超声波信号接口低电平2μs
delayMicroseconds(2);

之后发送10μs超声波

digitalWrite(outputPin, HIGH); // 使发出发出超声波信号接口高电平10μs,这里是至少10μs

声波发送之后禁止其继续发送,同时开始检测是否反射回来的声波

digitalWrite(outputPin, LOW); // 保持发出超声波信号接口低电平
  int distance = pulseIn(inputPin, HIGH); // 读出脉冲时间

pulseIn()单位为微秒,声速344m/s,所以距离cm=344100/1000000pulseIn()/2约等于pulseIn()/58.0distance= distance/58; // 将脉冲时间转化为距离(单位:厘米)
超声波模块工作受物体表面反射程度影响,并且在传播过程中信号强度容易衰减,因此该模块适用的检测距离有限,一般在50cm以内相对正确,而且我们在避障时不需要检测太远的距离,因此超过50cm以上的都按50cm计算

if (distance >=50)
  {
    //如果距离小于50厘米返回数据
    return 50;
  }//如果距离小于50厘米小灯熄灭
  else
    return distance;

void avoidance()函数解析

小车前进过程中只检测前方距离障碍的距离,并且控制舵机,保持超声波模块位于正前方。

motorRun(FORWARD,200);
  myServo.write(90);
  dis[1]=getDistance(); //中间

当检测到小车前方距离障碍距离小于30cm时停车,检测两边距离。

motorRun(STOP,0);

控制舵机每次运动一个周期后都返回正前方位置。由于舵机运动需要一定的时间,因此在每转过一个角度的时候都延时delay(15),供其运动。

for (pos = 90; pos <= 150; pos += 1) 
{
      myServo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
}

当运动到最左边时检测小车左边距离障碍的距离

dis[2]=getDistance(); //左边

向右边运动,检测右边距离

for (pos = 150; pos >= 30; pos -= 1) 
{
  myServo.write(pos);              // tell servo to go to position in variable 'pos'
  delay(15);                       // waits 15ms for the servo to reach the position
  if(pos==90)
    dis[1]=getDistance(); //中间
}
dis[0]=getDistance();  //右边

将前边、左边、右边距离障碍的距离都检测结束之后,舵机回到最初位置。

for (pos = 30; pos <= 90; pos += 1) 
{
  myServo.write(pos);              // tell servo to go to position in variable 'pos'
  delay(15);                       // waits 15ms for the servo to reach the position
}

注意事项

1.舵机使用时要防止其堵转,因为点击堵转时电流会增大,很容易烧坏舵机。
2.舵机的红色电源线接入电压一般要大于等于其工作电压,供电不足会导致舵机不停自传。
3.Arduino 《Servo.h》库里提供的write()函数输出的PWM即为舵机专用的20ms为周期的PWM波,如果使用其他开发板或者函数的话,请务必保证输出方波周期为20ms,否则舵机不会受控制

总结

  这一篇讲解了舵机和超声波模块的使用方法,舵机在大家以后的开发生涯中应该会经常用到,因此舵机的使用规则(控制周期为20ms)请大家一定要记清楚,在舵机不受控制的时候建议大家可以购买一个舵机测试仪来测试舵机。

欢迎各位有兴趣的朋友加入Q群1:789127261 点评、交流

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Arduino智能小车——超声波避障 的相关文章

  • arduino安装DHT11库

    步骤操作如下 打开Arduino IDE 在菜单栏中选择 项目 gt 加载库 gt 管理库 在库管理器的搜索框中 输入 DHT sensor library 找到 DHT sensor library by Adafruit 点击 安装 安
  • Arduino 中的字符串比较

    我正在开发基于网络的家庭自动化系统 因此我的Arduino向服务器发送请求并在串行监视器中获得以下响应以及 loneOn 这是由于Serial println r 陈述 HTTP 1 1 200 OK Date Mon 13 Oct 201
  • 如何在 Arduino 上比较 __FlashStringHelper* 和 char*?

    我有一个将文本行输出到串行的板 我需要将这些文本行与我所知道的文本进行比较 本质上 我想做strcmp thestring F knownstring 但是似乎没有一个采用 FlashStringHelper 类型的 strcmp 版本 有
  • PySerial 从 Arduino 读取线路的延迟

    我正在使用带有基本 DigitalReadSerial 设置的 arduino uno 如下所述 http arduino cc en Tutorial DigitalReadSerial http arduino cc en Tutori
  • Arduino:使用串口和软件串口与蓝牙模块

    我的目的是使用 Arduino 通过 HC 05 蓝牙模块在 PC 和 Android 设备之间建立通信 我使用 PC 和 Arduino 串行监视器 之间的 USB 通信以及 SoftwareSerial 来连接到 HC 05 我的问题是
  • 如何将 ASCII char 转换为其 ASCII int 值?

    我想将 char 转换为它的ASCII http en wikipedia org wiki ASCIIint 值 我可以用所有可能的值填充一个数组并与其进行比较 但这对我来说似乎不正确 我想要类似的东西 char mychar k pub
  • 经典蓝牙 (2.1) 设备可实现的最小数据包延迟是多少?

    我正在使用 RN42 http www microchip com wwwproducts en RN42 http www microchip com wwwproducts en RN42 蓝牙模块以 115200 波特率 UART S
  • Python自动选择串口(适用于Arduino)

    目前 Python 程序必须知道设备 Arduino 位于哪个端口 然后 Python 才能与设备进行通信 Problem 每当设备拔出并重新插入时 其 COM 端口都会发生变化 因此必须再次向 Python 提供正确的串行端口 以便它找到
  • 如何实现Android Open Accessory模式即服务?

    我一直在研究 Android 开放配件开发套件 依照指示Google 提供的 DemoKit 示例 http developer android com guide topics usb accessory html 我在使解决方案适应我的
  • Arduino 的 C++ 类文件中的字符串无法编译

    我正在用 C 为 Arduino 编写一个堆栈类sketch http www arduino cc en Tutorial Sketch 我相信它完全符合AVR 如果这就是它的名字 我记不清了 编译器 我都用过malloc and fre
  • HM10 ble改变特征值AT命令Arduino

    谁能帮我用AT命令写入特征值 或者如何使用Hm10模块将数据从arduino发送到另一个ble设备 HM10发送AT START后 会通告数据包 并且可以检测服务和特征 但特征值是默认的0x00 如何更改 多次检查数据表 但找不到能够执行相
  • 为什么使用 libnfc 和 PN532 SHIELD 时“找不到 NFC 设备”

    我在 ArchLinux 上安装了 libnfc 并通过我的 Adafruit PN532 arduino SHIELD 使用它 nfc list返回消息 未找到 NFC 设备 我在 etc nfc devices d pn532 conf
  • Arduino:连接字符串时崩溃和错误

    我尝试将 AES 256 加密的输出连接到一个字符串 将此字符串与从 Android 手机发送的加密字符串进行比较 基本上 连接似乎有效 但在几次运行后会出现错误 不可读的字符 字符串变得更短而不是更长 或崩溃 它是可重现的 重启后在同一点
  • 如何使用GSM模块SIM800和Arduino Uno发送短信?

    我正在尝试通过 SIM800 GSM 模块从 Arduino 发送短信 消息到达给定号码 但格式不正确 它显示 消息格式不支持 我在这里添加了我的代码 非常感谢您的快速回复 include
  • 在声明对象之前在全局函数中使用类的成员函数

    我有一个事件处理函数 在该函数中调用了类的成员函数 事件处理函数在类 cpp 文件中声明 但不是类的一部分 它不是成员函数 当我编译代码时 编译器表示该函数在范围内 因为它正在调用全局事件处理函数中的成员函数 我的问题如下 有没有办法在全局
  • Arduino C++ 析构函数?

    我知道在Arduino中你不能使用delete 那么什么时候调用 C 类中定义的析构函数呢 同样 如果我想创建一个指向数组的指针 我必须使用malloc and free 当对象被销毁时 析构函数被调用 对于自动 堆栈上 变量 它在离开其作
  • Arduino串口数据解析

    我正在编写一个应用程序 通过蓝牙用我的 Android 手机控制我的机器人 一切都很顺利 数据得到回显和验证 但我在协议方面遇到了一些问题 特别是我希望我的机器人的轮子在我发送时转动一个命令 例如s 10 100 or s 30 10 数值
  • 无法将 minicom 的输出保存到文件中

    当我使用Minicom从串口捕获数据时 我需要将大数据保存到一个文件中 名为minicom cap 但是 如果我按 Ctrl A 和 L 来捕获文件 则失败 没有创建文件 minicom cap之前并不存在 我的下载目录已正确创建 我的操作
  • 仅从日期计算夏令时

    我正在使用 Arduino 和实时时钟芯片 该芯片补偿了闰年等 因此它始终具有正确的日期 但我认为由于区域复杂性 它不处理夏令时 时钟可以给出日 月 年 从 1 开始 以及星期几 星期日 0 到星期六 6 因为我需要与用户输入的日期和时间进
  • Arduino Uno 上传失败

    我收到了一个 arduino uno 和一些代码 作为我正在从事的项目的一部分 我以前从未与他们合作过 所以这对我来说是一次全新的体验 我正在使用他们网站上的 Arduino 应用程序 当我编译代码时 它工作正常 然而 当我上传时 我收到错

随机推荐

  • 开发模式下的测试:简化链码的测试过程

    1 启动网络 先打开终端1窗口 当前终端 为了确保系统中的Docker镜像文件是完整的 首先使用docker images命令查看Docker镜像信息 显示本地Docker Registry sudo docker images 在dev模
  • vue中使用vab-magnifier实现放大镜效果

    效果图如下 1 首先 使用npm或yarn安装vab magnifier插件 npm install vab magnifier 或 yarn add vab magnifier 2 在Vue组件中引入vab magnifier插件 imp
  • 区块链学习1:以太坊私有网络搭建

    参考教程 https blog csdn net xuguangyuansh article details 80308218 https blog csdn net u012254277 article details 66477769
  • 【自然语言处理入门】01:利用jieba对数据集进行分词,并统计词频

    一 基本要求 使用jieba对垃圾短信数据集进行分词 然后统计其中的单词出现的个数 找到出现频次最高的top100个词 二 完整代码 coding UTF 8 from collections import Counter import j
  • Linux系统之openEuler安装部署

    Linux系统之openEuler安装部署 一 openEuler介绍 1 openEuler简介 2 openEuler的硬件要求 物理机的安装要求 虚拟机的安装要求 二 下载openEuler系统镜像 1 官方网址 2 下载openEu
  • 正则表达式的简单说明

    标识符 说明 备注 d 0 9任意一个数字 w 任意一字母或者下划线或数字 a z A Z 0 9 和 s 包括空格 制表符 换行符等空白字符的其中一个 小数点可以匹配任意字符 除了换行符 如果要匹配包含 n 在内的所有字符 一般使用 s
  • SpringCloud——pom文件初始配置

    SpringCloud pom文件初始配置 由于版本的不断更新 如果出现dependency XXX not found 更换版本号就行可以了
  • SuperSet 最新 Docker 部署文档(含元数据库切换、汉化、镜像导出)

    网上大部分文档还是 Python2 的部署方式 尤其是汉化 坑还有不少 使用最新docker镜像重新整理了一下 一 安装开源标准版 能够访问互联网 1 安装 docker 2 下载官方 docker 镜像 docker pull apach
  • Ajax和jQuery

    Ajax和jQuery 一 Ajax 1 Ajax概述 ajax Asynchronous JavaScript and XML Asynchronous 异步的 JavaScript 和 XML Asynchronous 异步的意思 Ja
  • vue2 webpack3 升级 webpack5 全过程,内附详细配置文件

    由于 webpack5 需要 node 版本 gt 10 13 0 请先将 node版本 升级 npm版本 用node自带的就可以了 官方最新的node版本好像不兼容下面的webpack5配置 建议升级成 LTS版本 这里我升级的是v14
  • uni-app中onLoad不起作用

    最近开始使用uni app 坑还是很多的 今天在使用onLoad是发现 页面上的onLoad方法是可以起作用的 但是组件中的onLoad方法并没有起作用 后来经过一番尝试后还是不行 看文档发现uni app 支持的页面生命周期函数是包含on
  • 《质量全面管控-从项目管理到容灾测试》读书笔记

    介绍 该书是葛长芝老师 结合案例讲解测试实践技术 主要内容有 项目管理 项目需求管理 代码质量控制 自动化部署 软件测试 安全测试与安全管理 自动化测试基础 自动化测试框架 性能测试 性能分析 监控平台与故障排查 灾难恢复与容灾测试等 这本
  • 怎样才能提升广告投放效果?牢记这5点!

    在我们的日常生活中 广告可以说一个随处可见 无所不在的一个事物 各行各业的组织 企业等都会进行广告宣传 以达到推广产品或建立企业形象等商业目的 广告虽然非常常见 覆盖率比较高 广告的代言人和展现形式也不尽相同 但并不是所有的广告都能达到预期
  • UE4C++ 如何在静态函数中SpawnActor

    最近在学习用C 复刻YouTube上的一个蓝图库存系统 其中在从背包中丢弃枪械 然后在世界中生成一个枪械Actor的时候遇到了问题 由于这个生成函数是在几个不同的类中复用的 YouTube原版的做法是把这个函数写在了GameState类里面
  • Java基础面试题附带答案

    091内部类可以引用它的包含类 外部类 的成员吗 有没有什么限制 一个内部类对象可以访问创建它的外部类对象的成员 包括私有成员 092Anonymous Inner Class 匿名内部类 是否可以继承其它类 是否可以 实现接口 可以继承其
  • 黑苹果睡眠唤醒usb失灵_(已解决)睡眠唤醒重启的问题

    Time stamp Domain Message Duration Delay UUID 0C11AC44 B57D 4895 8067 C95A
  • Intent详细用法介绍

    文章目录 Intent介绍 Intent属性 intent种类 intent过滤器 Intent介绍 Activity Service Broadcast通信 可传递Bundle Intent属性 Component name属性 通过se
  • 2020年网络系统管理改革试点评分标准四

    考核点11 在D盘查看备份文件 分数 5分 考点答案 考点解析 本题考查点 组策略备份 配置案例 依次选择 服务器管理器 工具 组策略管理 在 组策略管理 窗口中依次选择 林 域 ChinaSkills cn 右键单击 组策略对象 在弹出的
  • 2D Alpha Shape:基于二维Delaunay(德洛内)三角网的边缘点查找

    文章目录 0 效果 1 Delaunay三角网基本内容 2 Bowyer Watson算法 3 基于Delanay方法的Alpha Shape确定散乱点的边缘点 4 代码实现 C Opencv 5 参考 0 效果 点间距10 1 Delau
  • Arduino智能小车——超声波避障

    Arduino智能小车 超声波避障 Arduino智能小车系列教程时空门 Arduino智能小车 拼装篇 点击跳转 Arduino智能小车 测试篇 点击跳转 Arduino智能小车 调速篇 点击跳转 Arduino智能小车 超声波避障 点击