[Matlab]椭球拟合+正圆修正+Qt转换

% 已知三组数据(三个坐标平面上的截线) 椭球拟合
x1 = [-267, -215, -140, -99, -65, -90, -110, -128, -164, -222, -258, -390, -456, -506, -542, -559, -549, -509, -462, -395, -335];
y1 = [-276, -251, -213, -147, -74, 11, 43, 64, 106, 137, 154, 141, 114, 67, 3, -34, -110, -177, -225, -259, -273];

y2 = [-265, -227, -150, -74, -11, 43, 110, 140, 152, 138, 115, 78, 56, -8, -52, -131, -182, -238, -263, -281, -274];
z2 = [197, 286, 346, 365, 358, 340, 260, 214, 165, 36, -16, -58, -84, -123, -116, -98, -68, -13, 47, 109, 158];

x3 = [-514, -528, -506, -484, -472, -425, -388, -365, -335, -296, -241, -151, -126, -114, -66, -72, -99, -135, -214, -325, -383, -469, -518];
z3 = [113, 170, 251, 272, 300, 339, 362, 372, 363, 377, 354, 344, 318, 288, 208, 71, 17, -43, -73, -92, -60, -9, 65];

fprintf(‘开始椭球拟合…\n’);

% 方法:使用数值优化直接拟合椭球参数
% 椭球方程: (x-x0)²/a² + (y-y0)²/b² + (z-z0)²/c² = 1

% 初始参数估计
x0_est = mean([x1, x3]);
y0_est = mean([y1, y2]);
z0_est = mean([z2, z3]);
a_est = std([x1, x3]);
b_est = std([y1, y2]);
c_est = std([z2, z3]);

initial_params = [x0_est, y0_est, z0_est, a_est, b_est, c_est];
fprintf(‘初始参数: 中心(%.1f,%.1f,%.1f), 半径(%.1f,%.1f,%.1f)\n’, initial_params);

% 使用优化方法
options = optimset(‘Display’, ‘iter’, ‘MaxFunEvals’, 10000, ‘TolX’, 1e-6);
optimal_params = fminsearch(@(p) ellipsoid_error(p, x1, y1, y2, z2, x3, z3), initial_params, options);

x0 = optimal_params(1);
y0 = optimal_params(2);
z0 = optimal_params(3);
a = optimal_params(4);
b = optimal_params(5);
c = optimal_params(6);

fprintf(‘\n=== 椭球拟合结果 ===\n’);
fprintf(‘椭球方程: (x-%.2f)²/%.2f² + (y-%.2f)²/%.2f² + (z-%.2f)²/%.2f² = 1\n’, x0, a, y0, b, z0, c);
fprintf(‘椭球中心: (%.2f, %.2f, %.2f)\n’, x0, y0, z0);
fprintf(‘半轴长度: a=%.2f, b=%.2f, c=%.2f\n’, a, b, c);

% 生成椭球面(确保实数)
[x_ell, y_ell, z_ell] = generate_simple_ellipsoid(x0, y0, z0, a, b, c, 30);

% 计算三个平面的截线椭圆
theta = linspace(0, 2*pi, 100);

% XY平面截线 (z=0)
xy_x = x0 + a * cos(theta);
xy_y = y0 + b * sin(theta);

% YZ平面截线 (x=0)
yz_y = y0 + b * cos(theta);
yz_z = z0 + c * sin(theta);

% XZ平面截线 (y=0)
xz_x = x0 + a * cos(theta);
xz_z = z0 + c * sin(theta);

% 可视化结果
figure(‘Position’, [100, 100, 1400, 1000]);

% 子图1: 三维椭球与数据点
subplot(2,3,1);
surf(x_ell, y_ell, z_ell, ‘FaceAlpha’, 0.2, ‘EdgeColor’, ‘none’, ‘FaceColor’, ‘cyan’);
hold on;

% 绘制数据点
scatter3(x1, y1, zeros(size(x1)), 60, ‘r’, ‘filled’, ‘MarkerEdgeColor’, ‘k’);
scatter3(zeros(size(y2)), y2, z2, 60, ‘g’, ‘filled’, ‘MarkerEdgeColor’, ‘k’);
scatter3(x3, zeros(size(x3)), z3, 60, ‘b’, ‘filled’, ‘MarkerEdgeColor’, ‘k’);

scatter3(x0, y0, z0, 120, ‘k’, ‘filled’, ‘Marker’, ‘x’, ‘LineWidth’, 3);
title(‘三维椭球与截线数据点’);
xlabel(‘X’); ylabel(‘Y’); zlabel(‘Z’);
legend(‘椭球’, ‘XY平面数据’, ‘YZ平面数据’, ‘XZ平面数据’, ‘椭球中心’);
grid on; axis equal;

% 子图2: XY平面截线
subplot(2,3,2);
plot(xy_x, xy_y, ‘r-‘, ‘LineWidth’, 3);
hold on;
scatter(x1, y1, 50, ‘r’, ‘filled’);
scatter(x0, y0, 80, ‘k’, ‘x’, ‘LineWidth’, 2);
title(‘XY平面截线 (z=0)’);
xlabel(‘X’); ylabel(‘Y’);
legend(‘拟合椭圆’, ‘数据点’, ‘中心’);
grid on; axis equal;

% 子图3: YZ平面截线
subplot(2,3,3);
plot(yz_y, yz_z, ‘g-‘, ‘LineWidth’, 3);
hold on;
scatter(y2, z2, 50, ‘g’, ‘filled’);
scatter(y0, z0, 80, ‘k’, ‘x’, ‘LineWidth’, 2);
title(‘YZ平面截线 (x=0)’);
xlabel(‘Y’); ylabel(‘Z’);
legend(‘拟合椭圆’, ‘数据点’, ‘中心’);
grid on; axis equal;

% 子图4: XZ平面截线
subplot(2,3,4);
plot(xz_x, xz_z, ‘b-‘, ‘LineWidth’, 3);
hold on;
scatter(x3, z3, 50, ‘b’, ‘filled’);
scatter(x0, z0, 80, ‘k’, ‘x’, ‘LineWidth’, 2);
title(‘XZ平面截线 (y=0)’);
xlabel(‘X’); ylabel(‘Z’);
legend(‘拟合椭圆’, ‘数据点’, ‘中心’);
grid on; axis equal;

% 计算拟合误差
fprintf(‘\n=== 拟合质量分析 ===\n’);

% XY平面误差
errors_xy = zeros(size(x1));
for i = 1:length(x1)
errors_xy(i) = ((x1(i)-x0)^2/a^2 + (y1(i)-y0)^2/b^2) – 1;
end

% YZ平面误差
errors_yz = zeros(size(y2));
for i = 1:length(y2)
errors_yz(i) = ((y2(i)-y0)^2/b^2 + (z2(i)-z0)^2/c^2) – 1;
end

% XZ平面误差
errors_xz = zeros(size(x3));
for i = 1:length(x3)
errors_xz(i) = ((x3(i)-x0)^2/a^2 + (z3(i)-z0)^2/c^2) – 1;
end

fprintf(‘XY平面 – 平均绝对误差: %.4f, 最大误差: %.4f\n’, mean(abs(errors_xy)), max(abs(errors_xy)));
fprintf(‘YZ平面 – 平均绝对误差: %.4f, 最大误差: %.4f\n’, mean(abs(errors_yz)), max(abs(errors_yz)));
fprintf(‘XZ平面 – 平均绝对误差: %.4f, 最大误差: %.4f\n’, mean(abs(errors_xz)), max(abs(errors_xz)));

% 子图5: 拟合误差分布
subplot(2,3,5);
all_errors = [errors_xy, errors_yz, errors_xz];
histogram(all_errors, 20, ‘FaceColor’, ‘m’, ‘FaceAlpha’, 0.7);
xlabel(‘拟合误差’); ylabel(‘频数’);
title(‘拟合误差分布’);
grid on;

% 子图6: 数据点与拟合椭圆距离
subplot(2,3,6);
plot(1:length(errors_xy), abs(errors_xy), ‘ro-‘, ‘LineWidth’, 1.5, ‘MarkerSize’, 4);
hold on;
plot(length(errors_xy)+1:length(errors_xy)+length(errors_yz), abs(errors_yz), ‘go-‘, ‘LineWidth’, 1.5, ‘MarkerSize’, 4);
plot(length(errors_xy)+length(errors_yz)+1:length(all_errors), abs(errors_xz), ‘bo-‘, ‘LineWidth’, 1.5, ‘MarkerSize’, 4);
xlabel(‘数据点索引’); ylabel(‘绝对误差’);
title(‘各数据点拟合误差’);
legend(‘XY平面’, ‘YZ平面’, ‘XZ平面’);
grid on;

% 显示拟合优度
fprintf(‘总体平均绝对误差: %.4f\n’, mean(abs(all_errors)));

% 辅助函数:椭球误差计算
function error = ellipsoid_error(params, x1, y1, y2, z2, x3, z3)
x0 = params(1); y0 = params(2); z0 = params(3);
a = params(4); b = params(5); c = params(6);

total_error = 0;

% XY平面数据误差 (z=0)
for i = 1:length(x1)
error_val = ((x1(i)-x0)^2/a^2 + (y1(i)-y0)^2/b^2) – 1;
total_error = total_error + error_val^2;
end

% YZ平面数据误差 (x=0)
for i = 1:length(y2)
error_val = ((y2(i)-y0)^2/b^2 + (z2(i)-z0)^2/c^2) – 1;
total_error = total_error + error_val^2;
end

% XZ平面数据误差 (y=0)
for i = 1:length(x3)
error_val = ((x3(i)-x0)^2/a^2 + (z3(i)-z0)^2/c^2) – 1;
total_error = total_error + error_val^2;
end

error = total_error;
end

% 辅助函数:生成简单椭球面
function [x, y, z] = generate_simple_ellipsoid(x0, y0, z0, a, b, c, n)
theta = linspace(0, 2*pi, n);
phi = linspace(0, pi, n);
[Theta, Phi] = meshgrid(theta, phi);

% 生成椭球坐标(确保实数)
x = x0 + a * sin(Phi) .* cos(Theta);
y = y0 + b * sin(Phi) .* sin(Theta);
z = z0 + c * cos(Phi);

% 确保所有坐标都是实数
x = real(x);
y = real(y);
z = real(z);
end

//正圆修正

% 原始椭球参数
x0_orig = -303.51;
y0_orig = -60.65;
z0_orig = 132.76;
a_orig = 237.59;
b_orig = 215.39;
c_orig = 241.03;

fprintf(‘=== 原始椭球参数 ===\n’);
fprintf(‘中心: (%.2f, %.2f, %.2f)\n’, x0_orig, y0_orig, z0_orig);
fprintf(‘半轴长度: a=%.2f, b=%.2f, c=%.2f\n’, a_orig, b_orig, c_orig);

% 步骤1: 将椭球平移到原点
% 新坐标 = 原始坐标 – 中心坐标
fprintf(‘\n=== 坐标平移 ===\n’);
fprintf(‘平移向量: (%.2f, %.2f, %.2f)\n’, -x0_orig, -y0_orig, -z0_orig);

% 步骤2: 计算平均半径并生成正圆
r_avg = (a_orig + b_orig + c_orig) / 3;
fprintf(‘\n=== 半径归一化 ===\n’);
fprintf(‘原始平均半径: %.2f\n’, r_avg);
fprintf(‘目标半径: %.2f (正圆)\n’, r_avg);

% 生成原始椭球(用于对比)
[x_orig, y_orig, z_orig] = generate_ellipsoid(x0_orig, y0_orig, z0_orig, a_orig, b_orig, c_orig, 40);

% 生成平移后的椭球(中心在原点)
[x_centered, y_centered, z_centered] = generate_ellipsoid(0, 0, 0, a_orig, b_orig, c_orig, 40);

% 生成正圆球体
[x_sphere, y_sphere, z_sphere] = generate_sphere(0, 0, 0, r_avg, 40);

% 可视化结果
figure(‘Position’, [100, 100, 1500, 500]);

% 子图1: 原始椭球
subplot(1,3,1);
surf(x_orig, y_orig, z_orig, ‘FaceAlpha’, 0.3, ‘EdgeColor’, ‘none’, ‘FaceColor’, ‘blue’);
hold on;
scatter3(x0_orig, y0_orig, z0_orig, 100, ‘r’, ‘filled’, ‘Marker’, ‘x’, ‘LineWidth’, 3);
scatter3(0, 0, 0, 100, ‘k’, ‘filled’, ‘Marker’, ‘o’);
title(‘原始椭球’);
xlabel(‘X’); ylabel(‘Y’); zlabel(‘Z’);
legend(‘原始椭球’, ‘椭球中心’, ‘坐标原点’);
grid on; axis equal;

% 子图2: 平移后的椭球(中心在原点)
subplot(1,3,2);
surf(x_centered, y_centered, z_centered, ‘FaceAlpha’, 0.3, ‘EdgeColor’, ‘none’, ‘FaceColor’, ‘green’);
hold on;
scatter3(0, 0, 0, 100, ‘r’, ‘filled’, ‘Marker’, ‘x’, ‘LineWidth’, 3);
title(‘平移后椭球(中心在原点)’);
xlabel(‘X’); ylabel(‘Y’); zlabel(‘Z’);
legend(‘平移椭球’, ‘中心(0,0,0)’);
grid on; axis equal;

% 子图3: 正圆球体
subplot(1,3,3);
surf(x_sphere, y_sphere, z_sphere, ‘FaceAlpha’, 0.3, ‘EdgeColor’, ‘none’, ‘FaceColor’, ‘red’);
hold on;
scatter3(0, 0, 0, 100, ‘r’, ‘filled’, ‘Marker’, ‘x’, ‘LineWidth’, 3);
title(sprintf(‘正圆球体 (半径=%.2f)’, r_avg));
xlabel(‘X’); ylabel(‘Y’); zlabel(‘Z’);
legend(‘正圆球体’, ‘中心(0,0,0)’);
grid on; axis equal;

% 坐标变换函数
fprintf(‘\n=== 坐标变换公式 ===\n’);
fprintf(‘1. 坐标平移:\n’);
fprintf(‘ x_new = x_original – (%.2f)\n’, x0_orig);
fprintf(‘ y_new = y_original – (%.2f)\n’, y0_orig);
fprintf(‘ z_new = z_original – (%.2f)\n’, z0_orig);

fprintf(‘\n2. 半径归一化(生成正圆):\n’);
fprintf(‘ 目标半径 R = %.2f\n’, r_avg);
fprintf(‘ 对于任意点 (x, y, z),正圆坐标为:\n’);
fprintf(‘ magnitude = sqrt(x² + y² + z²)\n’);
fprintf(‘ scale = %.2f / magnitude\n’, r_avg);
fprintf(‘ x_sphere = x * scale\n’);
fprintf(‘ y_sphere = y * scale\n’);
fprintf(‘ z_sphere = z * scale\n’);

% 验证变换
fprintf(‘\n=== 变换验证 ===\n’);
test_points = [x0_orig, y0_orig, z0_orig; % 原始中心点
x0_orig + a_orig, y0_orig, z0_orig; % X轴端点
x0_orig, y0_orig + b_orig, z0_orig; % Y轴端点
x0_orig, y0_orig, z0_orig + c_orig]; % Z轴端点

for i = 1:size(test_points, 1)
x_orig = test_points(i, 1);
y_orig = test_points(i, 2);
z_orig = test_points(i, 3);

% 坐标平移
x_centered = x_orig – x0_orig;
y_centered = y_orig – y0_orig;
z_centered = z_orig – z0_orig;

% 半径归一化
magnitude = sqrt(x_centered^2 + y_centered^2 + z_centered^2);
scale = r_avg / magnitude;
x_sphere = x_centered * scale;
y_sphere = y_centered * scale;
z_sphere = z_centered * scale;
sphere_radius = sqrt(x_sphere^2 + y_sphere^2 + z_sphere^2);

fprintf(‘点%d:\n’, i);
fprintf(‘ 原始: (%.1f, %.1f, %.1f)\n’, x_orig, y_orig, z_orig);
fprintf(‘ 平移: (%.1f, %.1f, %.1f), 距离=%.1f\n’, x_centered, y_centered, z_centered, magnitude);
fprintf(‘ 正圆: (%.1f, %.1f, %.1f), 半径=%.1f\n’, x_sphere, y_sphere, z_sphere, sphere_radius);
end

% 完整的坐标变换函数
fprintf(‘\n=== 完整变换函数 ===\n’);
fprintf(‘function [x_sphere, y_sphere, z_sphere] = transformToSphere(x, y, z)\n’);
fprintf(‘ %% 输入: 原始坐标\n’);
fprintf(‘ %% 输出: 正圆球体上的坐标\n’);
fprintf(‘ \n’);
fprintf(‘ %% 椭球参数\n’);
fprintf(‘ x0 = %.4f;\n’, x0_orig);
fprintf(‘ y0 = %.4f;\n’, y0_orig);
fprintf(‘ z0 = %.4f;\n’, z0_orig);
fprintf(‘ R = %.4f;\n’, r_avg);
fprintf(‘ \n’);
fprintf(‘ %% 坐标平移\n’);
fprintf(‘ x_centered = x – x0;\n’);
fprintf(‘ y_centered = y – y0;\n’);
fprintf(‘ z_centered = z – z0;\n’);
fprintf(‘ \n’);
fprintf(‘ %% 半径归一化\n’);
fprintf(‘ magnitude = sqrt(x_centered^2 + y_centered^2 + z_centered^2);\n’);
fprintf(‘ if magnitude > 0\n’);
fprintf(‘ scale = R / magnitude;\n’);
fprintf(‘ x_sphere = x_centered * scale;\n’);
fprintf(‘ y_sphere = y_centered * scale;\n’);
fprintf(‘ z_sphere = z_centered * scale;\n’);
fprintf(‘ else\n’);
fprintf(‘ x_sphere = R; y_sphere = 0; z_sphere = 0;\n’);
fprintf(‘ end\n’);
fprintf(‘end\n’);

% 辅助函数:生成椭球面
function [x, y, z] = generate_ellipsoid(x0, y0, z0, a, b, c, n)
theta = linspace(0, 2*pi, n);
phi = linspace(0, pi, n);
[Theta, Phi] = meshgrid(theta, phi);

x = x0 + a * sin(Phi) .* cos(Theta);
y = y0 + b * sin(Phi) .* sin(Theta);
z = z0 + c * cos(Phi);
end

% 辅助函数:生成球体
function [x, y, z] = generate_sphere(x0, y0, z0, r, n)
theta = linspace(0, 2*pi, n);
phi = linspace(0, pi, n);
[Theta, Phi] = meshgrid(theta, phi);

x = x0 + r * sin(Phi) .* cos(Theta);
y = y0 + r * sin(Phi) .* sin(Theta);
z = z0 + r * cos(Phi);
end

//Qt转换

QVector3D MainWindow::transformToSphere(double x, double y, double z)
{
// 椭球参数
const double centerX = -303.51;
const double centerY = -60.65;
const double centerZ = 132.76;
const double radius = 231.34; // 平均半径

// 坐标平移
double x_centered = x – centerX;
double y_centered = y – centerY;
double z_centered = z – centerZ;

// 半径归一化
double magnitude = std::sqrt(x_centered * x_centered +
y_centered * y_centered +
z_centered * z_centered);

if (magnitude > 1e-10) {
double scale = radius / magnitude;
return QVector3D(x_centered * scale,
y_centered * scale,
z_centered * scale);
} else {
return QVector3D(radius, 0, 0);
}
}

[Orangepi]wifi模块异常不能用

执行如下sh文件
#!/bin/bash
# 用法: sudo ./fix_wifi.sh [SSID] [PASSWORD]
set -e

IF=”wlx7822884b2787″
SSID=”${1:-rtlwap}”
PASSWORD=”${2:-}”

echo “=== 1) 基本信息 ===”
echo “日期: $(date)”
uname -r
echo “—— lsmod 相关模块 ——”
lsmod | egrep -i ‘wl|rtl|8812|88xx|88|rtw|wlanuav’ || true
echo “—— ifconfig/ip link ——”
ip -c addr show “$IF” || ip -c link show
echo “—— iw dev (无线设备) ——”
iw dev || true

echo
echo “=== 2) 查看内核日志(相关条目) ===”
sudo dmesg | egrep -i “$IF|0bda|a81a|rtl88|8812|88×2|rtw|wlanuav|usb” | tail -n 200 || true

echo
echo “=== 3) 确保 RFKILL 未阻塞 ===”
rfkill list || true
echo “尝试解除软件阻塞…”
sudo rfkill unblock all || true
sleep 1
rfkill list || true

echo
echo “=== 4) 关闭驱动/设备的省电(iw)并设置国家码 ===”
sudo iw reg set SG || true
sudo iw dev “$IF” set power_save off || true

echo
echo “=== 5) 让 NetworkManager 管理该接口 ===”
sudo nmcli device set “$IF” managed yes || true
sudo systemctl restart NetworkManager
sleep 1
nmcli device status

echo
echo “=== 6) 重新加载模块(如果模块不稳定可尝试) ===”
# 尝试找到模块并重载(安全尝试)
MODNAME=$(lsmod | egrep -o ‘rtl[[:alnum:]_]*|88[[:alnum:]_]*|wl[a-z0-9_]*’ | head -n1 || true)
if [ -n “$MODNAME” ]; then
echo “检测到模块: $MODNAME — 尝试重载”
sudo modprobe -r “$MODNAME” 2>/dev/null || true
sleep 1
sudo modprobe “$MODNAME” 2>/dev/null || true
else
echo “未检测到可疑模块名称,跳过自动重载”
fi
sleep 1

echo
echo “=== 7) 重置接口并扫描可用 Wi‑Fi ===”
sudo ip link set “$IF” down || true
sleep 1
sudo ip link set “$IF” up || true
sleep 1
echo “扫描(ifname=$IF)…”
sudo nmcli device wifi rescan ifname “$IF” || true
sudo nmcli device wifi list ifname “$IF” || true

echo
echo “=== 8) 清理旧同名连接并尝试连接 ===”
# 删除所有同名的 rtlwap 连接,避免冲突
for id in $(nmcli -t -f NAME,UUID connection show | awk -F: -v s=”$SSID” ‘$1 ~ “^”s {print $2}’); do
echo “删除旧连接 $id”
sudo nmcli connection delete “$id” || true
done

if [ -z “$PASSWORD” ]; then
echo “未提供密码,尝试无密码连接(若是有密码请以第二个参数提供)”
sudo nmcli device wifi connect “$SSID” ifname “$IF” || true
else
echo “尝试连接 SSID=’$SSID’(ifname=$IF)…”
sudo nmcli device wifi connect “$SSID” password “$PASSWORD” ifname “$IF” || true
fi

echo
echo “=== 9) 连接状态与最后日志摘要 ===”
nmcli device status
nmcli connection show –active || true
echo “—– dmesg 相关(尾部 200 行) —–”
sudo dmesg | tail -n 200
echo “如果连接失败,请把上面输出全贴过来(特别是 dmesg 与 nmcli device status)”

[Qt]QML地图-4

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtPositioning 5.15

Item {
id: root
width: 640
height: 480

property var baseCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property var centerCoordinate: { “latitude”: 36.50, “longitude”: 117.2 } // 中心坐标
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”
property var tilesCache: ({}) // 缓存瓦片对象

// 外部传入的旋转角度(单位:度)
property real markerAngle: 45

// 猴子标记
Image {
id: monkeyMarker
width: 30
height: 30
source: “qrc:/monkey.png”
z: 10
transformOrigin: Item.Center // 旋转中心在图片中点
rotation: root.markerAngle // 使用外部传入的角度
}

// 画红线
Canvas {
id: lineCanvas
anchors.fill: parent
z: 15
onPaint: {
var ctx = getContext(“2d”);
ctx.clearRect(0, 0, width, height);

ctx.lineWidth = 1;
ctx.strokeStyle = “red”;

var p1 = getPixelPos(root.baseCoordinate);
var p2 = getPixelPos(root.centerCoordinate);

ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}

Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000

Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
}
}

// 经纬度 → 瓦片坐标
function coordToPixel(lat, lon) {
var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);

var x = (lon + 180) / 360 * n;
var y = (1 – Math.log(Math.tan(lat * Math.PI / 180) +
1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n;
return { “x”: x, “y”: y };
}

// 转屏幕像素坐标
function getPixelPos(coord) {
var centerTile = coordToPixel(root.centerCoordinate.latitude, root.centerCoordinate.longitude);
var pointTile = coordToPixel(coord.latitude, coord.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

var px = (pointTile.x – startX) * root.tileSize + tileContainer.x;
var py = (pointTile.y – startY) * root.tileSize + tileContainer.y;
return { “x”: px, “y”: py };
}

// 更新猴子位置(不再计算角度)
function updateMarkerPosition() {
var center = root.centerCoordinate;
var centerTile = coordToPixel(center.latitude, center.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

// 计算中心点像素位置
var px = (centerTile.x – startX) * root.tileSize + tileContainer.x;
var py = (centerTile.y – startY) * root.tileSize + tileContainer.y;

monkeyMarker.x = px – monkeyMarker.width / 2;
monkeyMarker.y = py – monkeyMarker.height / 2;
}

// 更新瓦片
function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;

var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;

var centerTile = coordToPixel(center.latitude, center.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

var currentKeys = {};
for (var x = startX; x < startX + tilesX; x++) { for (var y = startY; y < startY + tilesY; y++) { var key = x + "_" + y; currentKeys[key] = true; var path = root.tileRoot + "/" + zoom + "/" + x + "/" + y + ".png"; if (!tilesCache[key]) { var tile = Qt.createQmlObject( 'import QtQuick 2.15; Image { width:' + root.tileSize + '; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }', tileContainer ); tilesCache[key] = tile; } var tileObj = tilesCache[key]; tileObj.x = (x - startX) * root.tileSize; tileObj.y = (y - startY) * root.tileSize; } } // 删除多余瓦片 for (var key in tilesCache) { if (!currentKeys[key]) { tilesCache[key].destroy(); delete tilesCache[key]; } } // 平移 tileContainer,使中心瓦片对齐 var offsetX = (centerTile.x - startX) * root.tileSize - flick.width / 2; var offsetY = (centerTile.y - startY) * root.tileSize - flick.height / 2; tileContainer.x = -offsetX; tileContainer.y = -offsetY; updateMarkerPosition(); lineCanvas.requestPaint(); // 重新画红线 } Component.onCompleted: updateVisibleTiles() }

[Python]检查瓦片连续性

import os
import argparse
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import time

def check_tile_continuity_practical(base_path, target_z_levels=None):
“””
检查瓦片文件的连续性(基于实际存在的X文件夹,检查Y的连续性包括边界)

Args:
base_path: 瓦片文件根目录
target_z_levels: 要检查的z层级列表,默认为[10, 12, 14, 16]

Returns:
dict: 包含检查结果的字典
“””
if target_z_levels is None:
target_z_levels = [10, 12, 14, 16]

results = {}

for z in target_z_levels:
z_path = Path(base_path) / str(z)
print(f”正在检查层级 z={z}…”)

if not z_path.exists():
print(f” 警告: 层级 {z} 的目录不存在: {z_path}”)
results[z] = {‘status’: ‘missing’, ‘message’: f’目录不存在: {z_path}’}
continue

# 获取所有x子目录
x_dirs = [d for d in z_path.iterdir() if d.is_dir() and d.name.isdigit()]

if not x_dirs:
print(f” 警告: 层级 {z} 下没有找到x子目录”)
results[z] = {‘status’: ’empty’, ‘message’: ‘没有x子目录’}
continue

x_dirs.sort(key=lambda x: int(x.name))
x_values = [int(x_dir.name) for x_dir in x_dirs]

print(f” 实际X范围: {min(x_values)}-{max(x_values)}, 共{len(x_values)}个目录”)

# 检测该层级的Y理论范围(基于所有X目录的Y文件)
y_global_range = detect_y_global_range(x_dirs)
if y_global_range:
print(f” 检测到Y全局范围: {y_global_range[0]}-{y_global_range[1]}”)
else:
print(f” 警告: 无法检测到Y全局范围”)
y_global_range = (0, 0)

y_continuity_issues = []
total_tiles = 0
missing_tiles = 0

# 检查每个x目录下的y文件连续性
for x_dir in x_dirs:
x_value = int(x_dir.name)

# 获取所有y.png文件
y_files = list(x_dir.glob(“*.png”))
y_values = []

for y_file in y_files:
if y_file.stem.isdigit():
y_values.append(int(y_file.stem))

if not y_values:
# 空目录 – 如果检测到全局范围,则整个范围都缺失
if y_global_range and y_global_range[0] != y_global_range[1]:
all_missing_y = list(range(y_global_range[0], y_global_range[1] + 1))
missing_tiles += len(all_missing_y)
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: all_missing_y,
‘y_range’: y_global_range,
‘expected_count’: y_global_range[1] – y_global_range[0] + 1,
‘actual_count’: 0,
‘note’: ‘空目录’
})
else:
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: [],
‘y_range’: (0, 0),
‘expected_count’: 0,
‘actual_count’: 0,
‘note’: ‘空目录’
})
continue

y_values.sort()

# 使用全局范围检查Y的连续性(包括边界)
if y_global_range and y_global_range[0] != y_global_range[1]:
y_min, y_max = y_global_range
y_missing = check_number_continuity_with_bounds(y_values, y_min, y_max)
expected_count = y_max – y_min + 1
else:
# 如果没有全局范围,使用该目录的实际范围
y_min, y_max = min(y_values), max(y_values)
y_missing = check_number_continuity(y_values)
expected_count = len(y_values) + len(y_missing)

total_tiles += len(y_values)

if y_missing:
missing_tiles += len(y_missing)
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: y_missing,
‘y_range’: (y_min, y_max),
‘expected_count’: expected_count,
‘actual_count’: len(y_values),
‘note’: ‘部分缺失’
})

# 汇总结果
results[z] = {
‘status’: ‘checked’,
‘actual_x_range’: (min(x_values), max(x_values)) if x_values else (0, 0),
‘y_global_range’: y_global_range,
‘x_count’: len(x_values),
‘x_missing’: [], # 不检查X的连续性,只基于实际存在的X目录
‘total_tiles’: total_tiles,
‘missing_tiles’: missing_tiles,
‘y_continuity_issues’: y_continuity_issues,
‘completeness_rate’: (total_tiles / (total_tiles + missing_tiles)) * 100 if (total_tiles + missing_tiles) > 0 else 0,
‘method’: ‘practical’
}

return results

def detect_y_global_range(x_dirs, sample_limit=30):
“””
检测该层级所有X目录的Y全局范围(不扩展边界)

Args:
x_dirs: X目录列表
sample_limit: 最大抽样目录数量

Returns:
tuple: (y_min, y_max) 或 None(如果无法检测)
“””
all_y_values = []

# 等间距抽样以提高覆盖率
sample_count = min(sample_limit, len(x_dirs))
if len(x_dirs) <= sample_count: sampled_dirs = x_dirs # 目录不多时全量检查 else: step = len(x_dirs) // sample_count sampled_dirs = x_dirs[::step] + [x_dirs[-1]] # 确保包含最后一个目录 print(f" 抽样检查 {len(sampled_dirs)} 个X目录的Y范围") for i, x_dir in enumerate(sampled_dirs): y_files = list(x_dir.glob("*.png")) y_values = [int(f.stem) for f in y_files if f.stem.isdigit()] if y_values: dir_min = min(y_values) dir_max = max(y_values) all_y_values.extend(y_values) if i < 3: # 显示前3个目录的范围作为参考 print(f" X={x_dir.name}: Y范围 {dir_min}-{dir_max}") if not all_y_values: return None # 直接使用检测到的最小最大值,不扩展边界 y_min = min(all_y_values) y_max = max(all_y_values) print(f" 最终Y全局范围: {y_min}-{y_max}") return (y_min, y_max) def check_number_continuity_with_bounds(numbers, expected_min, expected_max): """ 检查数字序列的连续性,包括边界检测 Args: numbers: 现有的数字列表 expected_min: 预期的最小值 expected_max: 预期的最大值 Returns: list: 缺失的数字列表(包括边界缺失) """ if not numbers: return list(range(expected_min, expected_max + 1)) existing_set = set(numbers) expected_set = set(range(expected_min, expected_max + 1)) missing = sorted(list(expected_set - existing_set)) return missing def check_number_continuity(numbers): """ 检查数字序列的连续性(基于实际范围) Args: numbers: 排序后的数字列表 Returns: list: 缺失的数字列表 """ if not numbers: return [] min_val = min(numbers) max_val = max(numbers) expected_set = set(range(min_val, max_val + 1)) actual_set = set(numbers) missing = sorted(list(expected_set - actual_set)) return missing def print_results(results): """打印检查结果""" print("\n" + "="*80) print("瓦片文件连续性检查报告(基于实际X目录,包含Y边界检测)") print("="*80) total_actual_tiles = 0 total_missing_tiles = 0 for z, data in results.items(): print(f"\n层级 z={z}:") if data['status'] != 'checked': print(f" ⚠️ {data['message']}") continue print(f" 实际X范围: {data['actual_x_range'][0]}-{data['actual_x_range'][1]}") if data['y_global_range']: print(f" Y全局范围: {data['y_global_range'][0]}-{data['y_global_range'][1]}") print(f" X目录数量: {data['x_count']}") print(f" 总瓦片数: {data['total_tiles']:,}") print(f" 缺失瓦片数: {data['missing_tiles']:,}") print(f" 完整度: {data['completeness_rate']:.2f}%") total_actual_tiles += data['total_tiles'] total_missing_tiles += data['missing_tiles'] if data['y_continuity_issues']: empty_dirs = sum(1 for issue in data['y_continuity_issues'] if issue.get('note') == '空目录') partial_dirs = sum(1 for issue in data['y_continuity_issues'] if issue.get('note') == '部分缺失') print(f" ⚠️ 发现 {len(data['y_continuity_issues'])} 个问题目录:") if empty_dirs > 0:
print(f” 空目录: {empty_dirs}个”)
if partial_dirs > 0:
print(f” Y不连续目录: {partial_dirs}个”)

# 显示部分缺失目录的详细信息
partial_issues = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘部分缺失’]
if partial_issues:
print(f”\n Y不连续目录详情:”)
for issue in partial_issues[:5]: # 显示前5个
boundary_missing = []
if issue[‘missing_y’]:
y_min, y_max = issue[‘y_range’]
boundary_missing = [y for y in issue[‘missing_y’] if y == y_min or y == y_max]

print(f” X={issue[‘x’]}: Y范围{issue[‘y_range’]}, 缺失{len(issue[‘missing_y’])}个文件”)
if boundary_missing:
print(f” 边界缺失: {boundary_missing}”)
if len(issue[‘missing_y’]) <= 10: print(f" 所有缺失Y: {issue['missing_y']}") else: print(f" 缺失Y示例: {issue['missing_y'][:5]} ... {issue['missing_y'][-5:]}") if len(partial_issues) > 5:
print(f” … 还有 {len(partial_issues) – 5} 个目录”)
else:
print(f” ✅ 所有Y轴文件连续完整”)

# 汇总统计
print(“\n” + “=”*80)
print(“汇总统计”)
print(“=”*80)
total_files = total_actual_tiles + total_missing_tiles
overall_completeness = (total_actual_tiles / total_files) * 100 if total_files > 0 else 0
print(f”总瓦片数: {total_files:,}”)
print(f”实际瓦片数: {total_actual_tiles:,}”)
print(f”缺失瓦片数: {total_missing_tiles:,}”)
print(f”总体完整度: {overall_completeness:.2f}%”)

def save_results_to_txt(results, output_file=”tile_check_results.txt”):
“””将详细检查结果保存到txt文件”””
with open(output_file, ‘w’, encoding=’utf-8′) as f:
f.write(“=” * 80 + “\n”)
f.write(“瓦片文件连续性检查报告(基于实际X目录,包含Y边界检测)\n”)
f.write(“=” * 80 + “\n”)
f.write(f”生成时间: {time.strftime(‘%Y-%m-%d %H:%M:%S’)}\n”)
f.write(“说明: 基于实际存在的X目录检查Y文件的连续性,包含边界检测\n\n”)

total_actual_tiles = 0
total_missing_tiles = 0
checked_levels = 0

for z, data in results.items():
f.write(f”\n层级 z={z}:\n”)
f.write(“-” * 50 + “\n”)

if data[‘status’] != ‘checked’:
f.write(f”状态: {data[‘message’]}\n”)
continue

checked_levels += 1
f.write(f”实际X范围: {data[‘actual_x_range’][0]}-{data[‘actual_x_range’][1]}\n”)
if data[‘y_global_range’]:
f.write(f”Y全局范围: {data[‘y_global_range’][0]}-{data[‘y_global_range’][1]}\n”)
f.write(f”X目录数量: {data[‘x_count’]}\n”)
f.write(f”总瓦片数: {data[‘total_tiles’]:,}\n”)
f.write(f”缺失瓦片数: {data[‘missing_tiles’]:,}\n”)
f.write(f”完整度: {data[‘completeness_rate’]:.2f}%\n”)

total_actual_tiles += data[‘total_tiles’]
total_missing_tiles += data[‘missing_tiles’]

# Y轴连续性问题
if data[‘y_continuity_issues’]:
empty_dirs = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘空目录’]
partial_dirs = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘部分缺失’]

f.write(f”\nY轴连续性问题:\n”)
f.write(f” 空目录: {len(empty_dirs)}个\n”)
f.write(f” Y不连续目录: {len(partial_dirs)}个\n”)

# 详细列出空目录
if empty_dirs:
f.write(f”\n 空目录列表:\n”)
for issue in empty_dirs:
f.write(f” X={issue[‘x’]}\n”)

# 详细列出部分缺失目录
if partial_dirs:
f.write(f”\n Y不连续目录详情:\n”)
for issue in partial_dirs:
# 检查边界缺失
boundary_missing = []
if issue[‘missing_y’]:
y_min, y_max = issue[‘y_range’]
boundary_missing = [y for y in issue[‘missing_y’] if y == y_min or y == y_max]

f.write(f” X={issue[‘x’]}: Y范围{issue[‘y_range’]}, 应有{issue[‘expected_count’]}个, 实有{issue[‘actual_count’]}个, 缺失{len(issue[‘missing_y’])}个\n”)
if boundary_missing:
f.write(f” 边界缺失: {boundary_missing}\n”)
if len(issue[‘missing_y’]) <= 20: f.write(f" 所有缺失Y: {issue['missing_y']}\n") else: f.write(f" 缺失Y: {issue['missing_y'][:10]} ... {issue['missing_y'][-10:]} (共{len(issue['missing_y'])}个)\n") else: f.write("Y轴文件: 连续完整 ✓\n") f.write("\n") # 汇总统计 f.write("\n" + "=" * 80 + "\n") f.write("汇总统计\n") f.write("=" * 80 + "\n") f.write(f"检查层级数量: {checked_levels}\n") total_files = total_actual_tiles + total_missing_tiles f.write(f"总瓦片数: {total_files:,}\n") f.write(f"实际瓦片数: {total_actual_tiles:,}\n") f.write(f"缺失瓦片数: {total_missing_tiles:,}\n") if total_files > 0:
overall_completeness = (total_actual_tiles / total_files) * 100
f.write(f”总体完整度: {overall_completeness:.2f}%\n”)

print(f”详细报告已保存到: {output_file}”)

def save_missing_list(results, output_file=”missing_tiles.txt”):
“””保存缺失瓦片列表到文件”””
with open(output_file, ‘w’, encoding=’utf-8′) as f:
f.write(“# 缺失瓦片列表(基于实际X目录,包含Y边界检测)\n”)
f.write(“# 格式: z/x/y\n”)
f.write(“# 生成时间: {}\n”.format(time.strftime(“%Y-%m-%d %H:%M:%S”)))
f.write(“# 说明: 列出实际存在的X目录中缺失的Y文件,包括边界缺失\n”)
f.write(“=”*50 + “\n”)

total_missing = 0
for z, data in results.items():
if data[‘status’] == ‘checked’ and data[‘y_continuity_issues’]:
f.write(f”\n# 层级 z={z}\n”)

# 记录所有缺失的Y文件
for issue in data[‘y_continuity_issues’]:
if issue.get(‘note’) in [‘部分缺失’, ‘空目录’]:
for missing_y in issue[‘missing_y’]:
f.write(f”{z}/{issue[‘x’]}/{missing_y}\n”)
total_missing += 1

f.write(f”\n# 总共缺失 {total_missing} 个瓦片文件\n”)

print(f”缺失列表已保存到: {output_file}”)

def main():
parser = argparse.ArgumentParser(description=’检查瓦片文件连续性(基于实际X目录,包含Y边界检测)’)
parser.add_argument(‘path’, help=’瓦片文件根目录路径’)
parser.add_argument(‘-z’, ‘–zoom-levels’, nargs=’+’, type=int,
default=[10, 12, 14, 16], help=’要检查的z层级,默认为10 12 14 16′)
parser.add_argument(‘-o’, ‘–output’, default=’tile_check_results.txt’,
help=’输出结果文件名’)

args = parser.parse_args()

if not os.path.exists(args.path):
print(f”错误: 路径不存在: {args.path}”)
return

print(f”检查路径: {args.path}”)
print(f”检查层级: {args.zoom_levels}”)
print(“检查方法: 基于实际X目录检查Y连续性(包含边界检测)”)
print(“开始检查…” + “\n”)

start_time = time.time()

results = check_tile_continuity_practical(args.path, args.zoom_levels)

end_time = time.time()

print_results(results)

# 保存详细报告
save_results_to_txt(results, args.output)

# 保存缺失列表
missing_list_file = args.output.replace(‘.txt’, ‘_missing_list.txt’)
save_missing_list(results, missing_list_file)

print(f”\n检查完成! 耗时: {end_time – start_time:.2f}秒”)

if __name__ == “__main__”:
base_path = r”E:\Map\Layer_2025-09-27_231600\Layer”

# 使用改进的方法检查
print(“=== 基于实际X目录检查Y连续性(包含边界检测)===”)
results = check_tile_continuity_practical(base_path)
print_results(results)
save_results_to_txt(results, “improved_check_results.txt”)
save_missing_list(results, “improved_missing_list.txt”)

[Qt]QML文件-2

void MainWindow::updateCoordinates(double lat, double lon)
{
QQuickWidget *quickWidget = ui->quickWidget;
if (!quickWidget) return;

QQuickItem *rootObject = quickWidget->rootObject();
if (!rootObject) return;

QVariantMap coord;
coord[“latitude”] = lat;
coord[“longitude”] = lon;
rootObject->setProperty(“centerCoordinate”, coord);

QMetaObject::invokeMethod(rootObject, “updateVisibleTiles”);
}

//QML

import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
id: root
width: 640
height: 480

property var centerCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”

Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000

Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
property var tilesCache: ({}) // 缓存瓦片对象
}

Rectangle {
id: redDot
width: 12
height: 12
color: “red”
radius: 6
anchors.centerIn: parent
}

onContentXChanged: updateVisibleTiles()
onContentYChanged: updateVisibleTiles()
}

function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;

var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;

// 中心瓦片浮点坐标
var centerTileXf = (center.longitude + 180) / 360 * n;
var centerTileYf = (1 – Math.log(Math.tan(center.latitude*Math.PI/180) + 1/Math.cos(center.latitude*Math.PI/180)) / Math.PI) / 2 * n;

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTileXf – tilesX/2);
var startY = Math.floor(centerTileYf – tilesY/2);

// 记录当前视野瓦片 key
var currentKeys = {};
for (var x = startX; x < startX + tilesX; x++) { for (var y = startY; y < startY + tilesY; y++) { var key = x + "_" + y; currentKeys[key] = true; var path = root.tileRoot + "/" + zoom + "/" + x + "/" + y + ".png"; if (!tileContainer.tilesCache[key]) { var tile = Qt.createQmlObject( 'import QtQuick 2.15; Image { width:' + root.tileSize + '; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }', tileContainer ); tileContainer.tilesCache[key] = tile; } // 更新位置 var tileObj = tileContainer.tilesCache[key]; tileObj.x = (x - startX) * root.tileSize; tileObj.y = (y - startY) * root.tileSize; } } // 删除超出视野的瓦片 for (var key in tileContainer.tilesCache) { if (!currentKeys[key]) { tileContainer.tilesCache[key].destroy(); delete tileContainer.tilesCache[key]; } } // 移动 tileContainer,使中心瓦片在 Flickable 中心 var offsetX = (centerTileXf - startX) * root.tileSize - flick.width/2; var offsetY = (centerTileYf - startY) * root.tileSize - flick.height/2; tileContainer.x = -offsetX; tileContainer.y = -offsetY; // 红点始终在视口中心 redDot.x = flick.width / 2 - redDot.width/2; redDot.y = flick.height / 2 - redDot.height/2; } Component.onCompleted: updateVisibleTiles() }

[Linux]开机自动挂载TF卡

1.确保已安装 exFAT 支持
sudo apt update
sudo apt install exfat-fuse exfatprogs

2.获取 TF 卡信息,获取UUID等
sudo blkid /dev/mmcblk1p1
比如返回 /dev/mmcblk1p1: UUID=”3931-6530″ BLOCK_SIZE=”512″ TYPE=”exfat”

3.编辑 fstab
sudo vi /etc/fstab

4.在文件末尾添加以下内容
# TF Card Auto Mount (exFAT)
UUID=3931-6530 /mnt/tfcard exfat defaults,uid=1000,gid=1000,umask=000,noatime 0 0

[Qt]QML地图文件

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15

Item {
width: 800
height: 600

Plugin {
id: mapPlugin
name: “osm” // 使用 OpenStreetMap 插件
}

// 坐标属性,可通过 C++ 更新
property var coordinateA: QtPositioning.coordinate(36.01, 116.11)
property var coordinateB: QtPositioning.coordinate(37.0, 117.0)
property real zoomLevelVal: 10

Map {
anchors.fill: parent
plugin: mapPlugin
center: coordinateA // 默认以 A 为中心
zoomLevel: zoomLevelVal // 使用绑定的 zoomLevel 属性

// 显示从 A 到 B 的路径
MapPolyline {
line.width: 1
line.color: “red”
path: [coordinateA, coordinateB]
}

MapQuickItem {
coordinate: coordinateA // A 坐标点
sourceItem: Image {
width: 30
height: 30
source: “qrc:/monkey.png” // 替换为您的箭头图片
//transformOrigin:Item.Center // 以图片中心为基准点
rotation: Math.atan2(-coordinateB.latitude + coordinateA.latitude,
-coordinateB.longitude + coordinateA.longitude) * 180 / Math.PI
}
}
}
}

[OrangePi]生成镜像

1.下载 git clone https://gitee.com/orangepi-xunlong/opi-bkimg.git
2.进入当前目录下的 opi-bkimg
3.sudo ./opi-bkimg
4.生成img文件在/mnt文件夹

[STM32G4] ADC转换

LL_ADC_EnableIT_EOC(ADC1);
LL_ADC_StartCalibration(ADC1,LL_ADC_SINGLE_ENDED);
while(LL_ADC_IsCalibrationOnGoing(ADC1));
LL_ADC_Enable(ADC1);
LL_ADC_REG_StartConversion(ADC1);

————————

void ADC1_read(uint16_t *pusbuf)
{
LL_ADC_REG_StartConversion(ADC1);
while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
pusbuf[0] = LL_ADC_REG_ReadConversionData10(ADC1);
while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
pusbuf[1] = LL_ADC_REG_ReadConversionData10(ADC1);
}