使用asp.net core 3.0 搭建智能小车2

  上一篇中我们把基本的运行环境搭建完成了,这一篇中,我们实战通过树莓派B+连接HC-SR04超声波测距传感器,用c# GPIO控制传感器完成距离测定,并将距离显示在网页上.

1.HC-SR04接线

  传感器如下图:

  使用asp.net core 3.0 搭建智能小车2_第1张图片

  HC-SR04 模块可以测量 3cm – 4m 的距离,精确度可以达到 3mm.这个模块包括 超声波发射器、超声波接收器和控制电路三部分.该传感器有4个引脚:

  VCC,   超声波模块电源脚,接5V电源即可

  Trig,  超声波发送脚

  Echo,超声波接收检测脚

  GND,接地

1.1HC-SR04超声波模块工作原理:      

  (1) 树莓派向 Trig 脚发送一个至少 10us 的脉冲信号。
  (2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波
  (3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
  (4) Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
  (5) 计算距离:距离(单位:m)  =  (start - end) * 声波速度 / 2 

        使用asp.net core 3.0 搭建智能小车2_第2张图片

1.2 接线

      4 个引脚由 2 个电源引脚(Vcc 、GND)和 2 个控制引脚(Trig、Echo)组成。
  Vcc 和 Gnd 接 5v DC 电源,但不推荐用独立电源给它供电,应使用树莓派的 GPIO 口输出 5v 和 Gnd 给它供电。不然会影响这个模块的运行。
  Trig 引脚用来接收来自树莓派的控制信号。接任意 GPIO 口。
  Echo 引脚用来发送测距结果给树莓派。接任意 GPIO 口。

  对应树莓派40pin引脚对照表:

使用asp.net core 3.0 搭建智能小车2_第3张图片

     我这里把Trlg接到23,Echo接到24,VCC接到5V,(BCM编码方式)GND接GND:

    使用asp.net core 3.0 搭建智能小车2_第4张图片

    这样线就接好了.开始编码阶段.

2.c# 程序

  打开上一章建立的空项目首先在Models新建一个类 SiteConfig:

 public class SiteConfig
 {
       
        /// 
        /// 超声波控制端 默认23
        /// 
        public int TriggerPin { get; set; }

        /// 
        /// 超声波接收端 默认24
        /// 
        public int EchoPin { get; set; }
     
 }

之后在 appsettings.json 中添加 这个节点:

"SiteConfig": { 
    "TriggerPin": 23,
    "EchoPin": 24,
  }

在 Startup.cs 类的ConfigureServices 方法添加

 services.Configure(Configuration.GetSection("SiteConfig"));

这样通过依赖注入我们就可以使用我们配置的变量了.这些无关紧要的东西写完后,我们在项目中新建文件夹 Playground 并在这个文件下建立Ultrasonic文件夹:

 使用asp.net core 3.0 搭建智能小车2_第5张图片

在Ultrasonic里面建立三个文件 IHcsr04Client.cs,Hcsr04Client.cs,Hcsr04ReadEventArgs.cs

IHcsr04Client:

 public interface IHcsr04Client
    {
        event EventHandler OnDataAvailable;
        void Start();
        void Stop();
    }

Hcsr04Client:

 

 public class Hcsr04Client : IHcsr04Client
    {
        private readonly int _echo;
        private readonly int _trigger;
        private int _lastMeasurment = 0;       
        public const int NoObstacleDistance = -1;
        private readonly object _locker = new object();
        private readonly GpioController _controller;
        private readonly Stopwatch _timer = new Stopwatch();
        public event EventHandler OnDataAvailable;

        public bool IsRunning { get; set; }

        public Hcsr04Client(IOptions option, GpioController controller)
        {
            _echo = option.Value.EchoPin;
            _trigger = option.Value.TriggerPin;
            _controller = controller;
        }

        public void Start()
        {
            lock (_locker)
            {
                IsRunning = true;
                if (!_controller.IsPinOpen(_echo))
                    _controller.OpenPin(_echo, PinMode.Input);

                if (!_controller.IsPinOpen(_trigger))
                {
                    _controller.OpenPin(_trigger, PinMode.Output);
                    _controller.Write(_trigger, PinValue.Low);
                }
                Task.Run(() => PerformContinuousReads());
            }
        }
        public void Stop()
        {
            lock (_locker)
            {
                IsRunning = false;
                if (_controller.IsPinOpen(_trigger))
                    _controller.ClosePin(_trigger);
                if (_controller.IsPinOpen(_echo))
                    _controller.ClosePin(_echo);
            }
        }
        private void PerformContinuousReads()
        {
            while (IsRunning)
            {
                var sensorData = RetrieveSensorData();

                if (!IsRunning) continue;
                OnDataAvailable?.Invoke(this, sensorData);
                Thread.Sleep(200);
            }
        }
        private Hcsr04ReadEventArgs RetrieveSensorData()
        {
            try
            {
                _timer.Reset();

                while (Environment.TickCount - _lastMeasurment < 60)
                {
                    Thread.Sleep(TimeSpan.FromMilliseconds(Environment.TickCount - _lastMeasurment));
                }

                _controller.Write(_trigger, PinValue.High);     // trigger上高电平
                Thread.Sleep(TimeSpan.FromMilliseconds(0.01));  // 持续一段时间,发出足够的脉冲
                _controller.Write(_trigger, PinValue.Low);      // 设置低电平


                if (!GpioEX.WaitForValue(_controller, _echo, PinValue.Low)) // echo等待低电平结束,记录时间
                    throw new TimeoutException();

                _lastMeasurment = Environment.TickCount;

                _timer.Start();

                if (!GpioEX.WaitForValue(_controller, _echo, PinValue.High)) // echo等待高电平结束,记录时间
                    throw new TimeoutException();

                _timer.Stop();

                TimeSpan elapsed = _timer.Elapsed;


                var distance = elapsed.TotalMilliseconds / 2.0 * 34.3; 

                return new Hcsr04ReadEventArgs(distance);

            }
            catch
            {
                return Hcsr04ReadEventArgs.CreateInvalidReading();
            }

        }
    }

Hcsr04ReadEventArgs:

 public class Hcsr04ReadEventArgs : EventArgs
    {
        internal Hcsr04ReadEventArgs(double distance)
        {
            Distance = distance;
        }

        private Hcsr04ReadEventArgs(bool isValid) : this(Hcsr04Client.NoObstacleDistance)
        {
            IsValid = isValid;
        }

        /// 
        /// 读数是否有效.
        /// 
        public bool IsValid { get; } = true;

        /// 
        /// 是否检测到任何障碍物.
        /// 
        public bool HasObstacles => Distance != Hcsr04Client.NoObstacleDistance;

        /// 
        /// 获取到障碍物的实际距离,以厘米为单位.
        /// 
        public double Distance { get; }


        internal static Hcsr04ReadEventArgs CreateInvalidReading() => new Hcsr04ReadEventArgs(false);

最后在注入到服务:

 services.AddSingleton();

代码都非常简单,也有相关注释.大家看一眼就懂了.之后就是网网页上展示了.

 先弄一个Controller,在Controllers文件新建一个Hcsr04Controller和CarController的控制器:

 public class Hcsr04Controller : Controller
    {
        public static string iscsb = "stop";
        private readonly IHcsr04Client _hcsr04;
        private readonly IHubContext _chatHub;
        public Hcsr04Controller(IHcsr04Client hcsr04, IHubContext chatHub)
        {
            _hcsr04 = hcsr04;
            _chatHub = chatHub;
        }
        public async Task Hcsr04On()
        {
            _hcsr04.OnDataAvailable += async (s, e) =>
            {
                if (!e.IsValid)
                {
                    await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", "声波没有返回,被折射掉了.");
                }
                else if (e.HasObstacles)
                {
                    await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", $"距离:{e.Distance:N2}cm.");
                }
                else
                {
                    await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", "未检测到障碍物.");
                }
            };
            _hcsr04.Start();
            iscsb = "start";
            await _chatHub.Clients.All.SendAsync("ReceiveMessage", "50", "开启超声波通知.");
            return Content("超声波打开");
        }
        public async Task Hcsr04Off()
        {
            _hcsr04.Stop();
            iscsb = "stop";
            await _chatHub.Clients.All.SendAsync("ReceiveMessage", "51", "超声波关闭通知.");
            return Content("超声波关闭");
        }
    }

我这里面使用了signalR,方便数据到达时候在web上面展示,SignalR的内容这里就不说了,就是简单的使用.

CarController就什么都不用改了.之后新建一个视图:

使用asp.net core 3.0 搭建智能小车2_第6张图片

代码:

@{
    ViewData["Title"] = "智能小车控制面板";
}
@section Css{
    "~/css/car.css" rel="stylesheet" />
}
class="text-center">
class="display-4">控制面板

温度:"wd">35°C 湿度:"sd">50%

    class="Switch">
  • "checkbox" name="Storage" id="csb" onclick="KZCSB(this)" /> 超声波
  • "checkbox" name="Storage2" id="bz" onclick="KZBZ(this)" /> 红外避障
  • "checkbox" name="Storage2" id="wf" onclick="KZWIFIYK(this)" checked="checked" /> WiFi遥控
  • "checkbox" name="Storage2" id="hw" onclick="KZHWYK(this)" /> 红外遥控
class="control-wrapper">
class="control-btn control-top" id="up" onclick="carmove(this,'up')"> class="fa fa-chevron-up">∧
class="control-inner-btn control-inner">
class="control-btn control-left" id="left" onclick="carmove(this,'left')"> class="fa fa-chevron-left"><
class="control-inner-btn control-inner">
class="control-btn control-bottom" id="down" onclick="carmove(this,'down')"> class="fa fa-chevron-down">∨
class="control-inner-btn control-inner">
class="control-btn control-right" id="right" onclick="carmove(this,'right')"> class="fa fa-chevron-right">>
class="control-inner-btn control-inner">
class="control-round" id="pause" onclick="carmove(this,'pause')">
class="control-round-inner"> class="fa fa-pause-circle">P
class="c-box">
class="c-left">
"text-align:center;margin-top:10px;">超声波数据

"csbdata" style="color:Red;text-align:center">

class="c-right">
"text-align:center;margin-top:10px;">红外避障数据

"bzdata" style="color:Red;text-align:center">

@section Scripts{ }

编码阶段就完成了,没啥含量,简单粗暴,能用就行.现在我们把代码生成完成把生成的一堆文件都ftp到我们树莓派的目录上面.

在树莓派上我们要重启我们的站点才能生效:

sudo systemctl restart kestrel-carapp.service

之后在浏览器输入树莓派的IP来看效果吧:

使用asp.net core 3.0 搭建智能小车2_第7张图片

使用asp.net core 3.0 搭建智能小车2_第8张图片

  今天超声波模块就弄完了.下一章把红外避障和电机驱动上,让它跑起来

 

你可能感兴趣的:(使用asp.net core 3.0 搭建智能小车2)