Files
ParkingRobot/MultiWheelC/Movements/LineTracking.cs
T

179 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using ClumsyCore;
using ClumsyCore.DTools;
using ClumsyCore.Interfaces;
using ClumsyCore.Pilot;
using CommonUsage.Chassis;
using FundamentalLib;
using MDCSToolBox.Clumsy.Tracks;
using MDCSToolBox.Commons.Controllers;
using MyParking.Shared;
namespace MultiWheelC
{
// C层单车底盘:按照车轮里程行驶指定的相对距离。
public class LineTracking : MovementDefinition
{
// 相对动作启动位置的行驶距离,单位mm。
// 正数表示前进,负数表示后退。
public float TargetDistance;
public float MaxSpeed = PilotDefinition.Conf.LineTrackMaxSpeed;
public float Kp = PilotDefinition.Conf.LineTrackKp;
public float Ki = PilotDefinition.Conf.LineTrackKi;
public float Kd = PilotDefinition.Conf.LineTrackKd;
public float DeadZone = PilotDefinition.Conf.LineTrackDeadZone;
public int SrcId = -1;
public int DstId = -1;
public Action<int> LeaveSrcFunction;
// 接近目标后是否保留速度,交给下一个动作接管。
public bool EnableHandover;
// 进入动作衔接的剩余距离,单位mm。
public float HandoverDistance = 80f;
// HandoverSpeed小于0时,使用MaxSpeed的此比例。
public float HandoverSpeedRatio = 0.5f;
// 大于等于0时,直接作为衔接速度,单位m/s。
public float HandoverSpeed = -1f;
public float MinHandoverSpeed = 0.05f;
private PIDController _pid;
// 读取当前单车直线行驶里程,单位mm。
private static float ReadPosition()
{
return
(PilotDefinition.Self.LFLActualPos + PilotDefinition.Self.LFRActualPos) / 2f;
}
// 根据动作启动位置和目标距离执行直线里程闭环。
public override IEnumerable<bool> Get()
{
if (float.IsNaN(TargetDistance) || float.IsInfinity(TargetDistance))
{
throw new ArgumentOutOfRangeException(
nameof(TargetDistance),
"目标行驶距离必须是有限值。");
}
if (float.IsNaN(MaxSpeed) || float.IsInfinity(MaxSpeed) || MaxSpeed <= 0f)
{
throw new ArgumentOutOfRangeException(
nameof(MaxSpeed),
"最大速度必须是大于零的有限值。");
}
var chassis = (MultiWheelChassis)PilotDefinition.Chassis;
// 每次启动动作时重新读取起始编码器位置。
var startPosition = ReadPosition();
// PID仍然控制绝对编码器位置,但绝对目标由动作自动计算。
var targetPosition = startPosition + TargetDistance;
_pid = new PIDController(ReadPosition, Kp, Ki, Kd, 0, DeadZone, MaxSpeed)
{
SpeedAccPerSec = Math.Abs(MaxSpeed) / 2f
};
var handoverRequested = false;
var keepHandoverSpeed = false;
DLog.Log(
$"直线里程动作:" +
$"起点={startPosition:F1}mm" +
$"距离={TargetDistance:F1}mm" +
$"目标={targetPosition:F1}mm",
"straight_line");
try
{
while (true)
{
var currentPosition = ReadPosition();
var remainingDistance = targetPosition - currentPosition;
// 接近目标后,保留一定速度交给后续动作。
if (EnableHandover && Math.Abs(remainingDistance) <= Math.Max(1f, HandoverDistance))
{
var direction = Math.Sign(remainingDistance);
if (direction == 0)
{
direction = Math.Sign(TargetDistance);
}
var requestedSpeed = HandoverSpeed >= 0f ? Math.Abs(HandoverSpeed) : Math.Abs(MaxSpeed) * HandoverSpeedRatio;
var maximumSpeed = Math.Abs(MaxSpeed);
var minimumSpeed = Math.Min(Math.Abs(MinHandoverSpeed), maximumSpeed);
var limitedSpeed = Math.Max(minimumSpeed, Math.Min(requestedSpeed, maximumSpeed));
var handoverSpeed = limitedSpeed * direction;
chassis.SendXYThSpeed(handoverSpeed, 0f, 0f);
handoverRequested = true;
// 保持一个调度周期,让速度命令实际生效。
yield return true;
break;
}
var speed = _pid.GetResponse(targetPosition);
chassis.SendXYThSpeed(speed, 0f, 0f);
if (_pid.IsArrived())
{
break;
}
yield return true;
}
if (SrcId != -1 &&
LeaveSrcFunction != null)
{
LeaveSrcFunction(SrcId);
DLog.Log($"释放放车点{SrcId}", "straight_line");
}
// 只有正常完成动作衔接时才允许保留非零速度。
keepHandoverSpeed = handoverRequested;
}
finally
{
// 普通完成、人工停止或异常退出时都必须停车。
if (!keepHandoverSpeed)
{
chassis.SendXYThSpeed(0f, 0f, 0f);
}
}
yield return false;
}
}
//直线行走基于detour
public class LineTracking_based_detour : MovementDefinition
{
public float LineDistance = 1000f;
public int SrcId = -1;
public int DstId = -1;
public Action<int> LeaveSrcFunction = null;
public Painter painter = UI.GetPainter("Line", false);
// C层单车轨迹:执行早期版本的两点直线跟踪动作。
public override IEnumerable<bool> Get()
{
var curpose = DetourInterface.getCartLocation();
Console.WriteLine($"curpose.th:{curpose.th}");
var src = new Vector2((float)curpose.x, (float)curpose.y);
var headingRadians =
AngleMath.DegreesToRadians(curpose.th);
var dst = new Vector2(
(float)(curpose.x +
LineDistance * Math.Cos(headingRadians)),
(float)(curpose.y +
LineDistance * Math.Sin(headingRadians)));
// var dst = new Vector2((float)curpose.x + LineDistance * (float)Math.Cos(curpose.th),
// (float)curpose.y + LineDistance * (float)Math.Sin(curpose.th));
Console.WriteLine($"src:{src.X} {src.Y}");
Console.WriteLine($"dst:{dst.X} {dst.Y}");
painter.DrawLine(Color.Green, src.X, src.Y, dst.X, dst.Y, width: 3);
var tracker = new ChassisController().Get();
var linePath = new LineTrack(src, dst) { CarDirectionBias = LineDistance > 0 ? 0 : 180 };
tracker.AddTrack(linePath);
var _dt = new DriveTask(tracker.Track());
_dt.Wait();
if (SrcId != -1 && LeaveSrcFunction != null)
{
LeaveSrcFunction(SrcId);
DLog.Log($"释放放车点{SrcId}", "straight_line");
}
yield return false;
}
}
}