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

相关推荐
互联网架构小马2 分钟前
Flask使用SQLAlchemy添加悲观锁和乐观锁
后端·python·flask
shencz200010 分钟前
树莓派0 2W重启后突然没有声音
linux·树莓派
默默且听风13 分钟前
Express.js中的中间件详解
后端·node.js·express
AskHarries39 分钟前
Spring Boot集成geode快速入门Demo
java·spring boot·后端·geode
知其然亦知其所以然1 小时前
深入Kafka:如何保证数据一致性与可靠性?
后端·面试·kafka
WHYBIGDATA1 小时前
Scala中高级的函数编程
开发语言·后端·scala
吃青椒的小新1 小时前
独一无二的设计模式——单例模式(Java实现)
java·后端·单例模式·设计模式
知识分享小能手1 小时前
从新手到高手:Scala函数式编程完全指南,Scala 访问修饰符(6)
大数据·开发语言·后端·python·数据分析·scala·函数式编程
蝎子莱莱爱打怪2 小时前
maven卸载旧版本3.1.0 并安装新版本3.8.8
后端
肖哥弹架构2 小时前
使用MarshallingView实现自动化的XML响应生成
后端