Initial commit from MyParking project

This commit is contained in:
2026-08-04 10:29:21 +08:00
commit d9432bd529
138 changed files with 16957 additions and 0 deletions
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using FundamentalLib;
using Newtonsoft.Json;
namespace CommonUsage.Chassis
{
public abstract class AbstractChassis
{
protected AbstractChassis()
{
Valid = false;
}
public abstract void Initialize();
public abstract void Visualize();
public abstract void AfterDirectionChanged();
/// <summary>
/// 当前行进方向。
/// </summary>
[Obsolete]
public float DirectionAngle
{
get => _originBiasTh;
set
{
_originBiasTh = value;
if (_originBiasTh != _lastDirectionAngle) AfterDirectionChanged();
_lastDirectionAngle = _originBiasTh;
}
}
protected float _originBiasX = 0f, _originBiasY = 0f, _originBiasTh;
public Vector3 GetOriginBias()
{
return new Vector3(_originBiasX, _originBiasY, _originBiasTh);
}
public class CarSpeed
{
public float Vx,Vy,Vw;
}
public abstract CarSpeed GetCarSpeed(bool isActual = false);
public List<GeometricControlPoint> GetGeometricControlPoints()
{
return GeometricControlPoints;
}
public void ComputeWheelsGeometrically(float speed)
{
// 打印调用位置信息
var stackTrace = new StackTrace(true);
var callerFrame = stackTrace.GetFrame(1); // 获取调用者的帧
if (callerFrame != null)
{
var fileName = callerFrame.GetFileName();
var lineNumber = callerFrame.GetFileLineNumber();
DLog.Log($"s:{speed:0.000} from {fileName} ln.{lineNumber}", $"WheelComputeCaller");
}
DefineGeometricWheelComputation(speed);
}
protected abstract void DefineGeometricWheelComputation(float speed);
public void DriveStop()
{
PredefinedDriveStop();
CustomDriveStop?.Invoke();
}
public abstract void PredefinedDriveStop();
public Action CustomDriveStop;
public abstract bool ComputeRotateWheels(float rotSpeed);
public abstract float CalculateTurningSpeedDecayFac(float turn);
public enum ChassisState
{
Standby,
Running,
AbnormalFeedback,
ExceedMotionAbility,
}
protected ChassisState State;
protected string StateDescription;
public (ChassisState State, string Description) GetChassisState()
{
return (State, StateDescription);
}
public bool Debug = false;
public float AccPerSecond = 0.2f;
public float DeAccPerSecond = 0.2f;
public float MaxSpeed = 1; // m/s
public float MinTurnSpeedFac = 0.5f;
public float MaxTurnThreshold = 90f;
public float GcpThetaPerSecond = 10f;
public DateTime LastMoveTime = DateTime.MinValue;
protected bool Valid = false;
protected List<GeometricControlPoint> GeometricControlPoints = new();
protected bool RotatingActive = false;
protected bool GoingActive = false;
protected bool GoingWheelAligned = false;
private float _lastDirectionAngle = 0;
}
}
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
namespace CommonUsage.Chassis
{
public class DiffSteerWheel:SteerWheel
{
public DiffSteerWheel(float wheelDistance,Vector2 position, float angleLowerLimit, float angleUpperLimit, Action<float> speedWriter,
Func<float> speedReader, Action<float> angleWriter, Func<float> angleReader, Action<float> leftSpeedWriter, Action<float> rightSpeedWriter,
float angleLimitMarginDeg = 15f) : base(position,
angleLowerLimit, angleUpperLimit, speedWriter, speedReader, angleWriter, angleReader, angleLimitMarginDeg)
{
_leftSpeedWriter = leftSpeedWriter;
_rightSpeedWriter = rightSpeedWriter;
WheelDistance = wheelDistance;
}
public float GetLeftSendSpeed()
{
return _leftSendSpeed;
}
public float GetRightSendSpeed()
{
return _rightSendSpeed;
}
public void WriteLeftSpeed(float speed)
{
_leftSpeedWriter(_leftSendSpeed = speed);
}
public void WriteRightSpeed(float speed)
{
_rightSpeedWriter(_rightSendSpeed = speed);
}
public float WheelDistance;
private readonly Action<float> _leftSpeedWriter;
private readonly Action<float> _rightSpeedWriter;
private float _leftSendSpeed;
private float _rightSendSpeed;
}
}
@@ -0,0 +1,170 @@
using FundamentalLib;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using System.Diagnostics;
namespace CommonUsage.Chassis
{
public class DifferentialChassis : AbstractChassis
{
public void SetLeftRightWheels(Wheel wheelL, Wheel wheelR)
{
_leftWheel = wheelL;
_rightWheel = wheelR;
_halfWheelTrack = Math.Abs(_leftWheel.Position.Y);
}
public override void Visualize()
{
}
public override CarSpeed GetCarSpeed(bool isActual = false)
{
if (!isActual)
{
return new CarSpeed()
{
Vx = (_speedL + _speedR) / 2f,
Vw = (_speedR - _speedL) / Math.Abs(_leftWheel.Position.Y - _rightWheel.Position.Y) /
(float)Math.PI * 180f * 1000f,
Vy = 0
};
}
else
{
return new CarSpeed()
{
Vx = GetLinearSpeed(),
Vw = (_rightWheel.ReadSpeed() - _leftWheel.ReadSpeed()) /
Math.Abs(_leftWheel.Position.Y - _rightWheel.Position.Y) /
(float)Math.PI * 180f * 1000f,
Vy = 0
};
}
}
public (Wheel,Wheel) GetWheels()
{
return (_leftWheel, _rightWheel);
}
public float GetLinearSpeed()
{
return (_leftWheel.ReadSpeed() + _rightWheel.ReadSpeed()) / 2f;
}
public override void Initialize()
{
GeometricControlPoints.Add(new GeometricControlPoint(new Vector2(0, 0)));
Valid = true;
}
public override void AfterDirectionChanged()
{
}
public override void PredefinedDriveStop()
{
if (!Valid) return;
_sendSpeedL = _sendSpeedR = 0;
_speedL = _speedR = 0;
_leftWheel.WriteSpeed(_sendSpeedL);
_rightWheel.WriteSpeed(_sendSpeedR);
GoingActive = false;
RotatingActive = false;
}
protected override void DefineGeometricWheelComputation(float speed)
{
var now = DateTime.Now;
if (!GoingActive) LastMoveTime = now;
SendSpeed(speed, GeometricControlPoints[0].Theta, now - LastMoveTime);
GoingActive = true;
RotatingActive = false;
}
public override bool ComputeRotateWheels(float rotSpeed)
{
if (!RotatingActive) LastMoveTime = DateTime.Now;
SendSpeed(0, rotSpeed);
GoingActive = false;
RotatingActive = true;
return true;
}
public override float CalculateTurningSpeedDecayFac(float turn)
{
return 1 - Math.Min(turn, MaxTurnThreshold) / MaxTurnThreshold * MinTurnSpeedFac;
}
public void SendSpeed(float linearSpeed, float angularSpeed, TimeSpan? deltaTime = null)
{
var edgeLinearSpeed = (float)(angularSpeed / 180f * Math.PI * _halfWheelTrack / 1000);
var vl = linearSpeed - edgeLinearSpeed;
var vr = linearSpeed + edgeLinearSpeed;
_speedL = vl;
_speedR = vr;
AccumulateSpeed(vl, vr, deltaTime);
LastMoveTime = DateTime.Now;
}
private void AccumulateSpeed(float vl, float vr, TimeSpan? deltaTime = null)
{
// var dTime = (float)(deltaTime ?? DateTime.Now - LastMoveTime).TotalSeconds;
//
// var speedSignL = Math.Sign(vl - _sendSpeedL);
// var accL = Math.Abs(vl) > Math.Abs(_sendSpeedL) ? AccPerSecond : DeAccPerSecond;
// _sendSpeedL += speedSignL * Math.Min(Math.Abs(vl - _sendSpeedL), accL * dTime);
// _leftWheel.WriteSpeed(_sendSpeedL);
//
// var speedSignR = Math.Sign(vr - _sendSpeedR);
// var accR = Math.Abs(vr) > Math.Abs(_sendSpeedR) ? AccPerSecond : DeAccPerSecond;
// _sendSpeedR += speedSignR * Math.Min(Math.Abs(vr - _sendSpeedR), accR * dTime);
// _rightWheel.WriteSpeed(_sendSpeedR);
// if (Debug)
// Console.WriteLine($"DiffChassis, target:{v:0.00},send:{_sendSpeed:0.0}");
// var dTime = (float)(deltaTime ?? DateTime.Now - LastMoveTime).TotalSeconds;
var dTime = (float)(deltaTime ?? DateTime.Now - LastMoveTime).TotalSeconds;
float diffL = vl - _sendSpeedL;
float diffR = vr - _sendSpeedR;
float accL = Math.Abs(vl) > Math.Abs(_sendSpeedL) ? AccPerSecond : DeAccPerSecond;
float accR = Math.Abs(vr) > Math.Abs(_sendSpeedR) ? AccPerSecond : DeAccPerSecond;
float maxDeltaL = accL * dTime;
float maxDeltaR = accR * dTime;
float factorL = Math.Abs(diffL) > maxDeltaL ? maxDeltaL / Math.Abs(diffL) : 1.0f;
float factorR = Math.Abs(diffR) > maxDeltaR ? maxDeltaR / Math.Abs(diffR) : 1.0f;
float factor = Math.Min(factorL, factorR);
_sendSpeedL += diffL * factor;
_sendSpeedR += diffR * factor;
_leftWheel.WriteSpeed(_sendSpeedL);
_rightWheel.WriteSpeed(_sendSpeedR);
// Console.WriteLine($"DiffChassis, target:{vl:0.00},send:{_sendSpeedL:0.00} dTime{dTime} diffL:{diffL} factor:{factor}" );
}
private Wheel _leftWheel;
private Wheel _rightWheel;
private float _halfWheelTrack; // millimeter
private float _sendSpeedL;
private float _sendSpeedR;
private int _direction = 1; // 1 forward, -1 backward
private float _speedL;
private float _speedR;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Diagnostics;
using CommonUsage.Mathematics;
using FundamentalLib;
namespace CommonUsage.Chassis
{
public class SingleSteerChassis : AbstractChassis
{
public void SetSteerWheel(SteerWheel wheel)
{
_steerWheel = wheel;
}
public SteerWheel GetSteerWheel()
{
return _steerWheel;
}
public override void Visualize()
{
}
public override CarSpeed GetCarSpeed(bool isActual = false)
{
if (!isActual)
{
var sendAngle = _steerWheel.GetSendAngle();
var sendAngleRad = _steerWheel.GetSendAngle() / 180f * Math.PI;
// VSteer* Cos = v;
var vsteer = _sendSpeed / ((Math.Cos(Math.Abs(sendAngleRad)) + 0.000001));
var vsteerY = vsteer * Math.Sin(sendAngleRad);
// Console.WriteLine($"{vsteer} {vsteerY} {sendAngleRad} {_sendSpeed}");
return new CarSpeed()
{
Vx = (float)(_sendSpeed * Math.Cos(Math.Abs(sendAngle) / 180f * Math.PI)),
Vy = 0,
Vw = (float)(_sendSpeed * Math.Sin(Math.Abs(sendAngle) / 180f * Math.PI) /
Math.Abs(_steerWheel.Position.X / 1000f) / Math.PI * 180f)
//阿克曼
// Vx = (float)(_sendSpeed),
// Vy = 0,
// Vw = (float)(vsteerY / Math.Abs(_steerWheel.Position.X / 1000f) / Math.PI * 180f)
};
}
else
{
return new CarSpeed()
{
Vx = (float)(_steerWheel.ReadSpeed() *
Math.Cos(Math.Abs(_steerWheel.ReadAngle()) / 180f * Math.PI)),
Vy = 0,
Vw = (float)(_steerWheel.ReadSpeed() *
Math.Sin(Math.Abs(_steerWheel.ReadAngle()) / 180f * Math.PI) /
Math.Abs(_steerWheel.Position.X / 1000f) / Math.PI * 180f)
};
}
}
public override void Initialize()
{
GeometricControlPoints = new List<GeometricControlPoint>()
{
new (_steerWheel.Position),
new (Vector2.Zero)
};
Valid = true;
}
public override void AfterDirectionChanged()
{
if (Math.Abs(CommonMath.ThDiff(0, _originBiasTh)) > 90)
{
GeometricControlPoints = new List<GeometricControlPoint>()
{
new (-_steerWheel.Position),
};
_direction = -1;
}
else
{
GeometricControlPoints = new List<GeometricControlPoint>()
{
new (_steerWheel.Position),
};
_direction = 1;
}
}
public override void PredefinedDriveStop()
{
if (!Valid) return;
_sendSpeed = 0;
_steerWheel.WriteSpeed(_sendSpeed);
GoingActive = false;
RotatingActive = false;
}
protected override void DefineGeometricWheelComputation(float speed)
{
var now = DateTime.Now;
if (!GoingActive)
{
LastMoveTime = now;
GoingWheelAligned = false;
}
SendSteerMotion(speed * _direction, GeometricControlPoints[0].Theta, now - LastMoveTime);
GoingActive = true;
RotatingActive = false;
}
public override bool ComputeRotateWheels(float rotSpeed)
{
if (!RotatingActive)
{
LastMoveTime = DateTime.Now;
GoingWheelAligned = false;
}
SendSteerMotion(rotSpeed, 90);
GoingActive = false;
RotatingActive = true;
return true;
}
public void SendSteerMotion(float speed, float theta, TimeSpan? deltaTime = null)
{
_steerWheel.WriteAngle(theta);
if (!GoingWheelAligned && Math.Abs(CommonMath.ThDiff(_steerWheel.ReadAngle(), theta)) < 1)
GoingWheelAligned = true;
if (!GoingWheelAligned) speed = 0;
var turnThresholdSpeed = CalculateTurningSpeedDecayFac(Math.Abs(theta)) * MaxSpeed;
AccumulateSpeed(Math.Min(turnThresholdSpeed, Math.Abs(speed)) * Math.Sign(speed), deltaTime);
LastMoveTime = DateTime.Now;
}
public override float CalculateTurningSpeedDecayFac(float turn)
{
return 1 - Math.Min(turn, MaxTurnThreshold) / MaxTurnThreshold * MinTurnSpeedFac;
}
private void AccumulateSpeed(float v, TimeSpan? deltaTime = null)
{
// _targetSpeed = v;
var speedSign = Math.Sign(v - _sendSpeed);
var acc = Math.Abs(v) > Math.Abs(_sendSpeed) ? AccPerSecond : DeAccPerSecond;
_sendSpeed += speedSign * Math.Min(Math.Abs(v - _sendSpeed),
acc * (float)(deltaTime ?? DateTime.Now - LastMoveTime).TotalSeconds);
_steerWheel.WriteSpeed(_sendSpeed);
if (Debug)
Console.WriteLine($"SingleSteer, target:{v:0.00},send:{_sendSpeed:0.0}");
}
private SteerWheel _steerWheel;
private float _sendSpeed;
private int _direction = 1; // 1 forward, -1 backward
}
}
@@ -0,0 +1,100 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using CommonUsage.Mathematics;
namespace CommonUsage.Chassis
{
public class SteerWheel : Wheel
{
public SteerWheel(Vector2 position, float angleLowerLimit, float angleUpperLimit, Action<float> speedWriter,
Func<float> speedReader, Action<float> angleWriter, Func<float> angleReader, float angleLimitMarginDeg = 15f) : base(position, speedWriter,
speedReader)
{
_angleLowerLimit = angleLowerLimit;
_angleUpperLimit = angleUpperLimit;
_angleWriter = angleWriter;
_angleReader = angleReader;
_centerDistance = position.Length();
AngleLimitMarginDeg = angleLimitMarginDeg;
}
public float AngleLimitMarginDeg = 15f;
public bool TrySetDirection(bool allowReverse, ref float desireDirection, ref int dir)
{
if (TryNormalizeAngleInLimit(desireDirection, out var normalized))
{
dir = 1;
desireDirection = normalized;
return true;
}
if (!allowReverse) return false;
var oppositeTh = (float)CommonMath.RoundTh(desireDirection + 180);
if (TryNormalizeAngleInLimit(oppositeTh, out normalized))
{
dir = -1;
desireDirection = normalized;
return true;
}
dir = 0;
return false;
}
private bool TryNormalizeAngleInLimit(float angle, out float normalized)
{
var lower = CommonMath.RoundTh(_angleLowerLimit);
var upper = CommonMath.RoundTh(_angleUpperLimit);
while (upper < lower) upper += 360;
normalized = (float)CommonMath.RoundTh(angle);
while (normalized < lower) normalized += 360;
while (normalized > upper && normalized - 360 >= lower) normalized -= 360;
var margin = Math.Min(normalized - lower, upper - normalized);
return normalized >= lower && normalized <= upper && margin >= Math.Max(0, AngleLimitMarginDeg);
}
public float ReadAngle()
{
return _angleReader();
}
public void WriteAngle(float angle)
{
_angleWriter.Invoke(_sendAngle = Math.Max(_angleLowerLimit, Math.Min(angle, _angleUpperLimit)));
}
public float GetSendAngle()
{
return _sendAngle;
}
public float GetAngleRelativeToChassis()
{
return ZeroDirection + _sendAngle;
}
public float CenterDistance()
{
return _centerDistance;
}
public float AngleLowerLimit => _angleLowerLimit;
public float AngleUpperLimit => _angleUpperLimit;
[JsonIgnore] private readonly Action<float> _angleWriter;
[JsonIgnore] private readonly Func<float> _angleReader;
private float _angleLowerLimit = -90, _angleUpperLimit = 90;
private float _centerDistance;
public float _sendAngle = 0f;
}
}
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using Newtonsoft.Json;
namespace CommonUsage.Chassis
{
public class Wheel
{
public Wheel(Vector2 position, Action<float> speedWriter, Func<float> speedReader)
{
PhysicalPosition = Position = position;
SpeedWriter = speedWriter;
SpeedReader = speedReader;
}
public void WriteSpeed(float speed)
{
SpeedWriter.Invoke(_sendSpeed = speed);
}
public float GetSendSpeed()
{
return _sendSpeed;
}
public float ReadSpeed()
{
return SpeedReader();
}
// PhysicalPosition ===(chassis transform)===> Position
// useful in dual agv coordination
public readonly Vector2 PhysicalPosition;
public Vector2 Position;
public float ZeroDirection = 0;
[JsonIgnore] public readonly Action<float> SpeedWriter;
[JsonIgnore] public readonly Func<float> SpeedReader;
public float _sendSpeed;
}
public class GeometricControlPoint
{
public GeometricControlPoint(Vector2 position)
{
Position = position;
}
public Vector2 Position;
public float Theta;
}
}