Hallo,
ich möchte ein kleineres Projekt von mir vorstellen, was aus Interesse und zum vertiefen der Kenntnisse im Bereich Trigonometrie entstanden ist.
Es handelt sich um eine Lenkachsensteuerung bei Fahrzeugen. Sie berechnet die optimalen Winkel aller lenkbaren Räder aller Achsen eines Fahrzeugs um den Gummiabrieb verschwindent gering zu halten.
Um ein Fahrzeug zu definieren stehen dem Benutzer zwei Arten von Achsen zur Verfügung: Zum einen können starre Achsen und zum anderen lenkbare Achsen definiert werden. Die lenkbaren Achsen teilen sich auf in eine "Steuerachse" und beliebig viele "normale" lenkbare Achsen. Mit der Steuerachse kann der Winkel der Kurve eingestellt werden, die anderen Achsen passen sich an diesen Winkel an.
Wie funktioniert's?
- Voraussetzung ist, dass das Fahrzeug mit einer Achsschenkellenkung ausgestattet ist (Also bspw. keine Drehschemel-Lenkung besitzt, bei der die Achse im Gesamten gedreht wird)
- Nachdem der Benutzer ein beliebig vielachsiges Fahrzeug definiert hat, wird im ersten Schritt die "Kurvenlinie" berechnet (Diese muss aus Performancegründen nur einmal berechnet werden, vorausgesetzt, die Anzahl der Achsen ändert sich danach nicht mehr). Die Kurvenlinie ist die Drehachse des Fahrzeugs und muss in drei Fällen anders berechnet werden:
1.) Das Fahrzeug besitzt eine starre Achse und beliebig viele lenkbare Achsen: Hier liegt die Kurvenlinie auf Höhe der starren Achse und verläuft in Achsrichtung.
2.) Das Fahrzeug besitzt beliebig viele starre Achsen und beliebig viele lenkbare Achsen: Hier muss zunächst die vorderste starre Achse und die hinterste Starre Achse ermittelt werden. Die Kurvenlinie liegt nun genau in der Mitte dieser beiden Achsen.
3.) Das Fahrzeug besitzt keine starre Achse und beliebig viele lenkbare Achsen: Hier muss zunächst die vorderste lenkbare Achse und die hinterste lenkbare Achse ermittelt werden. Die Kurvenlinie liegt nun genau in der Mitte dieser beiden Achsen.
- Nachdem die Kurvenachse berechnet wurde, kann nun ein bestimmter Kurvenwinkel angegeben werden. Von der Steuerachse ausgehend wird dann der Schnittpunkt mit der Kurvenlinie ermittelt. Alle lenkbaren Räder des Fahrzeugs werden dann auf diesen Schnittpunkt ausgerichtet.
Screenshots?
Legende:
- horizontale weiße Linien: Achsen
- vertikale / gedrehte weiße Linien: Räder (und deren Stellung)
- grünes Quadrat auf Achse: lenkbare Achse
- blauer Kreis auf Achse: starre Achse
- roter Kreis auf grünem Quadrat auf Achse: Steuerachse (lenkbare Achse)
- violette horizontale Linie: Kurvenlinie
- gelber Punkt auf Kurvenlinie: Schnittpunkt mit dieser
- türkise Linie: Hypotenuse des zur Berechnung des Schnittpunkts mit der Kurvenlinie erstellten Dreiecks
- gelbe Linie: Hypotenuse des Dreiecks, das vom Radmittelpunkt mit dem ausgerechneten Winkel des betroffenen Rades aufgespannt werden kann.
- Fall 1:
- Fall 2:
- Fall 3:
- Nachwort?
Vielleicht kann es einer verwenden (ich kann mir nichts wichtigeres in einem Spiel vorstellen, als die korrekten Winkel der Räder zum Kurvenmittelpunkt zu berechnen ). Spaß beiseite: Falls es jemand benötigt, um physikalische Dinge zu simulieren, oder einfach mal zum rumprobieren, der kann es gerne verwenden.
- Download?
Da es zu wenig ist, um dafür extra ein RAR-Archiv zu erstellen, etc. hier einfach der Quellcode: BlitzMax: [AUSKLAPPEN] [EINKLAPPEN]
Const WHEEL_SIZE:Float = 50 Const DRAWING_BENCHMARK:Float = 50 Const DRAWING_COLORINTENSITY:Int = 255 Const DRAWING_DRAWCURVELINE:Byte = True Const DRAWING_DRAWANGLELINES:Byte = True
Const SYS_LEFTWHEEL:Int = 0 Const SYS_RIGHTWHEEL:Int = 1
Type TWheel Field PosX:Float Field PosY:Float Field Size:Float Field Angle:Float Function CreateWheel:TWheel(PosX:Float, PosY:Float, Size:Float, Angle:Float) Local ReturnMe:TWheel = New TWheel ReturnMe.PosX = PosX ReturnMe.PosY = PosY ReturnMe.Size = Size ReturnMe.Angle = Angle Return ReturnMe End Function Method SetRot(NewRot:Float) Self.Angle = NewRot End Method Method Draw() Local PrevRot:Float = GetRotation() SetRotation(Self.Angle) DrawLine(Self.PosX, Self.PosY, Self.PosX, (Self.PosY - (Self.Size / 2))) DrawLine(Self.PosX, Self.PosY, Self.PosX, (Self.PosY + (Self.Size / 2))) SetRotation(PrevRot) End Method End Type
Type TAxle Field PosX:Float Field PosY:Float Field TrackWidth:Float Field Wheels:TWheel[2] Field IsCtrl:Byte Method CreateAxle(PosX:Float, PosY:Float, TrackWidth:Float) Self.PosX = PosX Self.PosY = PosY Self.TrackWidth = TrackWidth End Method Method Draw() Abstract Method SetWheelRot(WheelIndex:Int, Rot:Float) Abstract Method IsSteeringAxle:Byte() Abstract Method GetMaxWheelRot:Int() Abstract Method GetMinWheelRot:Int() Abstract Method DebugOnScreen(PosX:Int, PosY:Int) Abstract End Type
Type TSteeringAxle Extends TAxle Field SteerMax:Float Function CreateSteeringAxle:TSteeringAxle(PosX:Float, PosY:Float, TrackWidth:Float, SteerMax:Float, IsCtrl:Byte = False) Local ReturnMe:TSteeringAxle = New TSteeringAxle ReturnMe.CreateAxle(PosX / (100 / DRAWING_BENCHMARK), PosY / (100 / DRAWING_BENCHMARK), TrackWidth / (100 / DRAWING_BENCHMARK)) ReturnMe.SteerMax = SteerMax ReturnMe.IsCtrl = IsCtrl ReturnMe.Wheels[SYS_LEFTWHEEL] = TWheel.CreateWheel((ReturnMe.PosX - (ReturnMe.TrackWidth / 2)), ReturnMe.PosY, WHEEL_SIZE / (100 / DRAWING_BENCHMARK), 0) ReturnMe.Wheels[SYS_RIGHTWHEEL] = TWheel.CreateWheel((ReturnMe.PosX + (ReturnMe.TrackWidth / 2)), ReturnMe.PosY, WHEEL_SIZE / (100 / DRAWING_BENCHMARK), 0) Return ReturnMe End Function Method Draw() DrawLine(Self.PosX, Self.PosY, (Self.PosX - (Self.TrackWidth / 2)), Self.PosY) DrawLine(Self.PosX, Self.PosY, (Self.PosX + (Self.TrackWidth / 2)), Self.PosY) SetColor(0, DRAWING_COLORINTENSITY, 0) DrawRect((Self.PosX - 5), (Self.PosY - 5), 10, 10) If(Self.IsCtrl = True) SetColor(DRAWING_COLORINTENSITY, 0, 0) DrawOval((Self.PosX - 5), (Self.PosY - 5), 10, 10) EndIf SetColor(255, 255, 255) If(Self.Wheels[SYS_LEFTWHEEL] <> Null) Then Self.Wheels[SYS_LEFTWHEEL].Draw() If(Self.Wheels[SYS_RIGHTWHEEL] <> Null) Then Self.Wheels[SYS_RIGHTWHEEL].Draw() End Method Method SetWheelRot(WheelIndex:Int, Rot:Float) If(WheelIndex <> 0 And WheelIndex <> 1) Then Return Self.Wheels[WheelIndex].SetRot(Rot) End Method Method DebugOnScreen(PosX:Int, PosY:Int) DrawText("Steering Axle: PosY: " + Self.PosY + "; LeftWheelRot: " + Self.Wheels[SYS_LEFTWHEEL].Angle + " Deg; RightWheelRot: " + Self.Wheels[SYS_RIGHTWHEEL].Angle + " Deg", PosX, PosY) End Method Method GetMaxWheelRot:Int() Return Self.SteerMax End Method Method GetMinWheelRot:Int() Return -Self.SteerMax End Method Method IsSteeringAxle:Byte() Return True End Method End Type
Type TBeamAxle Extends TAxle Function CreateBeamAxle:TBeamAxle(PosX:Float, PosY:Float, TrackWidth:Float) Local ReturnMe:TBeamAxle = New TBeamAxle ReturnMe.CreateAxle(PosX / (100 / DRAWING_BENCHMARK), PosY / (100 / DRAWING_BENCHMARK), TrackWidth / (100 / DRAWING_BENCHMARK)) ReturnMe.Wheels[SYS_LEFTWHEEL] = TWheel.CreateWheel((ReturnMe.PosX - (ReturnMe.TrackWidth / 2)), ReturnMe.PosY, WHEEL_SIZE / (100 / DRAWING_BENCHMARK), 0) ReturnMe.Wheels[SYS_RIGHTWHEEL] = TWheel.CreateWheel((ReturnMe.PosX + (ReturnMe.TrackWidth / 2)), ReturnMe.PosY, WHEEL_SIZE / (100 / DRAWING_BENCHMARK), 0) ReturnMe.IsCtrl = False Return ReturnMe End Function Method Draw() DrawLine(Self.PosX, Self.PosY, (Self.PosX - (Self.TrackWidth / 2)), Self.PosY) DrawLine(Self.PosX, Self.PosY, (Self.PosX + (Self.TrackWidth / 2)), Self.PosY) SetColor(0, 0, DRAWING_COLORINTENSITY) DrawOval((Self.PosX - 5), (Self.PosY - 5), 10, 10) SetColor(255, 255, 255) If(Self.Wheels[SYS_LEFTWHEEL] <> Null) Then Self.Wheels[SYS_LEFTWHEEL].Draw() If(Self.Wheels[SYS_RIGHTWHEEL] <> Null) Then Self.Wheels[SYS_RIGHTWHEEL].Draw() End Method Method SetWheelRot(WheelIndex:Int, Rot:Float) End Method Method GetMaxWheelRot:Int() Return 0 End Method Method GetMinWheelRot:Int() Return 0 End Method Method IsSteeringAxle:Byte() Return False End Method Method DebugOnScreen(PosX:Int, PosY:Int) DrawText("Beam Axle: PosY: " + Self.PosY, PosX, PosY) End Method End Type
Type TLASModel Field Axles:TList Field CurveLine:Float = 0 Function CreateTLASM:TLASModel() Local ReturnMe:TLASModel = New TLASModel ReturnMe.Axles = CreateList() Return ReturnMe End Function Method AddAxle(Axle:TAxle) If(Axle <> Null) ListAddLast(Self.Axles, Axle) EndIf End Method Method Draw() If(DRAWING_DRAWCURVELINE = True) SetColor(DRAWING_COLORINTENSITY, 0, DRAWING_COLORINTENSITY) DrawLine(-GraphicsWidth(), Self.CurveLine, GraphicsWidth(), Self.CurveLine) SetColor(255, 255, 255) EndIf For Local x:TAxle = EachIn Self.Axles x.Draw() Next End Method Method CalculateCurveLine() Local BeamAxleFound:Byte = False For Local BeamAxleSearch:TAxle = EachIn Self.Axles If(BeamAxleSearch.IsSteeringAxle() = False) BeamAxleFound = True EndIf Next Local MinAxle:TAxle = Null For Local TempAxle:TAxle = EachIn Self.Axles If(BeamAxleFound = True) If(TempAxle.IsSteeringAxle() = False) If(MinAxle = Null) MinAxle = TempAxle Continue EndIf If(TempAxle.PosY <= MinAxle.PosY) MinAxle = TempAxle EndIf EndIf Else If(MinAxle = Null) MinAxle = TempAxle Continue EndIf If(TempAxle.PosY <= MinAxle.PosY) MinAxle = TempAxle EndIf EndIf Next Local MaxAxle:TAxle = Null For TempAxle = EachIn Self.Axles If(BeamAxleFound = True) If(TempAxle.IsSteeringAxle() = False) If(MaxAxle = Null) MaxAxle = TempAxle Continue EndIf If(TempAxle.PosY >= MaxAxle.PosY) MaxAxle = TempAxle EndIf EndIf Else If(MaxAxle = Null) MaxAxle = TempAxle Continue EndIf If(TempAxle.PosY >= MaxAxle.PosY) MaxAxle = TempAxle EndIf EndIf Next If(MinAxle <> Null And MaxAxle <> Null) Self.CurveLine = (MaxAxle.PosY + MinAxle.PosY) / 2 EndIf End Method Method SetSteeringRot(Angle:Float) For Local CtrlAxle:TAxle = EachIn Self.Axles If(CtrlAxle.IsSteeringAxle() = True) If(CtrlAxle.IsCtrl = True) Exit EndIf Else CtrlAxle = Null EndIf Next If(CtrlAxle <> Null) Local Length:Float = Abs(Self.CurveLine) + Abs(CtrlAxle.PosY) Local intercept:Float = (Length / Tan(Angle)) - CtrlAxle.PosX If(DRAWING_DRAWANGLELINES = True) SetColor(0, DRAWING_COLORINTENSITY, DRAWING_COLORINTENSITY) DrawLine(CtrlAxle.PosX, CtrlAxle.PosY, intercept, Self.CurveLine) SetColor(255, 255, 255) SetColor(DRAWING_COLORINTENSITY, DRAWING_COLORINTENSITY, 0) DrawOval((intercept - 5), (Self.CurveLine - 5), 10, 10) SetColor(255, 255, 255) EndIf For Local TempAxle:TAxle = EachIn Self.Axles If(TempAxle.IsSteeringAxle() = True) Local WheelAngle:Float Local Distance:Float Local TempLength:Float If(TempAxle.PosY > Self.CurveLine) TempLength = TempAxle.PosY - Self.CurveLine Else TempLength = Self.CurveLine - TempAxle.PosY EndIf Distance = (intercept - (TempAxle.TrackWidth / 2)) - TempAxle.PosX WheelAngle = ATan(TempLength / Distance) If(TempAxle.PosY < Self.CurveLine) TempAxle.Wheels[SYS_RIGHTWHEEL].SetRot(WheelAngle) Else TempAxle.Wheels[SYS_RIGHTWHEEL].SetRot(-WheelAngle) EndIf If(DRAWING_DRAWANGLELINES = True) SetColor(DRAWING_COLORINTENSITY, DRAWING_COLORINTENSITY, 0) DrawLine(TempAxle.PosX + (TempAxle.TrackWidth / 2), TempAxle.PosY, intercept, Self.CurveLine) EndIf Distance = (intercept + (TempAxle.TrackWidth / 2)) - TempAxle.PosX WheelAngle = ATan(TempLength / Distance) If(TempAxle.PosY < Self.CurveLine) TempAxle.Wheels[SYS_LEFTWHEEL].SetRot(WheelAngle) Else TempAxle.Wheels[SYS_LEFTWHEEL].SetRot(-WheelAngle) EndIf If(DRAWING_DRAWANGLELINES = True) SetColor(DRAWING_COLORINTENSITY, DRAWING_COLORINTENSITY, 0) DrawLine((TempAxle.PosX - TempAxle.TrackWidth / 2), TempAxle.PosY, intercept, Self.CurveLine) EndIf EndIf Next EndIf End Method Method DebugOnScreen(PosX:Int, PosY:Int) Local MomPosY:Int = PosY For Local TempAxle:TAxle = EachIn Self.Axles TempAxle.DebugOnScreen(PosX, MomPosY) MomPosY = MomPosY + 20 Next End Method End Type
- Codebeispiele?
Hier mal der Code, mit dem die oben gezeigten Screenshots erstellt wurden:
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] Graphics(1600, 900, 32, 60)
Global FPS:TTimer = CreateTimer(60)
Global TestModel:TLASModel = TLASModel.CreateTLASM()
TestModel.CalculateCurveLine() SetOrigin((GraphicsWidth() / 2), (GraphicsHeight() / 2))
Global MomDeg:Float = -45 Global DirectionRight:Byte = False
Repeat WaitTimer(FPS) Cls TestModel.SetSteeringRot(MomDeg) If(DirectionRight = False) MomDeg = MomDeg + 0.25 Else MomDeg = MomDeg - 0.25 EndIf If(MomDeg > 45 Or MomDeg < -45) DirectionRight = Not DirectionRight EndIf TestModel.Draw() TestModel.DebugOnScreen(-800, -430) DrawText("Deg: " + MomDeg, -800, -450) Flip 0 Until KeyHit(KEY_ESCAPE) End
Joa, ich glaube das wär's...
EDIT:
-> Kleinen Bug im Code behoben, der u.U. den Winkel einiger Räder falsch berechnete
-> Möglichkeit geschaffen, die Achsen auch auf horizontaler Ebene zu verschieben.
|