Go实现树莓派超声波测距

后面发现调用的两个Go的库进行测算还是没办法读到好的超声波值, 所以放弃

公式

距离(cm)=((声速(m/s)×时间(ms))/ 2) *10

代码

ultrasonicSensor.go

go 复制代码
package main

import (
	"context"
	"errors"
	"fmt"
	"periph.io/x/conn/v3/gpio"
	"periph.io/x/conn/v3/gpio/gpioreg"
	"time"
)

var (
	TimeoutError = errors.New("timeout error")
)

const (
	SpeedOfSound = 34300.0 // cm/s
)

type UltrasonicSensor struct {
	triggerIO gpio.PinIO
	echoIO    gpio.PinIO

	started  bool
	canceled chan struct{}
}

func NewUltrasonicSensor(triggerPinName, echoPinName string) (*UltrasonicSensor, error) {
	var (
		us = &UltrasonicSensor{
			canceled: make(chan struct{}),
		}
		err error
	)
	us.triggerIO = gpioreg.ByName(triggerPinName)
	if nil == us.triggerIO {
		return nil, errors.New("can't find trigger pin")
	}
	us.echoIO = gpioreg.ByName(echoPinName)
	if nil == us.echoIO {
		return nil, errors.New("can't find echo pin")
	}
	return us, err
}

func (u *UltrasonicSensor) Init() error {
	err := u.triggerIO.Out(gpio.Low)
	if nil != err {
		return err
	}
	err = u.echoIO.In(gpio.PullDown, gpio.NoEdge)
	return nil
}

func (u *UltrasonicSensor) Destroy() error {
	return nil
}

func (u *UltrasonicSensor) Start(detectInterval time.Duration, onDistanceChanged func(cm float64, err error) error) error {
	go u.run(detectInterval, onDistanceChanged)
	return nil
}

func (u *UltrasonicSensor) run(detectInterval time.Duration, onDistanceChanged func(cm float64, err error) error) {
	if u.started {
		return
	}
	u.started = true
	defer func() {
		select {
		case <-u.canceled:
		default:
		}
		u.started = false
	}()

	var (
		err          error
		distance     float64
		distanceTemp float64
		/*
			声速大约是343米/秒, 超声波模块最大测量是2m ~ 6m
			时间(s) = 2*距离(m)/声速(m/s) = 2 * 6 / 343 ≈ 0.035
		*/
		maxWaitTime = time.Millisecond * 35 //35
		parentCtx   = context.Background()
		ctx         context.Context
	)
	for {
		err = u.ultrasonicPulseOut() // 发送超声波
		if nil != err {
			onDistanceChanged(0, err)
			return
		}
		ctx, _ = context.WithTimeout(parentCtx, maxWaitTime)
		distanceTemp, err = u.getDist(ctx)
		if nil != err {
			if TimeoutError != err {
				onDistanceChanged(0, err)
				return
			}
			fmt.Println("read timeout")
			time.Sleep(detectInterval)
			continue
		}

		if distance != distanceTemp {
			distance = distanceTemp
			if err = onDistanceChanged(distance, nil); nil != err {
				return
			}
		}

		time.Sleep(detectInterval)
	}
}

func (u *UltrasonicSensor) ultrasonicPulseOut() (err error) {
	if err = u.triggerIO.Out(gpio.High); nil != err {
		return err
	}
	time.Sleep(time.Microsecond * 10) // 10us
	if err = u.triggerIO.Out(gpio.Low); nil != err {
		return err
	}
	time.Sleep(time.Microsecond * 1)
	return
}

func (u *UltrasonicSensor) getDist(ctx context.Context) (float64, error) {
	var (
		startDateTime time.Time // 开启超声波计时
	)

	waitForNext := func() error {
		select {
		case <-ctx.Done():
			return TimeoutError
		case <-u.canceled:
			return errors.New("cancel error")
		case <-time.After(time.Nanosecond * 1): // 读取引脚延时,也是软件最大误差
		}
		return nil
	}

	/* wait for start */
	for gpio.Low == u.echoIO.Read() {
		if err := waitForNext(); nil != err {
			return 0, err
		}
	}
	startDateTime = time.Now()

	/* wait for end */
	for gpio.High == u.echoIO.Read() {
		if err := waitForNext(); nil != err {
			return 0, err
		}
	}
	return time.Since(startDateTime).Seconds() * SpeedOfSound / 2.0, nil
}

func (u *UltrasonicSensor) Stop() error {
	if !u.started {
		return nil
	}
	select {
	case u.canceled <- struct{}{}:
	case <-time.After(time.Second * 2):
		return errors.New("cancel timeout")
	}
	return nil
}

main.go

go 复制代码
package main

import (
	"fmt"
	"log"
	"periph.io/x/host/v3"
	"sync"
	"time"
)

func main() {
	var (
		waiter sync.WaitGroup
	)

	// 初始化硬件
	if _, err := host.Init(); err != nil {
		log.Fatal(err)
	}
	sensor, err := NewUltrasonicSensor("GPIO17", "GPIO27")
	if nil != err {
		fmt.Println(err)
		return
	}
	if err = sensor.Init(); nil != err {
		fmt.Println("sensor init fail, ", err.Error())
		return
	}
	defer sensor.Destroy()
	waiter.Add(1)
	sensor.Start(time.Second, func(cm float64, err error) error {
		if nil != err {
			waiter.Done()
			fmt.Println(err)
			return err
		}

		fmt.Println("Distance ", cm, "(cm)")

		return nil
	})
	waiter.Wait()
}

Note

注意电源一定要选对, 否则输出功率不够无法准确测量距离...

参考

树莓派|超声波传感器

GPIO-ZERO DistanceSensor

相关推荐
hummhumm1 小时前
第 36 章 - Go语言 服务网格
java·运维·前端·后端·python·golang·java-ee
凡人的AI工具箱1 小时前
40分钟学 Go 语言高并发:Pipeline模式(一)
开发语言·后端·缓存·架构·golang
南鸳6101 小时前
Scala:根据身份证号码,输出这个人的籍贯
开发语言·后端·scala
小扳2 小时前
微服务篇-深入了解使用 RestTemplate 远程调用、Nacos 注册中心基本原理与使用、OpenFeign 的基本使用
java·运维·分布式·后端·spring·微服务·架构
ᝰꫝꪉꪯꫀ3612 小时前
JavaWeb——SpringBoot原理
java·开发语言·后端·springboot
LLLibra1462 小时前
如何使用Postman优雅地进行接口自动加密与解密
后端
LightOfNight2 小时前
Redis设计与实现第14章 -- 服务器 总结(命令执行器 serverCron函数 初始化)
服务器·数据库·redis·分布式·后端·缓存·中间件
刽子手发艺2 小时前
云服务器部署springboot项目、云服务器配置JDK、Tomcat
java·后端·部署
White graces3 小时前
Spring MVC练习(前后端分离开发实例)
java·开发语言·前端·后端·spring·java-ee·mvc
LightOfNight4 小时前
【设计模式】创建型模式之单例模式(饿汉式 懒汉式 Golang实现)
单例模式·设计模式·golang