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

相关推荐
E***U9452 分钟前
从新手到入门:如何判断自己是否真的学会了 Spring Boot
数据库·spring boot·后端
古城小栈3 分钟前
Golang 中 return 与 defer 的 长幼尊卑
golang
招风的黑耳17 分钟前
智慧养老项目:当SpringBoot遇到硬件,如何优雅地处理异常与状态管理?
java·spring boot·后端
回家路上绕了弯23 分钟前
分布式锁原理深度解析:从理论到实践
分布式·后端
磊磊磊磊磊39 分钟前
用AI做了个排版工具,分享一下如何高效省钱地用AI!
前端·后端·react.js
hgz07101 小时前
Spring Boot Starter机制
java·spring boot·后端
daxiang120922051 小时前
Spring boot服务启动报错 java.lang.StackOverflowError 原因分析
java·spring boot·后端
我家领养了个白胖胖1 小时前
极简集成大模型!Spring AI Alibaba ChatClient 快速上手指南
java·后端·ai编程
一代明君Kevin学长1 小时前
快速自定义一个带进度监控的文件资源类
java·前端·后端·python·文件上传·文件服务·文件流
aiopencode1 小时前
上架 iOS 应用到底在做什么?从准备工作到上架的流程
后端