makcar/app/src/main/java/com/aros/apron/tools/ApronArucoDetect.java

609 lines
25 KiB
Java
Raw Normal View History

2026-01-30 11:47:32 +08:00
package com.aros.apron.tools;
import android.os.Handler;
import android.os.Looper;
2026-01-30 11:47:32 +08:00
import com.aros.apron.constant.AMSConfig;
import com.aros.apron.entity.ArucoMarker;
import com.aros.apron.entity.Movement;
import com.aros.apron.manager.AlternateLandingManager;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
2026-02-01 14:34:23 +08:00
import org.opencv.core.Size;
2026-01-30 11:47:32 +08:00
import org.opencv.imgproc.Imgproc;
2026-02-01 14:34:23 +08:00
import org.opencv.objdetect.ArucoDetector;
import org.opencv.objdetect.DetectorParameters;
import org.opencv.objdetect.Dictionary;
2026-01-30 11:47:32 +08:00
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import dji.sdk.keyvalue.key.FlightControllerKey;
import dji.sdk.keyvalue.key.KeyTools;
import dji.sdk.keyvalue.value.common.EmptyMsg;
import dji.v5.common.callback.CommonCallbacks;
import dji.v5.common.error.IDJIError;
import dji.v5.manager.KeyManager;
2026-03-12 14:57:34 +08:00
/**
* 2帧一处理0处理-1跳过-2处理-3跳过
* 跳过的帧直接return啥也不干让飞机自己飘
*/
public class ApronArucoDetect {
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
private static final String TAG = "ApronArucoDetect";
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
// ========== 原有参数 ==========
private static double LENS_OFFSET_X = 0;
private static double LENS_OFFSET_Y = 0;
private static final int CENTER_ERR_MAX = 30;
private static final float SLOW_LAND_SPEED = -0.2f;
private static final float SLOW_SUPER_SPEED = -0.1f;
private static final int PIXEL_TRIGGER = 360;
private static final int TRIGGER_FRAME_THRESHOLD = 2;
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
// ========== 原有状态 ==========
private boolean isTriggerSuccess;
private boolean arucoNotFoundTag = false;
2026-02-01 14:34:23 +08:00
private boolean isStartAruco = false;
2026-01-30 11:47:32 +08:00
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Future<?> lastFuture = null;
2026-03-12 14:57:34 +08:00
private String TAG_LOG = getClass().getSimpleName();
2026-01-30 11:47:32 +08:00
Double resultYaw = 0.0;
private boolean triggerToAlternateLandingPoint;
long startTime;
long endTime;
private int sigleMarkerDetectFailsTimes;
private boolean dropTimesTag;
2026-03-12 14:57:34 +08:00
private int dropTimes = 0;
2026-01-30 11:47:32 +08:00
private boolean isDoublePayload;
2026-03-12 14:57:34 +08:00
private int trycount = 0;
2026-02-01 14:34:23 +08:00
2026-01-30 11:47:32 +08:00
public PIDControl pidControlX = null;
public PIDControl pidControlY = null;
public int checkThrowingErrorsTimes;
2026-03-12 14:57:34 +08:00
private int consecutiveTriggerCount = 0;
// ========== 【新增】2帧一处理计数器 ==========
private int frameCounter = 0;
// ========== 原有方法 ==========
public int getCheckThrowingErrorsTimes() { return checkThrowingErrorsTimes; }
public void setCheckThrowingErrorsTimes(int v) { checkThrowingErrorsTimes = v; }
public boolean isTriggerSuccess() { return isTriggerSuccess; }
public void setTriggerSuccess(boolean v) { isTriggerSuccess = v; }
public boolean isDoublePayload() { return isDoublePayload; }
public void setDoublePayload(boolean v) { isDoublePayload = v; }
public boolean isStartFastStick() { return startFastStick; }
public void setStartFastStick(boolean v) { startFastStick = v; }
public boolean isCanLanding() { return canLanding; }
public void setCanLanding(boolean v) {
startTime = 0;
endTime = 0;
canLanding = v;
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
private ApronArucoDetect() {}
2026-01-30 11:47:32 +08:00
private static class OpenCVHelperHolder {
private static final ApronArucoDetect INSTANCE = new ApronArucoDetect();
2026-03-12 14:57:34 +08:00
static { INSTANCE.init(); }
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
public static ApronArucoDetect getInstance() { return OpenCVHelperHolder.INSTANCE; }
2026-02-01 14:34:23 +08:00
2026-01-30 11:47:32 +08:00
public void init() {
2026-03-12 14:57:34 +08:00
pidControlX = new PIDControl(0.65f, 0.02f, 0.2f, 0.05f, 2.5f, 0.1f);
pidControlY = new PIDControl(0.65f, 0.02f, 0.2f, 0.05f, 2.5f, 0.1f);
// pidControlX = new PIDControl(0.8f, 0.002f, 0.09f, 0.05f, 2.0f, 0.05f);
// pidControlY = new PIDControl(0.8f, 0.002f, 0.09f, 0.05f, 2.0f, 0.05f);
2026-01-30 11:47:32 +08:00
pidControlX.reset();
pidControlY.reset();
}
2026-03-12 14:57:34 +08:00
public boolean startFastStick;
public boolean canLanding;
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
// ========== 【核心修改】主入口2帧一处理 ==========
2026-01-30 11:47:32 +08:00
public void detectArucoTags(int height, int width, byte[] data, Dictionary dictionary) {
2026-02-01 14:34:23 +08:00
isTriggerSuccess = true;
2026-01-30 11:47:32 +08:00
Movement.getInstance().setVirtualStickEnableReason(2);
2026-03-12 14:57:34 +08:00
// 原有过滤
2026-01-30 11:47:32 +08:00
if (isStartAruco || startFastStick) {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "过滤:" + isStartAruco + startFastStick);
2026-02-01 14:34:23 +08:00
if (!isStartAruco && startFastStick) {
2026-01-30 11:47:32 +08:00
checkThrowingErrorsTimes++;
}
return;
}
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// ========== 【关键】2帧一处理0处理-1跳过-2处理-3跳过 ==========
int currentFrame = frameCounter++;
boolean shouldProcess = (currentFrame % 2 == 0); // 偶数帧处理,奇数帧跳过
if (!shouldProcess) {
// 【关键】跳过的帧啥也不干直接return让飞机自己飘
LogUtil.log(TAG_LOG, "【跳过帧】" + currentFrame + " 让飞机自稳");
return;
}
LogUtil.log(TAG_LOG, "【处理帧】" + currentFrame + " 执行修正");
2026-01-30 11:47:32 +08:00
isStartAruco = true;
2026-01-30 11:47:32 +08:00
if (lastFuture != null && !lastFuture.isDone()) {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "break---");
2026-01-30 11:47:32 +08:00
lastFuture.cancel(true);
}
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// 动态偏移(原有)
int ultHeight = Movement.getInstance().getUltrasonicHeight();
if (ultHeight >= 30) { LENS_OFFSET_X = 0; LENS_OFFSET_Y = 0; }
else if (ultHeight >= 20) { LENS_OFFSET_X = 0; LENS_OFFSET_Y = 0; }
else if (ultHeight >= 10) { LENS_OFFSET_X = 20; LENS_OFFSET_Y = 10; }
else if (ultHeight >= 5) { LENS_OFFSET_X = 30; LENS_OFFSET_Y = 20; }
else { LENS_OFFSET_X = 55; LENS_OFFSET_Y = 35; }
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// ========== 原有完整逻辑(一点不改) ==========
2026-02-01 14:34:23 +08:00
lastFuture = executor.schedule(new Runnable() {
2026-01-30 11:47:32 +08:00
@Override
public void run() {
try {
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
yuvMat.put(0, 0, data);
Mat rgbMat = new Mat();
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2BGR_I420);
Mat grayImgMat = new Mat();
2026-02-01 14:34:23 +08:00
Imgproc.cvtColor(rgbMat, grayImgMat, Imgproc.COLOR_BGR2GRAY);
2026-03-12 14:57:34 +08:00
// ========== 【极致容错】双重检测策略 ==========
// 策略1增强图检测适应低光/反光)
Mat enhancedMat = createEnhancedImage(grayImgMat);
2026-01-30 11:47:32 +08:00
MatOfInt ids = new MatOfInt();
2026-02-01 14:34:23 +08:00
List<Mat> corners = new ArrayList<>();
2026-03-12 14:57:34 +08:00
DetectorParameters params = createUltraTolerantParams();
ArucoDetector detector = new ArucoDetector(dictionary, params);
detector.detectMarkers(enhancedMat, corners, ids);
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// 策略2如果失败用原图兜底适应过曝
2026-02-01 14:34:23 +08:00
if (ids.empty()) {
corners.clear();
detector.detectMarkers(grayImgMat, corners, ids);
}
2026-03-12 14:57:34 +08:00
// 策略3如果还失败降采样检测适应高分辨率模糊
if (ids.empty()) {
corners.clear();
Mat downscaled = new Mat();
Imgproc.resize(grayImgMat, downscaled, new Size(grayImgMat.width() * 0.8, grayImgMat.height() * 0.8));
detector.detectMarkers(downscaled, corners, ids);
// 坐标映射回原图比例
if (!ids.empty()) {
for (Mat corner : corners) {
Core.multiply(corner, new Scalar(1.25, 1.25), corner);
}
}
downscaled.release();
}
2026-02-01 14:34:23 +08:00
ids = keepOnly6(ids, corners);
2026-01-30 11:47:32 +08:00
2026-02-01 14:34:23 +08:00
boolean marker6Found = false;
2026-03-12 14:57:34 +08:00
Mat corner6 = new Mat();
2026-02-01 14:34:23 +08:00
if (!ids.empty()) {
2026-03-12 14:57:34 +08:00
trycount = 0;
2026-01-30 11:47:32 +08:00
arucoNotFoundTag = false;
int[] idArray = ids.toArray();
int ultrasonicHeight = Movement.getInstance().getUltrasonicHeight();
2026-02-01 14:34:23 +08:00
if (idArray.length == 1 && idArray[0] == 6) {
marker6Found = true;
corner6 = corners.get(0);
Point[] points = new Point[4];
for (int i = 0; i < 4; i++) {
double[] p = corner6.get(0, i);
points[i] = new Point(p[0], p[1]);
}
double pixelWidth = calculateDistance(points[0], points[1]);
2026-03-12 14:57:34 +08:00
double centerX = (points[0].x + points[1].x + points[2].x + points[3].x) / 4.0 + LENS_OFFSET_X;
double errX = Math.abs(centerX - rgbMat.width() / 2.0);
double centerY = (points[0].y + points[1].y + points[2].y + points[3].y) / 4.0 + LENS_OFFSET_Y;
double errY = Math.abs(centerY - rgbMat.height() / 2.0);
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "像素" + (int) pixelWidth +
2026-02-01 14:34:23 +08:00
" errX=" + (int) errX + " errY=" + (int) errY + " (含偏移" + LENS_OFFSET_X + ")");
2026-03-12 14:57:34 +08:00
// 速降判断(原有)
if (!startFastStick) {
if (pixelWidth >= PIXEL_TRIGGER &&
ultrasonicHeight <= 4 &&
errX < CENTER_ERR_MAX &&
errY < CENTER_ERR_MAX) {
consecutiveTriggerCount++;
LogUtil.log(TAG_LOG, "速降条件满足,累计帧数: " + consecutiveTriggerCount);
if (consecutiveTriggerCount >= TRIGGER_FRAME_THRESHOLD) {
startFastStick = true;
consecutiveTriggerCount = 0;
LogUtil.log(TAG_LOG, "【触发速降】连续满足" + TRIGGER_FRAME_THRESHOLD +
"pixel=" + (int) pixelWidth + " errX=" + (int) errX);
handler.postDelayed(new Runnable() {
@Override
public void run() {
handler.post(runnable);
LogUtil.log(TAG_LOG, "延迟结束,开始速降");
}
}, 300);
// 清理资源
enhancedMat.release();
grayImgMat.release();
rgbMat.release();
yuvMat.release();
ids.release();
if (!corners.isEmpty()) {
for (Mat c : corners) if (c != null) c.release();
}
isStartAruco = false;
return;
}
2026-03-12 14:57:34 +08:00
} else {
if (consecutiveTriggerCount > 0) {
LogUtil.log(TAG_LOG, "速降条件中断,重置累计帧数(原因:像素=" + (int)pixelWidth +
" 超声=" + ultrasonicHeight + " errX=" + (int)errX + " errY=" + (int)errY + ")");
consecutiveTriggerCount = 0;
}
}
2026-01-30 11:47:32 +08:00
}
}
2026-03-12 14:57:34 +08:00
// 原有修正逻辑
if (marker6Found) {
2026-02-01 14:34:23 +08:00
moveOnArucoDetected(new ArucoMarker(1, corner6, 0.24f), rgbMat.width(), rgbMat.height());
2026-01-30 11:47:32 +08:00
}
dropTimesTag = true;
2026-03-12 14:57:34 +08:00
2026-02-01 14:34:23 +08:00
} else {
2026-03-12 14:57:34 +08:00
// 原有丢失处理
LogUtil.log(TAG_LOG, "找不到了二维码");
2026-01-30 11:47:32 +08:00
if (!arucoNotFoundTag) {
startTime = System.currentTimeMillis();
arucoNotFoundTag = true;
}
endTime = System.currentTimeMillis();
2026-02-01 14:34:23 +08:00
long lostDuration = endTime - startTime;
2026-03-12 14:57:34 +08:00
if (lostDuration > 1000 && lostDuration <= 12000) {
if (Movement.getInstance().getUltrasonicHeight() <= 20) {
DroneHelper.getInstance().moveVxVyYawrateHeight(0f, 0f, 0f, 3f);
2026-01-30 11:47:32 +08:00
if (dropTimes > Integer.parseInt(AMSConfig.getInstance().getAlternateLandingTimes())) {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "超过复降限制,去备降点");
2026-01-30 11:47:32 +08:00
AlternateLandingManager.getInstance().startTaskProcess(null);
2026-03-12 14:57:34 +08:00
Movement.getInstance().setAlternate(true);
// 清理资源
enhancedMat.release();
grayImgMat.release();
rgbMat.release();
yuvMat.release();
ids.release();
if (!corners.isEmpty()) {
for (Mat c : corners) if (c != null) c.release();
}
isStartAruco = false;
2026-01-30 11:47:32 +08:00
return;
}
if (dropTimesTag) {
dropTimesTag = false;
dropTimes++;
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "复降第:" + dropTimes + "");
2026-01-30 11:47:32 +08:00
}
2026-02-01 14:34:23 +08:00
} else {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "执行位移");
2026-02-01 14:34:23 +08:00
DroneHelper.getInstance().moveVxVyYawrateHeight(0f, 0f, 0f, -0.3f);
2026-01-30 11:47:32 +08:00
}
2026-02-01 14:34:23 +08:00
} else if (lostDuration > 8000) {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "判定未识别到二维码,飞往备降点");
2026-02-01 14:34:23 +08:00
AlternateLandingManager.getInstance().startTaskProcess(null);
2026-01-30 11:47:32 +08:00
}
}
2026-03-12 14:57:34 +08:00
// 原有资源释放
enhancedMat.release();
2026-01-30 11:47:32 +08:00
grayImgMat.release();
2026-02-01 14:34:23 +08:00
rgbMat.release();
yuvMat.release();
ids.release();
if (!corners.isEmpty()) {
for (Mat c : corners) {
if (c != null) c.release();
}
}
2026-01-30 11:47:32 +08:00
isStartAruco = false;
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
} catch (Exception e) {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "Exception" + e);
2026-01-30 11:47:32 +08:00
isStartAruco = false;
}
}
2026-02-01 14:34:23 +08:00
}, 0, TimeUnit.MILLISECONDS);
}
2026-03-12 14:57:34 +08:00
// ========== 以下全部原有方法,一点不改 ==========
2026-02-01 14:34:23 +08:00
private static MatOfInt keepOnly6(MatOfInt ids, List<Mat> corners) {
if (ids.empty()) return ids;
int[] idArr = ids.toArray();
List<Integer> keepIdx = new ArrayList<>();
for (int i = 0; i < idArr.length; i++) if (idArr[i] == 6) keepIdx.add(i);
List<Mat> tmpCorners = new ArrayList<>(keepIdx.size());
for (int i : keepIdx) tmpCorners.add(corners.get(i));
corners.clear();
ids.release();
int[] newIds = new int[keepIdx.size()];
for (int j = 0; j < keepIdx.size(); j++) newIds[j] = 6;
2026-03-12 14:57:34 +08:00
corners.addAll(tmpCorners);
2026-02-01 14:34:23 +08:00
return new MatOfInt(newIds);
}
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
// ========== 【极致容错】图像增强:全高度段通用 ==========
private Mat createEnhancedImage(Mat src) {
2026-02-01 14:34:23 +08:00
Mat result = new Mat();
try {
2026-03-12 14:57:34 +08:00
// 1. 多尺度CLAHE适应不同亮度
Mat claheMat = new Mat();
Imgproc.createCLAHE(2.0, new Size(8, 8)).apply(src, claheMat);
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// 2. 中值滤波去噪(比双边快,保边足够)
Mat filtered = new Mat();
Imgproc.medianBlur(claheMat, filtered, 5);
claheMat.release();
// 3. 自适应阈值(块大小动态,适应全高度)
Mat binary = new Mat();
Imgproc.adaptiveThreshold(filtered, binary, 255,
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
Imgproc.THRESH_BINARY, 41, 3); // 块41更大常数3更敏感
2026-02-01 14:34:23 +08:00
filtered.release();
2026-03-12 14:57:34 +08:00
// 4. 形态学操作(连接断裂边框)
Mat morph = new Mat();
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
Imgproc.morphologyEx(binary, morph, Imgproc.MORPH_CLOSE, kernel);
kernel.release();
binary.release();
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
// 5. 轻度锐化(突出边缘,不过度)
Mat sharpened = new Mat();
Mat blurred = new Mat();
Imgproc.GaussianBlur(morph, blurred, new Size(0, 0), 3.0);
Core.addWeighted(morph, 1.3, blurred, -0.3, 0, sharpened);
morph.release();
2026-02-01 14:34:23 +08:00
blurred.release();
2026-03-12 14:57:34 +08:00
return sharpened;
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
} catch (Exception e) {
LogUtil.log(TAG_LOG, "增强失败: " + e.getMessage());
2026-02-01 14:34:23 +08:00
src.copyTo(result);
return result;
}
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
private DetectorParameters createUltraTolerantParams() {
DetectorParameters params = new DetectorParameters();
// 全高度段4dm=375px, 9dm=190px, 50dm=19px
params.set_minMarkerPerimeterRate(0.003f); // 降到0.00319像素也能检
// 畸变/反光/模糊宽容
params.set_polygonalApproxAccuracyRate(0.12f); // 更松
params.set_cornerRefinementMethod(1); // SUBPIX
params.set_cornerRefinementWinSize(3); // 降到3更快
params.set_cornerRefinementMaxIterations(30);
params.set_cornerRefinementMinAccuracy(0.12f); // 放宽收敛
// 阈值范围更大,适应全光照
params.set_adaptiveThreshWinSizeMin(3);
params.set_adaptiveThreshWinSizeMax(63); // 更大
params.set_adaptiveThreshWinSizeStep(10);
params.set_adaptiveThreshConstant(3); // 配合预处理
params.set_minCornerDistanceRate(0.02f);
params.set_minMarkerLengthRatioOriginalImg(0.03f); // 更宽容
params.set_minDistanceToBorder(1); // 边缘也检
params.set_perspectiveRemovePixelPerCell(2); // 降到2小像素精细
params.set_perspectiveRemoveIgnoredMarginPerCell(0.2f);
params.set_maxErroneousBitsInBorderRate(0.6f); // 60%容错
params.set_detectInvertedMarker(false);
return params;
}
2026-01-30 11:47:32 +08:00
private double calculateDistance(Point p1, Point p2) {
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
public void setDetectedBigMarkers() {
2026-03-12 14:57:34 +08:00
startFastStick = false;
isStartAruco = false;
consecutiveTriggerCount = 0;
frameCounter = 0; // 【新增】重置帧计数
2026-01-30 11:47:32 +08:00
}
2026-02-01 14:34:23 +08:00
2026-03-12 14:57:34 +08:00
private double calculateYawErrorFromCorners(Point[] pts) {
double dxTop = pts[1].x - pts[0].x;
double dyTop = pts[1].y - pts[0].y;
double angleTop = Math.toDegrees(Math.atan2(dyTop, dxTop));
double dxBottom = pts[2].x - pts[3].x;
double dyBottom = pts[2].y - pts[3].y;
double angleBottom = Math.toDegrees(Math.atan2(dyBottom, dxBottom));
double yawError = (angleTop + angleBottom) / 2.0;
while (yawError > 180) yawError -= 360;
while (yawError < -180) yawError += 360;
return yawError;
}
private void moveOnArucoDetected(ArucoMarker marker, int imageWidth, int imageHeight) {
Mat corner6 = marker.getConner();
2026-02-01 14:34:23 +08:00
Point[] pts = new Point[4];
for (int i = 0; i < 4; i++) {
double[] p = corner6.get(0, i);
pts[i] = new Point(p[0], p[1]);
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
double cx = (pts[0].x + pts[1].x + pts[2].x + pts[3].x) / 4.0 + LENS_OFFSET_X;
double cy = (pts[0].y + pts[1].y + pts[2].y + pts[3].y) / 4.0 + LENS_OFFSET_Y;
2026-02-01 14:34:23 +08:00
Point screenCenter = new Point(imageWidth / 2.0, imageHeight / 2.0);
2026-01-30 11:47:32 +08:00
double errX = (cx - screenCenter.x);
double errY = (cy - screenCenter.y);
2026-03-12 14:57:34 +08:00
int currentHeight = Movement.getInstance().getUltrasonicHeight();
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
double scaleFactor;
if (currentHeight >= 50) {
scaleFactor = 300.0; // 50m: 像素3, 大力修正
} else if (currentHeight >= 30) {
scaleFactor = 500.0; // 30m: 像素5
} else if (currentHeight >= 20) {
scaleFactor = 600.0; // 20m: 像素8
} else if (currentHeight >= 10) {
scaleFactor = 700.0; // 10m: 像素16
} else {
scaleFactor = 900.0; // 1m: 像素159, 温柔修正
}
2026-03-12 14:57:34 +08:00
pidControlX.setInputFilterAll((float) (errX / scaleFactor));
pidControlY.setInputFilterAll((float) (-errY / scaleFactor));
float rawVx = pidControlX.get_pid();
float rawVy = pidControlY.get_pid();
2026-03-12 14:57:34 +08:00
float yawRate = 0f;
2026-03-12 14:57:34 +08:00
float vx = (float) Math.max(-0.2, Math.min(0.2, rawVx));
float vy = (float) Math.max(-0.2, Math.min(0.2, rawVy));
2026-02-01 14:34:23 +08:00
double pixelWidth = Math.sqrt(Math.pow(pts[1].x - pts[0].x, 2) +
Math.pow(pts[1].y - pts[0].y, 2));
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
if (currentHeight >= 10 && currentHeight <= 55) {
double yawError = calculateYawErrorFromCorners(pts);
double absError = Math.abs(yawError);
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
if (absError > 10.0) {
float targetRate;
if (absError > 100.0) targetRate = 50.0f;
else if (absError > 50.0) targetRate = 30.0f;
else if (absError > 30.0) targetRate = 20.0f;
else targetRate = 10.0f;
yawRate = yawError > 0 ? targetRate : -targetRate;
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
String speedLabel = absError > 100 ? "高速50" :
absError > 50 ? "中速30" :
absError > 30 ? "低速20" : "微调10";
LogUtil.log(TAG_LOG, "机头矫正:误差=" + (int)yawError + "° 转速=" + yawRate + "°/s [" + speedLabel + "]");
} else {
yawRate = 0.0f;
LogUtil.log(TAG_LOG, "机头已对准:偏航误差=" + (int)yawError + "°");
}
} else {
yawRate = 0.0f;
}
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
float vz;
if (currentHeight <= 4) {
vz = 0.0f;
if (Math.abs(errX) > 120) {
vx = rawVx > 0 ? 0.135f : -0.135f;
} else if (Math.abs(errX) > 80) {
vx = rawVx > 0 ? 0.09f : -0.09f;
} else if (Math.abs(errX) > 60) {
vx = rawVx > 0 ? 0.07f : -0.07f;
} else if (Math.abs(errX) > 30) {
vx = rawVx > 0 ? 0.05f : -0.05f;
} else {
vx = 0f;
}
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
if (Math.abs(errY) > 120) {
vy = rawVy > 0 ? 0.135f : -0.135f;
} else if (Math.abs(errY) > 80) {
vy = rawVy > 0 ? 0.09f : -0.09f;
} else if (Math.abs(errY) > 60) {
vy = rawVy > 0 ? 0.07f : -0.07f;
} else if (Math.abs(errY) > 30) {
vy = rawVy > 0 ? 0.05f : -0.05f;
} else {
vy = 0f; // 【修正】
}
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
} else if (currentHeight <= 8) {
vz = SLOW_SUPER_SPEED;
} else {
vz = SLOW_LAND_SPEED;
}
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
DroneHelper.getInstance().moveVxVyYawrateHeight(vx, vy, yawRate, vz);
2026-01-30 11:47:32 +08:00
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "vx" + vx + "vy" + vy + " errX=" + (int) errX + " errY=" + (int) errY +
" pixelW=" + (int) pixelWidth + " vz=" + vz + " ult=" + currentHeight + " yaw=" + yawRate);
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
private int handlerCallbackCount = 0;
2026-01-30 11:47:32 +08:00
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable runnable = new Runnable() {
@Override
public void run() {
performOperation();
2026-03-12 14:57:34 +08:00
if (handlerCallbackCount < 20) {
handler.postDelayed(this, 50);
2026-01-30 11:47:32 +08:00
} else {
performNextStep();
}
}
};
private void performOperation() {
2026-03-12 14:57:34 +08:00
LogUtil.log(TAG_LOG, "快速下拉中..." + handlerCallbackCount);
2026-01-30 11:47:32 +08:00
DroneHelper.getInstance().moveVxVyYawrateHeight(0f, 0f, 0f, -4);
2026-03-12 14:57:34 +08:00
handlerCallbackCount++;
2026-01-30 11:47:32 +08:00
}
private void performNextStep() {
canLanding = true;
handlerCallbackCount = 0;
2026-03-12 14:57:34 +08:00
dropTimes = 0;
handler.removeCallbacks(runnable);
2026-01-30 11:47:32 +08:00
}
}