Go实现树莓派超声波测距

发布于:2024-05-09 ⋅ 阅读:(30) ⋅ 点赞:(0)

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

公式

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

代码

ultrasonicSensor.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

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