180 lines
4.7 KiB
Go
180 lines
4.7 KiB
Go
package throttle
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/cyrilix/robocar-protobuf/go/events"
|
|
"github.com/cyrilix/robocar-throttle/pkg/types"
|
|
"math"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
type Processor interface {
|
|
// Process compute throttle from steering value
|
|
Process(steering types.Steering) types.Throttle
|
|
|
|
SetSpeedZone(sz events.SpeedZone)
|
|
}
|
|
|
|
func NewSteeringProcessor(minThrottle, maxThrottle types.Throttle) *SteeringProcessor {
|
|
return &SteeringProcessor{
|
|
minThrottle: minThrottle,
|
|
maxThrottle: maxThrottle,
|
|
}
|
|
}
|
|
|
|
type SteeringProcessor struct {
|
|
minThrottle, maxThrottle types.Throttle
|
|
}
|
|
|
|
func (sp *SteeringProcessor) SetSpeedZone(_ events.SpeedZone) {
|
|
return
|
|
}
|
|
|
|
// Process compute throttle from steering value
|
|
func (sp *SteeringProcessor) Process(steering types.Steering) types.Throttle {
|
|
absSteering := math.Abs(float64(steering))
|
|
return sp.minThrottle + types.Throttle(float64(sp.maxThrottle-sp.minThrottle)*(1-absSteering))
|
|
}
|
|
|
|
func NewSpeedZoneProcessor(slowThrottle, normalThrottle, fastThrottle types.Throttle,
|
|
moderateSteering, fullSteering float64) *SpeedZoneProcessor {
|
|
return &SpeedZoneProcessor{
|
|
muSz: sync.Mutex{},
|
|
speedZone: events.SpeedZone_UNKNOWN,
|
|
slowThrottle: slowThrottle,
|
|
normalThrottle: normalThrottle,
|
|
fastThrottle: fastThrottle,
|
|
moderateSteering: moderateSteering,
|
|
fullSteering: fullSteering,
|
|
}
|
|
}
|
|
|
|
type SpeedZoneProcessor struct {
|
|
muSz sync.Mutex
|
|
speedZone events.SpeedZone
|
|
slowThrottle, normalThrottle, fastThrottle types.Throttle
|
|
moderateSteering, fullSteering float64
|
|
}
|
|
|
|
func (sp *SpeedZoneProcessor) SpeedZone() events.SpeedZone {
|
|
sp.muSz.Lock()
|
|
defer sp.muSz.Unlock()
|
|
return sp.speedZone
|
|
}
|
|
|
|
func (sp *SpeedZoneProcessor) SetSpeedZone(sz events.SpeedZone) {
|
|
sp.muSz.Lock()
|
|
defer sp.muSz.Unlock()
|
|
sp.speedZone = sz
|
|
}
|
|
|
|
// Process compute throttle from steering value
|
|
func (sp *SpeedZoneProcessor) Process(steering types.Steering) types.Throttle {
|
|
st := math.Abs(float64(steering))
|
|
|
|
switch sp.SpeedZone() {
|
|
case events.SpeedZone_FAST:
|
|
if st >= sp.fullSteering {
|
|
return sp.slowThrottle
|
|
} else if st >= sp.moderateSteering {
|
|
return sp.normalThrottle
|
|
}
|
|
return sp.fastThrottle
|
|
case events.SpeedZone_NORMAL:
|
|
if st > sp.fullSteering {
|
|
return sp.slowThrottle
|
|
}
|
|
return sp.normalThrottle
|
|
case events.SpeedZone_SLOW:
|
|
return sp.slowThrottle
|
|
}
|
|
return sp.slowThrottle
|
|
}
|
|
|
|
func NewCustomSteeringProcessor(cfg *Config) *CustomSteeringProcessor {
|
|
return &CustomSteeringProcessor{
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
type CustomSteeringProcessor struct {
|
|
cfg *Config
|
|
}
|
|
|
|
func (cp *CustomSteeringProcessor) Process(steering types.Steering) types.Throttle {
|
|
return cp.cfg.ValueOf(steering)
|
|
}
|
|
|
|
func (cp *CustomSteeringProcessor) SetSpeedZone(_ events.SpeedZone) {
|
|
return
|
|
}
|
|
|
|
var emptyConfig = Config{
|
|
SteeringValues: []types.Steering{},
|
|
ThrottleSteps: []types.Throttle{},
|
|
}
|
|
|
|
func NewConfigFromJson(fileName string) (*Config, error) {
|
|
content, err := os.ReadFile(fileName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read content from %s file: %w", fileName, err)
|
|
}
|
|
var ft Config
|
|
err = json.Unmarshal(content, &ft)
|
|
if err != nil {
|
|
return &emptyConfig, fmt.Errorf("unable to unmarshal json content from %s file: %w", fileName, err)
|
|
}
|
|
if len(ft.SteeringValues) == 0 {
|
|
return &emptyConfig, fmt.Errorf("invalid configuration, none steering value'")
|
|
}
|
|
if len(ft.SteeringValues) != len(ft.ThrottleSteps) {
|
|
return &emptyConfig, fmt.Errorf("invalid config, steering value number must be equals "+
|
|
"to throttle value number: %v/%v", len(ft.SteeringValues), len(ft.ThrottleSteps))
|
|
}
|
|
lastT := types.Throttle(1.)
|
|
for _, t := range ft.ThrottleSteps {
|
|
if t < 0. || t > 1. {
|
|
return &emptyConfig, fmt.Errorf("invalid throttle value: 0.0 < %v <= 1.0", t)
|
|
}
|
|
if t >= lastT {
|
|
return &emptyConfig, fmt.Errorf("invalid throttle value, all values must be decreasing: %v <= %v", lastT, t)
|
|
}
|
|
lastT = t
|
|
}
|
|
lastS := types.Steering(-0.001)
|
|
for _, s := range ft.SteeringValues {
|
|
if s < 0. || s > 1. {
|
|
return &emptyConfig, fmt.Errorf("invalid steering value: 0.0 < %v <= 1.0", s)
|
|
}
|
|
if s <= lastS {
|
|
return &emptyConfig, fmt.Errorf("invalid steering value, all values must be increasing: %v <= %v", lastS, s)
|
|
}
|
|
lastS = s
|
|
}
|
|
return &ft, nil
|
|
}
|
|
|
|
type Config struct {
|
|
SteeringValues []types.Steering `json:"steering_values"`
|
|
ThrottleSteps []types.Throttle `json:"throttle_steps"`
|
|
}
|
|
|
|
func (tc *Config) ValueOf(s types.Steering) types.Throttle {
|
|
st := s
|
|
if s < 0. {
|
|
st = s * -1
|
|
}
|
|
if st < tc.SteeringValues[0] {
|
|
return tc.ThrottleSteps[0]
|
|
}
|
|
|
|
for i, steeringStep := range tc.SteeringValues {
|
|
if st < steeringStep {
|
|
return tc.ThrottleSteps[i-1]
|
|
}
|
|
}
|
|
return tc.ThrottleSteps[len(tc.ThrottleSteps)-1]
|
|
}
|