Helmerttransformation fürs erste fertig, bis GNSS-Daten vorliegen

This commit is contained in:
2025-12-09 14:30:28 +01:00
parent 74d467e957
commit 859cbbade9
8 changed files with 2486 additions and 1301 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -6,15 +6,17 @@
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-12-08T16:59:58.933024Z",
"start_time": "2025-12-08T16:59:58.578335Z"
"end_time": "2025-12-09T12:25:40.573167Z",
"start_time": "2025-12-09T12:25:40.219840Z"
}
},
"source": [
"# Hier werden alle verwendeten Pythonmodule importiert\n",
"import Datenbank\n",
"import Import\n",
"import importlib"
"import importlib\n",
"import Koordinatentransformationen\n",
"import sqlite3"
],
"outputs": [],
"execution_count": 1
@@ -22,8 +24,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-08T17:00:01.134916Z",
"start_time": "2025-12-08T17:00:01.110846Z"
"end_time": "2025-12-09T12:25:40.603148Z",
"start_time": "2025-12-09T12:25:40.579387Z"
}
},
"cell_type": "code",
@@ -44,14 +46,14 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-08T17:00:02.698814Z",
"start_time": "2025-12-08T17:00:02.678346Z"
"end_time": "2025-12-09T12:25:40.629729Z",
"start_time": "2025-12-09T12:25:40.608515Z"
}
},
"cell_type": "code",
"source": [
"# Import der Koordinatendatei(en) vom Tachymeter\n",
"pfad_datei = r\"Daten\\campusnetz_koordinaten_25_11.csv\"\n",
"pfad_datei = r\"Daten\\campsnetz_final_koordinaten.csv\"\n",
"imp.import_koordinaten_lh_tachymeter(pfad_datei)"
],
"id": "d3bce3991a8962dc",
@@ -69,8 +71,8 @@
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-08T17:00:13.157273Z",
"start_time": "2025-12-08T17:00:13.147245Z"
"end_time": "2025-12-09T12:25:40.645668Z",
"start_time": "2025-12-09T12:25:40.635523Z"
}
},
"cell_type": "code",
@@ -86,170 +88,676 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[{'10009': Matrix([\n",
"{'10009': Matrix([\n",
"[1000.0],\n",
"[2000.0],\n",
"[ 100.0]])}, {'10006': Matrix([\n",
"[ 100.0]]), '10006': Matrix([\n",
"[ 1000.0],\n",
"[2032.6863],\n",
"[ 99.5825]])}, {'10010': Matrix([\n",
"[ 99.5825]]), '10010': Matrix([\n",
"[1011.8143],\n",
"[1973.3252],\n",
"[ 99.9259]])}, {'10018': Matrix([\n",
"[ 99.9259]]), '10018': Matrix([\n",
"[1008.5759],\n",
"[ 1942.762],\n",
"[ 100.2553]])}, {'10008': Matrix([\n",
"[ 100.2553]]), '10008': Matrix([\n",
"[979.7022],\n",
"[1991.401],\n",
"[ 99.732]])}, {'10005': Matrix([\n",
"[ 99.732]]), '10005': Matrix([\n",
"[ 966.5154],\n",
"[2014.6496],\n",
"[ 99.72]])}, {'10003': Matrix([\n",
"[ 99.72]]), '10003': Matrix([\n",
"[ 908.4312],\n",
"[1996.1248],\n",
"[ 99.7403]])}, {'10004': Matrix([\n",
"[ 99.7403]]), '10004': Matrix([\n",
"[ 954.1536],\n",
"[2021.6822],\n",
"[ 99.4916]])}, {'10007': Matrix([\n",
"[ 99.4916]]), '10007': Matrix([\n",
"[ 921.7481],\n",
"[1973.6201],\n",
"[ 99.9176]])}, {'10001': Matrix([\n",
"[ 99.9176]]), '10001': Matrix([\n",
"[ 833.9439],\n",
"[1978.3737],\n",
"[ 99.8946]])}, {'10002': Matrix([\n",
"[ 99.8946]]), '10002': Matrix([\n",
"[ 875.9684],\n",
"[1998.5174],\n",
"[ 99.5867]])}, {'100016': Matrix([\n",
"[ 99.5867]]), '100016': Matrix([\n",
"[ 928.2783],\n",
"[1944.0082],\n",
"[ 100.0459]])}, {'10011': Matrix([\n",
"[ 100.0459]]), '10011': Matrix([\n",
"[ 908.4308],\n",
"[1996.1277],\n",
"[ 99.7822]])}, {'10011a': Matrix([\n",
"[ 99.7822]]), '10011a': Matrix([\n",
"[844.9567],\n",
"[1891.157],\n",
"[ 99.8117]])}, {'10026': Matrix([\n",
"[ 99.8117]]), '10026': Matrix([\n",
"[1020.0059],\n",
"[1913.8703],\n",
"[ 100.3059]])}, {'10027': Matrix([\n",
"[ 100.3059]]), '10027': Matrix([\n",
"[1016.9451],\n",
"[1866.2914],\n",
"[ 100.3251]])}, {'10043': Matrix([\n",
"[ 100.3251]]), '10043': Matrix([\n",
"[1031.2077],\n",
"[1822.4739],\n",
"[ 100.3035]])}, {'10044': Matrix([\n",
"[ 100.3035]]), '10044': Matrix([\n",
"[ 1025.976],\n",
"[1782.4835],\n",
"[ 100.5461]])}, {'10021': Matrix([\n",
"[ 100.5461]]), '10021': Matrix([\n",
"[ 992.7607],\n",
"[1904.8854],\n",
"[ 100.3533]])}, {'10020': Matrix([\n",
"[ 100.3533]]), '10020': Matrix([\n",
"[ 984.6187],\n",
"[1903.3601],\n",
"[ 100.3423]])}, {'10024': Matrix([\n",
"[ 100.3423]]), '10024': Matrix([\n",
"[ 997.4831],\n",
"[1881.7862],\n",
"[ 100.3032]])}, {'10025': Matrix([\n",
"[ 100.3032]]), '10025': Matrix([\n",
"[996.3241],\n",
"[1866.844],\n",
"[100.4102]])}, {'10022': Matrix([\n",
"[100.4102]]), '10022': Matrix([\n",
"[990.0679],\n",
"[1896.536],\n",
"[100.2194]])}, {'10023': Matrix([\n",
"[100.2194]]), '10023': Matrix([\n",
"[ 987.3223],\n",
"[1889.8762],\n",
"[ 100.343]])}, {'10019': Matrix([\n",
"[ 100.343]]), '10019': Matrix([\n",
"[ 962.6387],\n",
"[1902.3565],\n",
"[ 99.9772]])}, {'10033': Matrix([\n",
"[ 99.9772]]), '10033': Matrix([\n",
"[ 964.0191],\n",
"[1860.8023],\n",
"[ 99.8551]])}, {'10017': Matrix([\n",
"[ 99.8551]]), '10017': Matrix([\n",
"[ 931.6761],\n",
"[1900.9945],\n",
"[ 99.9572]])}, {'10052': Matrix([\n",
"[ 99.9572]]), '10052': Matrix([\n",
"[ 1037.875],\n",
"[1757.2999],\n",
"[ 100.2737]])}, {'10042': Matrix([\n",
"[ 100.2737]]), '10042': Matrix([\n",
"[1017.3489],\n",
"[1803.0742],\n",
"[ 100.3441]])}, {'10053': Matrix([\n",
"[ 100.3441]]), '10053': Matrix([\n",
"[1033.3758],\n",
"[1723.4258],\n",
"[ 100.2774]])}, {'10037': Matrix([\n",
"[ 100.2774]]), '10037': Matrix([\n",
"[ 966.2253],\n",
"[1774.2051],\n",
"[ 99.9957]])}, {'10040': Matrix([\n",
"[ 99.9957]]), '10040': Matrix([\n",
"[ 990.8832],\n",
"[1780.9678],\n",
"[ 100.1677]])}, {'10041': Matrix([\n",
"[ 100.1677]]), '10041': Matrix([\n",
"[993.2769],\n",
"[1812.031],\n",
"[100.4749]])}, {'10038': Matrix([\n",
"[100.4749]]), '10038': Matrix([\n",
"[ 958.1899],\n",
"[1804.7135],\n",
"[ 100.0741]])}, {'10051': Matrix([\n",
"[ 100.0741]]), '10051': Matrix([\n",
"[1008.9811],\n",
"[1750.1838],\n",
"[ 100.288]])}, {'10036': Matrix([\n",
"[ 100.288]]), '10036': Matrix([\n",
"[ 948.6403],\n",
"[1763.5807],\n",
"[ 100.0063]])}, {'10035': Matrix([\n",
"[ 100.0063]]), '10035': Matrix([\n",
"[ 910.1265],\n",
"[1768.0099],\n",
"[ 100.0781]])}, {'10039': Matrix([\n",
"[ 100.0781]]), '10039': Matrix([\n",
"[ 960.3884],\n",
"[1820.0543],\n",
"[ 100.0983]])}, {'10059': Matrix([\n",
"[ 100.0983]]), '10059': Matrix([\n",
"[1049.2587],\n",
"[1662.5451],\n",
"[ 100.0148]])}, {'10050': Matrix([\n",
"[ 100.0148]]), '10050': Matrix([\n",
"[1010.0246],\n",
"[1726.2445],\n",
"[ 100.1493]])}, {'10049': Matrix([\n",
"[ 100.1493]]), '10049': Matrix([\n",
"[ 984.7667],\n",
"[1714.5709],\n",
"[ 100.0101]])}, {'100': Matrix([\n",
"[ 100.0101]]), '100': Matrix([\n",
"[ 957.3912],\n",
"[1716.2864],\n",
"[ 99.7777]])}, {'10013': Matrix([\n",
"[ 99.7777]]), '10013': Matrix([\n",
"[900.9076],\n",
"[1902.873],\n",
"[ 99.7911]])}, {'10028': Matrix([\n",
"[ 99.7911]]), '10028': Matrix([\n",
"[ 853.9608],\n",
"[1815.7417],\n",
"[ 99.7793]])}, {'10012': Matrix([\n",
"[ 99.7793]]), '10012': Matrix([\n",
"[ 895.3032],\n",
"[1924.1523],\n",
"[ 99.8758]])}, {'10014': Matrix([\n",
"[ 99.8758]]), '10014': Matrix([\n",
"[ 913.9706],\n",
"[1918.7731],\n",
"[ 99.8872]])}, {'10031': Matrix([\n",
"[ 99.8872]]), '10031': Matrix([\n",
"[ 937.1557],\n",
"[1855.2805],\n",
"[ 99.8479]])}, {'10015': Matrix([\n",
"[ 99.8479]]), '10015': Matrix([\n",
"[ 912.5157],\n",
"[1937.6471],\n",
"[ 99.9834]])}, {'10032': Matrix([\n",
"[ 99.9834]]), '10032': Matrix([\n",
"[ 954.6732],\n",
"[1845.9356],\n",
"[ 99.724]])}, {'10030': Matrix([\n",
"[ 99.724]]), '10030': Matrix([\n",
"[ 908.4749],\n",
"[1828.8008],\n",
"[ 99.5581]])}, {'10029': Matrix([\n",
"[ 99.5581]]), '10029': Matrix([\n",
"[ 909.3343],\n",
"[1814.8767],\n",
"[ 99.5486]])}, {'10034': Matrix([\n",
"[ 99.5486]]), '10034': Matrix([\n",
"[ 860.2357],\n",
"[1758.9282],\n",
"[ 99.737]])}, {'10045': Matrix([\n",
"[ 99.737]]), '10045': Matrix([\n",
"[867.2324],\n",
"[1705.063],\n",
"[ 99.7214]])}]\n"
"[ 99.7214]]), '10049a': Matrix([\n",
"[ 985.2561],\n",
"[1715.2109],\n",
"[ 99.9965]]), '10048': Matrix([\n",
"[ 957.3889],\n",
"[1716.2949],\n",
"[ 99.7212]]), '10047': Matrix([\n",
"[ 929.5334],\n",
"[1712.6429],\n",
"[ 99.6076]]), '10046': Matrix([\n",
"[ 910.663],\n",
"[1716.0969],\n",
"[ 99.5459]]), '10057': Matrix([\n",
"[969.6876],\n",
"[1655.597],\n",
"[ 99.7039]]), '10055': Matrix([\n",
"[ 922.4731],\n",
"[1647.7452],\n",
"[ 99.4658]]), '10054': Matrix([\n",
"[ 860.4481],\n",
"[1636.6722],\n",
"[ 99.7093]]), '10058': Matrix([\n",
"[1013.2592],\n",
"[1646.6356],\n",
"[ 99.8513]]), '10056': Matrix([\n",
"[ 939.9763],\n",
"[1636.4179],\n",
"[ 99.4027]])}\n"
]
}
],
"execution_count": 4
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-09T12:25:40.666696Z",
"start_time": "2025-12-09T12:25:40.654193Z"
}
},
"cell_type": "code",
"source": [
"importlib.reload(Datenbank)\n",
"db_zugriff = Datenbank.Datenbankzugriff(pfad_datenbank)\n",
"# Transformationen in ETRS89 / DREF91 Realisierung 2025\n",
"print(db_zugriff.get_koordinaten(\"naeherung_us\"))"
],
"id": "3989b7b41874c16a",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{}\n"
]
}
],
"execution_count": 5
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-09T12:25:40.711147Z",
"start_time": "2025-12-09T12:25:40.686969Z"
}
},
"cell_type": "code",
"source": [
"# ToDo: Sobald GNSS vorliegend Koordinaten im ETRS89 / DREF 91 (2025) daraus berechnen!\n",
"liste_koordinaten_naeherung_us_alt = {\n",
" 10001: (3794874.98408291, 546741.751930012, 5079995.3838),\n",
" 10002: (3794842.53340714, 546726.907150697, 5080039.8778),\n",
" 10008: (3794757.41294192, 546742.822339098, 5080107.3198),\n",
" 10012: (3794827.11937161, 546801.412652168, 5080028.5852),\n",
" 10026: (3794727.06042449, 546823.571170112, 5080134.2029),\n",
" 10028: (3794862.91900719, 546904.943464041, 5079920.8994),\n",
" 10037: (3794774.14751515, 546955.423068316, 5079960.9426),\n",
" 10044: (3794725.78597473, 546954.557211544, 5080009.9234),\n",
" 10054: (3794852.07416848, 547094.399826613, 5079715.1737),\n",
" 10059: (3794710.34348443, 547075.630380075, 5080119.6491),\n",
"}\n",
"\n",
"liste_koordinaten_naeherung_us_V2 = {\n",
" 10001: (3794874.984, 546741.752, 5080029.990),\n",
" 10002: (3794842.533, 546726.907, 5080071.133),\n",
" 10008: (3794757.413, 546742.822, 5080135.400),\n",
" 10012: (3794827.119, 546801.413, 5080065.404),\n",
" 10026: (3794727.060, 546823.571, 5080179.951),\n",
" 10028: (3794862.919, 546904.943, 5079963.214),\n",
" 10037: (3794774.148, 546955.423, 5080040.520),\n",
" 10044: (3794725.786, 546954.557, 5080084.411),\n",
" 10054: (3794852.074, 547094.400, 5079771.845),\n",
" 10059: (3794710.343, 547075.630, 5080153.653),\n",
"}\n",
"\n",
"liste_koordinaten_naeherung_us = {\n",
" 10001: (3794874.984, 546741.752, 5080029.990),\n",
" 10002: (3794842.533, 546726.907, 5080071.133),\n",
" 10037: (3794774.148, 546955.423, 5080040.520),\n",
" 10044: (3794725.786, 546954.557, 5080084.411),\n",
"}\n",
"\n",
"\n",
"con = sqlite3.connect(pfad_datenbank)\n",
"cursor = con.cursor()\n",
"sql = \"\"\"\n",
"UPDATE Netzpunkte\n",
"SET naeherungx_us = ?, naeherungy_us = ?, naeherungz_us = ?\n",
"WHERE punktnummer = ?\n",
"\"\"\"\n",
"for punktnummer, (x, y, z) in liste_koordinaten_naeherung_us.items():\n",
" cursor.execute(sql, (x, y, z, punktnummer))\n",
"con.commit()\n",
"cursor.close()\n",
"con.close()"
],
"id": "f64d9c01318b40f1",
"outputs": [],
"execution_count": 6
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-09T12:38:18.913365Z",
"start_time": "2025-12-09T12:38:17.077549Z"
}
},
"cell_type": "code",
"source": [
"# ToDo: Sobald GNSS-Daten vorliegen und die Berechnungen richtig sind, aufräumen!!!\n",
"\n",
"importlib.reload(Koordinatentransformationen)\n",
"trafos = Koordinatentransformationen.Transformationen(pfad_datenbank)\n",
"\n",
"\n",
"import numpy as np\n",
"\n",
"import itertools\n",
"import numpy as np\n",
"import sympy as sp\n",
"\n",
"db = Datenbank.Datenbankzugriff(pfad_datenbank)\n",
"dict_ausgangssystem = db.get_koordinaten(\"naeherung_lh\", \"Dict\")\n",
"dict_zielsystem = db.get_koordinaten(\"naeherung_us\", \"Dict\")\n",
"\n",
"gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))\n",
"anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)\n",
"\n",
"liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]\n",
"liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]\n",
"\n",
"def dist(a, b):\n",
" return float((a - b).norm())\n",
"\n",
"print(\"d(p2,p1)=\", dist(liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[0]))\n",
"print(\"d(P2,P1)=\", dist(liste_punkte_zielsystem[1], liste_punkte_zielsystem[0]))\n",
"print(\"m0 ~\", dist(liste_punkte_zielsystem[1], liste_punkte_zielsystem[0]) /\n",
" dist(liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[0]))\n",
"\n",
"\n",
"def dist(a, b):\n",
" return float((a - b).norm())\n",
"\n",
"ratios = []\n",
"pairs = list(itertools.combinations(range(len(liste_punkte_ausgangssystem)), 2))\n",
"\n",
"for i, j in pairs:\n",
" d_loc = dist(liste_punkte_ausgangssystem[i], liste_punkte_ausgangssystem[j])\n",
" d_ecef = dist(liste_punkte_zielsystem[i], liste_punkte_zielsystem[j])\n",
" if d_loc > 1e-6:\n",
" ratios.append(d_ecef / d_loc)\n",
"\n",
"print(\"Anzahl Ratios:\", len(ratios))\n",
"print(\"min/mean/max:\", min(ratios), sum(ratios)/len(ratios), max(ratios))\n",
"print(\"std:\", float(np.std(ratios)))\n",
"\n",
"S_loc = sum(liste_punkte_ausgangssystem, sp.Matrix([0,0,0])) / anzahl_gemeinsame_punkte\n",
"S_ecef = sum(liste_punkte_zielsystem, sp.Matrix([0,0,0])) / anzahl_gemeinsame_punkte\n",
"\n",
"print(\"S_loc:\", S_loc)\n",
"print(\"S_ecef:\", S_ecef)\n",
"print(\"Delta:\", (S_ecef - S_loc).evalf(6))\n",
"\n",
"\n",
"def dist(a, b):\n",
" return float((a - b).norm())\n",
"\n",
"n = len(liste_punkte_ausgangssystem)\n",
"\n",
"scores = []\n",
"for i in range(n):\n",
" d_loc = []\n",
" d_ecef = []\n",
" for j in range(n):\n",
" if i == j:\n",
" continue\n",
" d_loc.append(dist(liste_punkte_ausgangssystem[i], liste_punkte_ausgangssystem[j]))\n",
" d_ecef.append(dist(liste_punkte_zielsystem[i], liste_punkte_zielsystem[j]))\n",
"\n",
" d_loc = np.array(d_loc)\n",
" d_ecef = np.array(d_ecef)\n",
"\n",
" # Verhältnisvektor; robust gegen Nullschutz\n",
" r = d_ecef / np.where(d_loc == 0, np.nan, d_loc)\n",
"\n",
" # Streuung der Ratios für Punkt i\n",
" score = np.nanstd(r)\n",
" scores.append(score)\n",
"\n",
"for pn, sc in sorted(zip(gemeinsame_punktnummern, scores), key=lambda x: -x[1]):\n",
" print(pn, round(sc, 4))\n",
"\n",
"\n",
"\n",
"transformationsparameter = trafos.Helmerttransformation_Euler_Transformationsparameter_berechne()"
],
"id": "21d60465e432c649",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"d(p2,p1)= 46.60388451996242\n",
"d(P2,P1)= 54.462720048072995\n",
"m0 ~ 1.1686304823956102\n",
"Anzahl Ratios: 6\n",
"min/mean/max: 0.9679784506116116 1.0266943302085056 1.1686304823956102\n",
"std: 0.07473161831852519\n",
"S_loc: Matrix([[925.528400000000], [1883.39492500000], [100.005775000000]])\n",
"S_ecef: Matrix([[3794804.36275000], [546844.659750000], [5080056.51350000]])\n",
"Delta: Matrix([[3.79388e+6], [544961.], [5.07996e+6]])\n",
"10001 0.0936\n",
"10002 0.0873\n",
"10044 0.0527\n",
"10037 0.0477\n",
"Anzahl gemeinsame Punkte: 4\n",
"\n",
"Erste Zielpunkte:\n",
"10001 [3794874.984, 546741.752, 5080029.99]\n",
"10002 [3794842.533, 546726.907, 5080071.133]\n",
"10037 [3794774.148, 546955.423, 5080040.52]\n",
"10044 [3794725.786, 546954.557, 5080084.411]\n",
"\n",
"Erste Ausgangspunkte:\n",
"10001 [833.9439, 1978.3737, 99.8946]\n",
"10002 [875.9684, 1998.5174, 99.5867]\n",
"10037 [966.2253, 1774.2051, 99.9957]\n",
"10044 [1025.976, 1782.4835, 100.5461]\n",
"min/mean/max: 0.9679784506116116 1.0266943302085056 1.1686304823956102\n",
"R ist Orthonormal!\n",
"Iteration Nr.1 abgeschlossen\n",
"Matrix([[-85.7], [-61.5], [188.], [-0.246], [-0.821], [0.00489], [0.406]])\n",
"Iteration Nr.2 abgeschlossen\n",
"Matrix([[241.], [-94.3], [-151.], [0.191], [-0.153], [-0.109], [0.120]])\n",
"Iteration Nr.3 abgeschlossen\n",
"Matrix([[5.71], [5.03], [0.723], [0.00670], [0.0401], [0.0180], [-0.0355]])\n",
"Iteration Nr.4 abgeschlossen\n",
"Matrix([[-2.83], [-1.48], [-2.88], [0.000657], [-0.00186], [0.00135], [0.00102]])\n",
"Iteration Nr.5 abgeschlossen\n",
"Matrix([[0.441], [0.196], [0.417], [6.90e-8], [0.000310], [-0.000257], [-0.000169]])\n",
"Iteration Nr.6 abgeschlossen\n",
"Matrix([[-0.0781], [-0.0348], [-0.0729], [1.91e-9], [-5.48e-5], [4.49e-5], [3.00e-5]])\n",
"Iteration Nr.7 abgeschlossen\n",
"Matrix([[0.0137], [0.00611], [0.0128], [5.92e-11], [9.61e-6], [-7.89e-6], [-5.25e-6]])\n",
"Iteration Nr.8 abgeschlossen\n",
"Matrix([[-0.00241], [-0.00107], [-0.00225], [1.35e-12], [-1.69e-6], [1.39e-6], [9.23e-7]])\n",
"Iteration Nr.9 abgeschlossen\n",
"Matrix([[0.000423], [0.000188], [0.000395], [-4.72e-13], [2.96e-7], [-2.43e-7], [-1.62e-7]])\n",
"Iteration Nr.10 abgeschlossen\n",
"Matrix([[-7.42e-5], [-3.31e-5], [-6.95e-5], [1.10e-12], [-5.21e-8], [4.27e-8], [2.85e-8]])\n",
"Iteration Nr.11 abgeschlossen\n",
"Matrix([[1.30e-5], [5.82e-6], [1.22e-5], [-3.48e-13], [9.15e-9], [-7.51e-9], [-5.00e-9]])\n",
"Matrix([[3.79e+6], [5.47e+5], [5.08e+6], [3.79e+6], [5.47e+5], [5.08e+6], [3.79e+6], [5.47e+5], [5.08e+6], [3.79e+6], [5.47e+5], [5.08e+6]])\n",
"Matrix([[3.79e+6], [5.46e+5], [5.08e+6], [3.79e+6], [5.46e+5], [5.08e+6], [3.79e+6], [5.47e+5], [5.08e+6], [3.79e+6], [5.47e+5], [5.08e+6]])\n",
"x = Matrix([[3.80e+6], [5.48e+5], [5.08e+6], [0.979], [-0.481], [0.677], [3.42]])\n",
"\n",
"l_berechnet_final:\n",
"10001: 3794874.637, 546738.682, 5080033.793\n",
"10002: 3794844.297, 546729.060, 5080066.484\n",
"10037: 3794770.848, 546952.857, 5080042.910\n",
"10044: 3794727.668, 546958.039, 5080082.867\n",
"Streckendifferenzen:\n",
"[4.899982, 5.418896, 4.814927, 4.248968]\n",
"\n",
"Differenz Schwerpunkt (Vektor):\n",
"Matrix([[-4.66e-10], [-2.91e-11], [-4.66e-10]])\n",
"Betrag der Schwerpunkt-Differenz:\n",
"0.000m\n"
]
}
],
"execution_count": 10
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-09T13:21:57.869927Z",
"start_time": "2025-12-09T13:21:57.831927Z"
}
},
"cell_type": "code",
"source": [
"importlib.reload(Koordinatentransformationen)\n",
"trafos = Koordinatentransformationen.Transformationen(pfad_datenbank)\n",
"\n",
"koordinaten_transformiert = trafos.Helmerttransformation(transformationsparameter)\n",
"print(koordinaten_transformiert)"
],
"id": "df0dcccb73299fcf",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'100': Matrix([\n",
"[3794775.24129598],\n",
"[547003.784566026],\n",
"[5080016.88582709]]), '100016': Matrix([\n",
"[3794804.40667566],\n",
"[546789.942078133],\n",
"[5080079.63649543]]), '10003': Matrix([\n",
"[3794820.51175889],\n",
"[546738.121545569],\n",
"[5080085.66027136]]), '10004': Matrix([\n",
"[3794787.68039096],\n",
"[546724.355539902],\n",
"[5080122.57197924]]), '10005': Matrix([\n",
"[3794778.52744143],\n",
"[546733.440239574],\n",
"[5080127.82709672]]), '10006': Matrix([\n",
"[3794754.48961774],\n",
"[546723.992131817],\n",
"[ 5080154.6491784]]), '10007': Matrix([\n",
"[ 3794810.10603],\n",
"[546761.510609237],\n",
"[ 5080086.0020765]]), '10008': Matrix([\n",
"[3794768.08506589],\n",
"[546757.433496855],\n",
"[ 5080127.7145515]]), '10009': Matrix([\n",
"[3794753.66298807],\n",
"[546753.936763795],\n",
"[ 5080143.3866772]]), '10010': Matrix([\n",
"[3794744.05628981],\n",
"[546780.742811981],\n",
"[ 5080141.1636738]]), '10011': Matrix([\n",
"[3794820.53929209],\n",
"[546738.130781189],\n",
"[ 5080085.6893844]]), '10011a': Matrix([\n",
"[3794863.58049222],\n",
"[546820.559672293],\n",
"[5080009.68904121]]), '10012': Matrix([\n",
"[3794827.81056398],\n",
"[ 546801.06957277],\n",
"[ 5080052.2764653]]), '10013': Matrix([\n",
"[3794822.93179095],\n",
"[ 546821.6420314],\n",
"[5080048.14190298]]), '10014': Matrix([\n",
"[3794813.94866435],\n",
"[546809.911071387],\n",
"[5080061.83762516]]), '10015': Matrix([\n",
"[3794815.71134214],\n",
"[ 546792.41022388],\n",
"[5080067.67633712]]), '10017': Matrix([\n",
"[ 3794800.4139096],\n",
"[546829.880936662],\n",
"[5080066.46875977]]), '10018': Matrix([\n",
"[3794745.61855632],\n",
"[546808.043262064],\n",
"[5080128.60455588]]), '10019': Matrix([\n",
"[3794777.76781701],\n",
"[546835.162107796],\n",
"[5080085.96045632]]), '10020': Matrix([\n",
"[3794761.92015664],\n",
"[546838.977847251],\n",
"[5080100.04765691]]), '10021': Matrix([\n",
"[3794756.00797839],\n",
"[546839.303246531],\n",
"[5080105.58935921]]), '10022': Matrix([\n",
"[3794757.61562001],\n",
"[546846.316729031],\n",
"[5080100.89762826]]), '10023': Matrix([\n",
"[3794759.48549621],\n",
"[546851.850918009],\n",
"[5080096.94440005]]), '10024': Matrix([\n",
"[3794751.73730327],\n",
"[546861.360519194],\n",
"[ 5080100.294253]]), '10025': Matrix([\n",
"[3794752.15498749],\n",
"[546874.781453645],\n",
"[5080094.37790759]]), '10026': Matrix([\n",
"[3794736.30003562],\n",
"[546836.826883513],\n",
"[5080125.44708045]]), '10027': Matrix([\n",
"[3794736.96000806],\n",
"[546879.602518143],\n",
"[5080106.77720197]]), '10028': Matrix([\n",
"[3794854.42558972],\n",
"[546891.260466949],\n",
"[5079988.55485218]]), '10029': Matrix([\n",
"[3794813.64192358],\n",
"[546903.641106338],\n",
"[5080022.06775364]]), '10030': Matrix([\n",
"[3794814.74562118],\n",
"[546890.757518605],\n",
"[5080026.46489654]]), '10031': Matrix([\n",
"[3794794.79058969],\n",
"[ 546872.71612651],\n",
"[5080053.61063449]]), '10032': Matrix([\n",
"[3794781.55115635],\n",
"[546884.895506778],\n",
"[5080060.97408994]]), '10033': Matrix([\n",
"[3794775.28175346],\n",
"[546873.334968714],\n",
"[5080072.04785473]]), '10034': Matrix([\n",
"[3794847.88993759],\n",
"[546944.410223245],\n",
"[5079972.30963836]]), '10035': Matrix([\n",
"[3794811.83097489],\n",
"[546946.723835994],\n",
"[5080006.35848061]]), '10036': Matrix([\n",
"[3794783.39377131],\n",
"[546958.852789368],\n",
"[5080028.37568321]]), '10038': Matrix([\n",
"[3794777.81558632],\n",
"[546923.349922253],\n",
"[5080048.80883121]]), '10039': Matrix([\n",
"[3794776.73400961],\n",
"[546909.821579998],\n",
"[5080055.59246981]]), '10040': Matrix([\n",
"[3794753.10534012],\n",
"[546951.926584076],\n",
"[5080060.54410929]]), '10041': Matrix([\n",
"[3794752.59165627],\n",
"[546924.173873934],\n",
"[5080073.19205182]]), '10042': Matrix([\n",
"[ 3794734.5543882],\n",
"[546937.377006367],\n",
"[ 5080084.7094339]]), '10043': Matrix([\n",
"[3794725.01661161],\n",
"[546922.581244633],\n",
"[5080100.03703997]]), '10045': Matrix([\n",
"[3794840.94124498],\n",
"[546995.029310828],\n",
"[5079957.56668261]]), '10046': Matrix([\n",
"[3794809.35039725],\n",
"[546994.054056815],\n",
"[5079987.99204381]]), '10047': Matrix([\n",
"[ 3794795.4368399],\n",
"[547001.195960895],\n",
"[5079998.39175117]]), '10048': Matrix([\n",
"[3794775.20666754],\n",
"[ 547003.76019026],\n",
"[5080016.84921095]]), '10049': Matrix([\n",
"[ 3794755.2599089],\n",
"[547011.179377653],\n",
"[5080033.23331374]]), '10049a': Matrix([\n",
"[3794754.91370463],\n",
"[ 547010.69453884],\n",
"[5080033.75043702]]), '10050': Matrix([\n",
"[3794737.22036781],\n",
"[547005.884569236],\n",
"[ 5080052.9475627]]), '10051': Matrix([\n",
"[3794738.87891047],\n",
"[546983.860512271],\n",
"[5080060.85649738]]), '10052': Matrix([\n",
"[3794717.92069948],\n",
"[ 546983.44591806],\n",
"[5080081.08810058]]), '10053': Matrix([\n",
"[3794720.08539271],\n",
"[547013.409039769],\n",
"[ 5080066.3657566]]), '10054': Matrix([\n",
"[3794843.61283972],\n",
"[547056.002453676],\n",
"[5079929.24030295]]), '10055': Matrix([\n",
"[ 3794798.3440481],\n",
"[547058.886596772],\n",
"[5079971.04209003]]), '10056': Matrix([\n",
"[3794785.08794943],\n",
"[547072.889218702],\n",
"[5079977.73770134]]), '10057': Matrix([\n",
"[3794764.13960343],\n",
"[ 547061.72963122],\n",
"[5080002.94483388]]), '10058': Matrix([\n",
"[3794731.98341087],\n",
"[547079.121468109],\n",
"[5080026.61270465]]), '10059': Matrix([\n",
"[3794706.22500851],\n",
"[547072.229773112],\n",
"[5080054.43002136]])}\n"
]
}
],
"execution_count": 20
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-12-09T13:25:19.224013Z",
"start_time": "2025-12-09T13:25:19.204366Z"
}
},
"cell_type": "code",
"source": [
"importlib.reload(Datenbank)\n",
"db_zugriff = Datenbank.Datenbankzugriff(pfad_datenbank)\n",
"\n",
"db_zugriff.set_koordinaten(koordinaten_transformiert, \"naeherung_us\")"
],
"id": "f6993d81c8a145dd",
"outputs": [],
"execution_count": 24
}
],
"metadata": {

View File

@@ -1,53 +0,0 @@
10009;1000,0000;2000,0000;100,0000;
10006;1000,0000;2032,6863;99,5825;
10010;1011,8143;1973,3252;99,9259;
10018;1008,5759;1942,7620;100,2553;
10008;979,7022;1991,4010;99,7320;
10005;966,5154;2014,6496;99,7200;
10003;908,4312;1996,1248;99,7403;
10004;954,1536;2021,6822;99,4916;
10007;921,7481;1973,6201;99,9176;
10001;833,9439;1978,3737;99,8946;
10002;875,9684;1998,5174;99,5867;
100016;928,2783;1944,0082;100,0459;
10011;908,4308;1996,1277;99,7822;
10011a;844,9567;1891,1570;99,8117;
10026;1020,0059;1913,8703;100,3059;
10027;1016,9451;1866,2914;100,3251;
10043;1031,2077;1822,4739;100,3035;
10044;1025,9760;1782,4835;100,5461;
10021;992,7607;1904,8854;100,3533;
10020;984,6187;1903,3601;100,3423;
10024;997,4831;1881,7862;100,3032;
10025;996,3241;1866,8440;100,4102;
10022;990,0679;1896,5360;100,2194;
10023;987,3223;1889,8762;100,3430;
10019;962,6387;1902,3565;99,9772;
10033;964,0191;1860,8023;99,8551;
10017;931,6761;1900,9945;99,9572;
10052;1037,8750;1757,2999;100,2737;
10042;1017,3489;1803,0742;100,3441;
10053;1033,3758;1723,4258;100,2774;
10037;966,2253;1774,2051;99,9957;
10040;990,8832;1780,9678;100,1677;
10041;993,2769;1812,0310;100,4749;
10038;958,1899;1804,7135;100,0741;
10051;1008,9811;1750,1838;100,2880;
10036;948,6403;1763,5807;100,0063;
10035;910,1265;1768,0099;100,0781;
10039;960,3884;1820,0543;100,0983;
10059;1049,2587;1662,5451;100,0148;
10050;1010,0246;1726,2445;100,1493;
10049;984,7667;1714,5709;100,0101;
100;957,3912;1716,2864;99,7777;
10013;900,9076;1902,8730;99,7911;
10028;853,9608;1815,7417;99,7793;
10012;895,3032;1924,1523;99,8758;
10014;913,9706;1918,7731;99,8872;
10031;937,1557;1855,2805;99,8479;
10015;912,5157;1937,6471;99,9834;
10032;954,6732;1845,9356;99,7240;
10030;908,4749;1828,8008;99,5581;
10029;909,3343;1814,8767;99,5486;
10034;860,2357;1758,9282;99,7370;
10045;867,2324;1705,0630;99,7214;
1 10009 1000,0000 2000,0000 100,0000
2 10006 1000,0000 2032,6863 99,5825
3 10010 1011,8143 1973,3252 99,9259
4 10018 1008,5759 1942,7620 100,2553
5 10008 979,7022 1991,4010 99,7320
6 10005 966,5154 2014,6496 99,7200
7 10003 908,4312 1996,1248 99,7403
8 10004 954,1536 2021,6822 99,4916
9 10007 921,7481 1973,6201 99,9176
10 10001 833,9439 1978,3737 99,8946
11 10002 875,9684 1998,5174 99,5867
12 100016 928,2783 1944,0082 100,0459
13 10011 908,4308 1996,1277 99,7822
14 10011a 844,9567 1891,1570 99,8117
15 10026 1020,0059 1913,8703 100,3059
16 10027 1016,9451 1866,2914 100,3251
17 10043 1031,2077 1822,4739 100,3035
18 10044 1025,9760 1782,4835 100,5461
19 10021 992,7607 1904,8854 100,3533
20 10020 984,6187 1903,3601 100,3423
21 10024 997,4831 1881,7862 100,3032
22 10025 996,3241 1866,8440 100,4102
23 10022 990,0679 1896,5360 100,2194
24 10023 987,3223 1889,8762 100,3430
25 10019 962,6387 1902,3565 99,9772
26 10033 964,0191 1860,8023 99,8551
27 10017 931,6761 1900,9945 99,9572
28 10052 1037,8750 1757,2999 100,2737
29 10042 1017,3489 1803,0742 100,3441
30 10053 1033,3758 1723,4258 100,2774
31 10037 966,2253 1774,2051 99,9957
32 10040 990,8832 1780,9678 100,1677
33 10041 993,2769 1812,0310 100,4749
34 10038 958,1899 1804,7135 100,0741
35 10051 1008,9811 1750,1838 100,2880
36 10036 948,6403 1763,5807 100,0063
37 10035 910,1265 1768,0099 100,0781
38 10039 960,3884 1820,0543 100,0983
39 10059 1049,2587 1662,5451 100,0148
40 10050 1010,0246 1726,2445 100,1493
41 10049 984,7667 1714,5709 100,0101
42 100 957,3912 1716,2864 99,7777
43 10013 900,9076 1902,8730 99,7911
44 10028 853,9608 1815,7417 99,7793
45 10012 895,3032 1924,1523 99,8758
46 10014 913,9706 1918,7731 99,8872
47 10031 937,1557 1855,2805 99,8479
48 10015 912,5157 1937,6471 99,9834
49 10032 954,6732 1845,9356 99,7240
50 10030 908,4749 1828,8008 99,5581
51 10029 909,3343 1814,8767 99,5486
52 10034 860,2357 1758,9282 99,7370
53 10045 867,2324 1705,0630 99,7214

View File

@@ -32,11 +32,13 @@ class Datenbankzugriff:
def __init__(self, pfad_datenbank):
self.pfad_datenbank = pfad_datenbank
def get_koordinaten(self, koordinatenart, ausgabeart = "Vektoren"):
def get_koordinaten(self, koordinatenart, ausgabeart = "Dict"):
con = sqlite3.connect(self.pfad_datenbank)
cursor = con.cursor()
if koordinatenart == "naeherung_lh":
values = "punktnummer, naeherungx_lh, naeherungy_lh, naeherungz_lh"
elif koordinatenart == "naeherung_us":
values = "punktnummer, naeherungx_us, naeherungy_us, naeherungz_us"
liste_koordinaten = cursor.execute(f"""
SELECT {values} FROM Netzpunkte;
@@ -44,9 +46,33 @@ class Datenbankzugriff:
cursor.close()
con.close()
if ausgabeart == "Vektoren":
liste_koordinaten_vektoren = []
for koordinate in liste_koordinaten:
liste_koordinaten_vektoren.append({koordinate[0]: sp.Matrix([float(koordinate[1]), float(koordinate[2]), float(koordinate[3])])})
liste_koordinaten = liste_koordinaten_vektoren
return liste_koordinaten
liste_koordinaten = [
koordinate for koordinate in liste_koordinaten
if koordinate[1] is not None and koordinate[2] is not None and koordinate[3] is not None
]
if ausgabeart == "Dict":
return {koordinate[0]: sp.Matrix([float(koordinate[1]), float(koordinate[2]), float(koordinate[3])]) for koordinate in liste_koordinaten}
def set_koordinaten(self, dict_koordinaten, koordinatenart):
con = sqlite3.connect(self.pfad_datenbank)
cursor = con.cursor()
daten = []
for punktnummer, wert in dict_koordinaten.items():
daten.append((
float(wert[0]),
float(wert[1]),
float(wert[2]),
str(punktnummer)
))
if koordinatenart == "naeherung_lh":
pass
elif koordinatenart == "naeherung_us":
cursor.executemany(f"""UPDATE Netzpunkte SET naeherungx_us = ?, naeherungy_us = ?, naeherungz_us = ? WHERE punktnummer = ? AND naeherungx_us IS NULL AND naeherungy_us IS NULL AND naeherungz_us IS NULL""", daten)
con.commit()
cursor.close()
con.close()

View File

@@ -0,0 +1,282 @@
import sympy as sp
from sympy.algebras.quaternion import Quaternion
import Datenbank
from itertools import combinations
class Transformationen:
def __init__(self, pfad_datenbank):
self.pfad_datenbank = pfad_datenbank
@staticmethod
def R_matrix_aus_euler(e1, e2, e3):
return sp.Matrix([
[
sp.cos(e2) * sp.cos(e3),
sp.cos(e1) * sp.sin(e3) + sp.sin(e1) * sp.sin(e2) * sp.cos(e3),
sp.sin(e1) * sp.sin(e3) - sp.cos(e1) * sp.sin(e2) * sp.cos(e3)
],
[
-sp.cos(e2) * sp.sin(e3),
sp.cos(e1) * sp.cos(e3) - sp.sin(e1) * sp.sin(e2) * sp.sin(e3),
sp.sin(e1) * sp.cos(e3) + sp.cos(e1) * sp.sin(e2) * sp.sin(e3)
],
[
sp.sin(e2),
-sp.sin(e1) * sp.cos(e2),
sp.cos(e1) * sp.cos(e2)
]
])
def Helmerttransformation_Euler_Transformationsparameter_berechne(self):
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))
anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)
liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]
liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]
print("Anzahl gemeinsame Punkte:", anzahl_gemeinsame_punkte)
print("\nErste Zielpunkte:")
for pn, P in list(zip(gemeinsame_punktnummern, liste_punkte_zielsystem))[:5]:
print(pn, [float(P[0]), float(P[1]), float(P[2])])
print("\nErste Ausgangspunkte:")
for pn, p in list(zip(gemeinsame_punktnummern, liste_punkte_ausgangssystem))[:5]:
print(pn, [float(p[0]), float(p[1]), float(p[2])])
# --- Näherungswerte (minimal erweitert) ---
p1, p2, p3 = liste_punkte_ausgangssystem[0], liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[2]
P1, P2, P3 = liste_punkte_zielsystem[0], liste_punkte_zielsystem[1], liste_punkte_zielsystem[2]
# 1) Näherungswert Maßstab: Mittelwert aus allen Punktpaaren
ratios = []
for i, j in combinations(range(anzahl_gemeinsame_punkte), 2):
dp = (liste_punkte_ausgangssystem[j] - liste_punkte_ausgangssystem[i]).norm()
dP = (liste_punkte_zielsystem[j] - liste_punkte_zielsystem[i]).norm()
dp_f = float(dp)
if dp_f > 0:
ratios.append(float(dP) / dp_f)
m0 = sum(ratios) / len(ratios)
if ratios:
print("min/mean/max:",
min(ratios),
sum(ratios) / len(ratios),
max(ratios))
U = (P2 - P1) / (P2 - P1).norm()
W = (U.cross(P3 - P1)) / (U.cross(P3 - P1)).norm()
V = W.cross(U)
u = (p2 - p1) / (p2 - p1).norm()
w = (u.cross(p3 - p1)) / (u.cross(p3 - p1)).norm()
v = w.cross(u)
R0 = sp.Matrix.hstack(U, V, W) * sp.Matrix.hstack(u, v, w).T
XS = sum(liste_punkte_zielsystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
xS = sum(liste_punkte_ausgangssystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Translation0 = XS - m0 * R0 * xS
# 2) Test auf orthonormale Drehmatrix bei 3 Nachkommastellen!
if R0.T.applyfunc(lambda x: round(float(x), 3)) == R0.inv().applyfunc(lambda x: round(float(x), 3)) \
and (R0.T * R0).applyfunc(lambda x: round(float(x), 3)) == sp.eye(3).applyfunc(
lambda x: round(float(x), 3)) \
and ((round(R0.det(), 3) == 1.000 or round(R0.det(), 3) == -1.000)):
print("R ist Orthonormal!")
else:
print("R ist nicht Orthonormal!")
# 3) Euler-Näherungswerte aus R0
e2_0 = sp.asin(R0[2, 0])
# Schutz gegen Division durch 0 wenn cos(e2) ~ 0:
cos_e2_0 = sp.cos(e2_0)
e1_0 = sp.acos(R0[2, 2] / cos_e2_0)
e3_0 = sp.acos(R0[0, 0] / cos_e2_0)
# --- Symbolische Unbekannte (klassische 7 Parameter) ---
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
R_symbolisch = self.R_matrix_aus_euler(e1, e2, e3)
# 4) Funktionales Modell
f_zeilen = []
for punkt in liste_punkte_ausgangssystem:
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
f_zeile_i = sp.Matrix([dX, dY, dZ]) + m * R_symbolisch * punkt_vektor
f_zeilen.extend(list(f_zeile_i))
f_matrix = sp.Matrix(f_zeilen)
f = f_matrix
A_ohne_zahlen = f_matrix.jacobian([dX, dY, dZ, m, e1, e2, e3])
# Parameterschätzung
schwellenwert = 1e-4
anzahl_iterationen = 0
alle_kleiner_vorherige_iteration = False
l_vektor = sp.Matrix([koord for P in liste_punkte_zielsystem for koord in P])
l = l_vektor
P_mat = sp.eye(3 * anzahl_gemeinsame_punkte)
l_berechnet_0 = None
while True:
if anzahl_iterationen == 0:
zahlen_0 = {
dX: float(Translation0[0]),
dY: float(Translation0[1]),
dZ: float(Translation0[2]),
m: float(m0),
e1: float(e1_0),
e2: float(e2_0),
e3: float(e3_0)
}
x0 = sp.Matrix([zahlen_0[dX], zahlen_0[dY], zahlen_0[dZ],
zahlen_0[m], zahlen_0[e1], zahlen_0[e2], zahlen_0[e3]])
l_berechnet_0 = f.subs(zahlen_0).evalf(n=30)
dl_0 = l_vektor - l_berechnet_0
A_0 = A_ohne_zahlen.subs(zahlen_0).evalf(n=30)
N = A_0.T * P_mat * A_0
n_0 = A_0.T * P_mat * dl_0
Qxx_0 = N.inv()
dx = Qxx_0 * n_0
x = x0 + dx
x = sp.N(x, 30)
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
else:
zahlen_i = {
dX: float(x[0]),
dY: float(x[1]),
dZ: float(x[2]),
m: float(x[3]),
e1: float(x[4]),
e2: float(x[5]),
e3: float(x[6])
}
l_berechnet_i = f.subs(zahlen_i).evalf(n=30)
dl_i = l_vektor - l_berechnet_i
A_i = A_ohne_zahlen.subs(zahlen_i).evalf(n=30)
N_i = A_i.T * P_mat * A_i
Qxx_i = N_i.inv()
n_i = A_i.T * P_mat * dl_i
dx = Qxx_i * n_i
x = sp.Matrix(x + dx)
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
alle_kleiner = True
for i in range(dx.rows):
wert = float(dx[i])
if abs(wert) > schwellenwert:
alle_kleiner = False
if (alle_kleiner and alle_kleiner_vorherige_iteration) or anzahl_iterationen == 100:
break
alle_kleiner_vorherige_iteration = alle_kleiner
print(l.evalf(n=3))
print(l_berechnet_0.evalf(n=3))
print(f"x = {x.evalf(n=3)}")
# --- Neuberechnung Zielsystem ---
zahlen_final = {
dX: float(x[0]),
dY: float(x[1]),
dZ: float(x[2]),
m: float(x[3]),
e1: float(x[4]),
e2: float(x[5]),
e3: float(x[6])
}
l_berechnet_final = f.subs(zahlen_final).evalf(n=30)
liste_l_berechnet_final = []
for i in range(anzahl_gemeinsame_punkte):
Xi = l_berechnet_final[3 * i + 0]
Yi = l_berechnet_final[3 * i + 1]
Zi = l_berechnet_final[3 * i + 2]
liste_l_berechnet_final.append(sp.Matrix([Xi, Yi, Zi]))
print("")
print("l_berechnet_final:")
for punktnummer, l_fin in zip(gemeinsame_punktnummern, liste_l_berechnet_final):
print(f"{punktnummer}: {float(l_fin[0]):.3f}, {float(l_fin[1]):.3f}, {float(l_fin[2]):.3f}")
print("Streckendifferenzen:")
streckendifferenzen = [
(punkt_zielsys - l_final).norm()
for punkt_zielsys, l_final in zip(liste_punkte_zielsystem, liste_l_berechnet_final)
]
print([round(float(s), 6) for s in streckendifferenzen])
Schwerpunkt_Zielsystem = sum(liste_punkte_zielsystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunkt_berechnet = sum(liste_l_berechnet_final, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunktsdifferenz = Schwerpunkt_Zielsystem - Schwerpunkt_berechnet
print("\nDifferenz Schwerpunkt (Vektor):")
print(Schwerpunktsdifferenz.evalf(3))
print("Betrag der Schwerpunkt-Differenz:")
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")
return zahlen_final
def Helmerttransformation(self, transformationsparameter):
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
unterschiedliche_punktnummern = sorted(set(dict_ausgangssystem.keys()) ^ set(dict_zielsystem.keys()))
punktnummern_transformieren = [
punktnummer for punktnummer in unterschiedliche_punktnummern if punktnummer in dict_ausgangssystem
]
liste_punkte_ausgangssystem = [dict_ausgangssystem[punktnummer] for punktnummer in punktnummern_transformieren]
R = self.R_matrix_aus_euler(transformationsparameter[e1], transformationsparameter[e2], transformationsparameter[e3])
f_zeilen = []
for punkt in liste_punkte_ausgangssystem:
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
f_zeile_i = sp.Matrix([transformationsparameter[dX], transformationsparameter[dY], transformationsparameter[dZ]]) + transformationsparameter[m] * R * punkt_vektor
f_zeilen.extend(list(f_zeile_i))
f_matrix = sp.Matrix(f_zeilen)
dict_transformiert = {}
for i, pn in enumerate(punktnummern_transformieren):
Xi = f_matrix[3 * i + 0]
Yi = f_matrix[3 * i + 1]
Zi = f_matrix[3 * i + 2]
dict_transformiert[str(pn)] = sp.Matrix([
[float(Xi)],
[float(Yi)],
[float(Zi)]
])
return dict_transformiert

View File

@@ -0,0 +1,208 @@
@staticmethod
def R_matrix_aus_quaternion(q0, q1, q2, q3):
return sp.Matrix([
[1 - 2 * (q2 ** 2 + q3 ** 2), 2 * (q1 * q2 - q0 * q3), 2 * (q0 * q2 + q1 * q3)],
[2 * (q1 * q2 + q0 * q3), 1 - 2 * (q1 ** 2 + q3 ** 2), 2 * (q2 * q3 - q0 * q1)],
[2 * (q1 * q3 - q0 * q2), 2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1 ** 2 + q2 ** 2)]
])
def Helmerttransformation_Quaternionen(self):
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))
anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)
liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]
liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]
print("Anzahl gemeinsame Punkte:", anzahl_gemeinsame_punkte)
print("\nErste Zielpunkte:")
for pn, P in list(zip(gemeinsame_punktnummern, liste_punkte_zielsystem))[:5]:
print(pn, [float(P[0]), float(P[1]), float(P[2])])
print("\nErste Ausgangspunkte:")
for pn, p in list(zip(gemeinsame_punktnummern, liste_punkte_ausgangssystem))[:5]:
print(pn, [float(p[0]), float(p[1]), float(p[2])])
# ToDo: Achtung: Die Ergebnisse sind leicht anders, als in den Beispielrechnung von Luhmann (Rundungsfehler bei Luhmann?)
# ToDo: Automatische Ermittlung der Anzahl Nachkommastellen für Test auf Orthonormalität integrieren!
p1, p2, p3 = liste_punkte_ausgangssystem[0], liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[2]
P1, P2, P3 = liste_punkte_zielsystem[0], liste_punkte_zielsystem[1], liste_punkte_zielsystem[2]
# 1) Näherungswertberechnung
m0 = (P2 - P1).norm() / (p2 - p1).norm()
U = (P2 - P1) / (P2 - P1).norm()
W = (U.cross(P3 - P1)) / (U.cross(P3 - P1)).norm()
V = W.cross(U)
u = (p2 - p1) / (p2 - p1).norm()
w = (u.cross(p3 - p1)) / (u.cross(p3 - p1)).norm()
v = w.cross(u)
R = sp.Matrix.hstack(U, V, W) * sp.Matrix.hstack(u, v, w).T
XS = (P1 + P2 + P3) / 3
xS = (p1 + p2 + p3) / 3
Translation = XS - m0 * R * xS
# 2) Test auf orthonormale Drehmatrix bei 3 Nachkommastellen!
if R.T.applyfunc(lambda x: round(float(x), 3)) == R.inv().applyfunc(lambda x: round(float(x), 3)) and (
R.T * R).applyfunc(lambda x: round(float(x), 3)) == sp.eye(3).applyfunc(lambda x: round(float(x), 3)) and (
(round(R.det(), 3) == 1.000 or round(R.det(), 3) == -1.000)):
print("R ist Orthonormal!")
else:
print("R ist nicht Orthonormal!")
# 3) Quaternionen berechnen
# ToDo: Prüfen, ob Vorzeichen bei q0 richtig ist!
# ToDo: q0 stimmt nicht mit Luhmann überein!
q = Quaternion.from_rotation_matrix(R)
q0_wert = q.a
q1_wert = q.b
q2_wert = q.c
q3_wert = q.d
dX, dY, dZ, m, q0, q1, q2, q3 = sp.symbols('dX dY dZ m q0 q1 q2 q3')
R_symbolisch = self.R_matrix_aus_quaternion(q0, q1, q2, q3)
# 4) Funktionales Modell
f_zeilen = []
for punkt in liste_punkte_ausgangssystem:
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
f_zeile_i = sp.Matrix([dX, dY, dZ]) + m * R_symbolisch * punkt_vektor
f_zeilen.extend(list(f_zeile_i))
f_matrix = sp.Matrix(f_zeilen)
f = f_matrix
A_ohne_zahlen = f_matrix.jacobian([dX, dY, dZ, m, q0, q1, q2, q3])
# Parameterschätzung
schwellenwert = 1e-4
anzahl_iterationen = 0
alle_kleiner_vorherige_iteration = False
l_vektor = sp.Matrix([koord for P in liste_punkte_zielsystem for koord in P])
l = l_vektor
P = sp.eye(3 * anzahl_gemeinsame_punkte)
l_berechnet_0 = None
while True:
if anzahl_iterationen == 0:
zahlen_0 = {dX: float(Translation[0]), dY: float(Translation[1]), dZ: float(Translation[2]), m: float(m0),
q0: float(q0_wert), q1: float(q1_wert),
q2: float(q2_wert),
q3: float(q3_wert)}
x0 = sp.Matrix(
[zahlen_0[dX], zahlen_0[dY], zahlen_0[dZ], zahlen_0[m], zahlen_0[q0], zahlen_0[q1], zahlen_0[q2],
zahlen_0[q3]])
l_berechnet_0 = f.subs(zahlen_0).evalf(n=30)
dl_0 = l_vektor - l_berechnet_0
A_0 = A_ohne_zahlen.subs(zahlen_0).evalf(n=30)
N = A_0.T * P * A_0
n_0 = A_0.T * P * dl_0
Qxx_0 = N.inv()
dx = Qxx_0 * n_0
x = x0 + dx
x = sp.N(x, 30) # 30 Nachkommastellen
q_norm = sp.sqrt(x[4] ** 2 + x[5] ** 2 + x[6] ** 2 + x[7] ** 2)
x = sp.Matrix(x)
x[4] /= q_norm
x[5] /= q_norm
x[6] /= q_norm
x[7] /= q_norm
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
else:
zahlen_i = {dX: float(x[0]), dY: float(x[1]), dZ: float(x[2]), m: float(x[3]), q0: float(x[4]),
q1: float(x[5]),
q2: float(x[6]),
q3: float(x[7])}
l_berechnet_i = f.subs(zahlen_i).evalf(n=30)
dl_i = l_vektor - l_berechnet_i
A_i = A_ohne_zahlen.subs(zahlen_i).evalf(n=30)
N_i = A_i.T * P * A_i
Qxx_i = N_i.inv()
n_i = A_i.T * P * dl_i
dx = Qxx_i * n_i
x = sp.Matrix(x + dx)
q_norm = sp.sqrt(x[4] ** 2 + x[5] ** 2 + x[6] ** 2 + x[7] ** 2)
x[4] /= q_norm
x[5] /= q_norm
x[6] /= q_norm
x[7] /= q_norm
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
alle_kleiner = True
for i in range(dx.rows):
wert = float(dx[i])
if abs(wert) > schwellenwert:
alle_kleiner = False
if alle_kleiner and alle_kleiner_vorherige_iteration or anzahl_iterationen == 100:
break
alle_kleiner_vorherige_iteration = alle_kleiner
print(l.evalf(n=3))
print(l_berechnet_0.evalf(n=3))
print(f"x = {x.evalf(n=3)}")
# Neuberechnung Zielsystem
zahlen_final = {
dX: float(x[0]),
dY: float(x[1]),
dZ: float(x[2]),
m: float(x[3]),
q0: float(x[4]),
q1: float(x[5]),
q2: float(x[6]),
q3: float(x[7])
}
l_berechnet_final = f.subs(zahlen_final).evalf(n=30)
liste_l_berechnet_final = []
for i in range(anzahl_gemeinsame_punkte):
Xi = l_berechnet_final[3 * i + 0]
Yi = l_berechnet_final[3 * i + 1]
Zi = l_berechnet_final[3 * i + 2]
liste_l_berechnet_final.append(sp.Matrix([Xi, Yi, Zi]))
print("")
print("l_berechnet_final:")
for punktnummer, l_fin in zip(gemeinsame_punktnummern, liste_l_berechnet_final):
print(f"{punktnummer}: {float(l_fin[0]):.3f}, {float(l_fin[1]):.3f}, {float(l_fin[2]):.3f}")
print("Streckendifferenzen:")
streckendifferenzen = [
(punkt_zielsys - l_final).norm()
for punkt_zielsys, l_final in zip(liste_punkte_zielsystem, liste_l_berechnet_final)
]
print([round(float(s), 6) for s in streckendifferenzen])
Schwerpunkt_Zielsystem = sum(liste_punkte_zielsystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunkt_berechnet = sum(liste_l_berechnet_final, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunktsdifferenz = Schwerpunkt_Zielsystem - Schwerpunkt_berechnet
print("\nDifferenz Schwerpunkt (Vektor):")
print(Schwerpunktsdifferenz.evalf(3))
print("Betrag der Schwerpunkt-Differenz:")
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")
# ToDo: Abweichungen in Printausgabe ausgeben!

View File

@@ -265,4 +265,218 @@ print(Schwerpunktsdifferenz.evalf(3))
print("Betrag der Schwerpunkt-Differenz:")
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")
#ToDo: Abweichungen in Printausgabe ausgeben!
#ToDo: Abweichungen in Printausgabe ausgeben!
def Helmerttransformation_Euler(self):
db = Datenbank.Datenbankzugriff(self.pfad_datenbank)
dict_ausgangssystem = db.get_koordinaten("naeherung_lh", "Dict")
dict_zielsystem = db.get_koordinaten("naeherung_us", "Dict")
gemeinsame_punktnummern = sorted(set(dict_ausgangssystem.keys()) & set(dict_zielsystem.keys()))
anzahl_gemeinsame_punkte = len(gemeinsame_punktnummern)
liste_punkte_ausgangssystem = [dict_ausgangssystem[i] for i in gemeinsame_punktnummern]
liste_punkte_zielsystem = [dict_zielsystem[i] for i in gemeinsame_punktnummern]
print("Anzahl gemeinsame Punkte:", anzahl_gemeinsame_punkte)
print("\nErste Zielpunkte:")
for pn, P in list(zip(gemeinsame_punktnummern, liste_punkte_zielsystem))[:5]:
print(pn, [float(P[0]), float(P[1]), float(P[2])])
print("\nErste Ausgangspunkte:")
for pn, p in list(zip(gemeinsame_punktnummern, liste_punkte_ausgangssystem))[:5]:
print(pn, [float(p[0]), float(p[1]), float(p[2])])
# --- Näherungswerte (minimal erweitert) ---
p1, p2, p3 = liste_punkte_ausgangssystem[0], liste_punkte_ausgangssystem[1], liste_punkte_ausgangssystem[2]
P1, P2, P3 = liste_punkte_zielsystem[0], liste_punkte_zielsystem[1], liste_punkte_zielsystem[2]
# 1) Näherungswert Maßstab: Mittelwert aus allen Punktpaaren
ratios = []
for i, j in combinations(range(anzahl_gemeinsame_punkte), 2):
dp = (liste_punkte_ausgangssystem[j] - liste_punkte_ausgangssystem[i]).norm()
dP = (liste_punkte_zielsystem[j] - liste_punkte_zielsystem[i]).norm()
dp_f = float(dp)
if dp_f > 0:
ratios.append(float(dP) / dp_f)
m0 = sum(ratios) / len(ratios)
if ratios:
print("min/mean/max:",
min(ratios),
sum(ratios) / len(ratios),
max(ratios))
U = (P2 - P1) / (P2 - P1).norm()
W = (U.cross(P3 - P1)) / (U.cross(P3 - P1)).norm()
V = W.cross(U)
u = (p2 - p1) / (p2 - p1).norm()
w = (u.cross(p3 - p1)) / (u.cross(p3 - p1)).norm()
v = w.cross(u)
R0 = sp.Matrix.hstack(U, V, W) * sp.Matrix.hstack(u, v, w).T
XS = sum(liste_punkte_zielsystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
xS = sum(liste_punkte_ausgangssystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Translation0 = XS - m0 * R0 * xS
# 2) Test auf orthonormale Drehmatrix bei 3 Nachkommastellen!
if R0.T.applyfunc(lambda x: round(float(x), 3)) == R0.inv().applyfunc(lambda x: round(float(x), 3)) \
and (R0.T * R0).applyfunc(lambda x: round(float(x), 3)) == sp.eye(3).applyfunc(lambda x: round(float(x), 3)) \
and ((round(R0.det(), 3) == 1.000 or round(R0.det(), 3) == -1.000)):
print("R ist Orthonormal!")
else:
print("R ist nicht Orthonormal!")
# 3) Euler-Näherungswerte aus R0
e2_0 = sp.asin(R0[2, 0])
# Schutz gegen Division durch 0 wenn cos(e2) ~ 0:
cos_e2_0 = sp.cos(e2_0)
e1_0 = sp.acos(R0[2, 2] / cos_e2_0)
e3_0 = sp.acos(R0[0, 0] / cos_e2_0)
# --- Symbolische Unbekannte (klassische 7 Parameter) ---
dX, dY, dZ, m, e1, e2, e3 = sp.symbols('dX dY dZ m e1 e2 e3')
R_symbolisch = self.R_matrix_aus_euler(e1, e2, e3)
# 4) Funktionales Modell
f_zeilen = []
for punkt in liste_punkte_ausgangssystem:
punkt_vektor = sp.Matrix([punkt[0], punkt[1], punkt[2]])
f_zeile_i = sp.Matrix([dX, dY, dZ]) + m * R_symbolisch * punkt_vektor
f_zeilen.extend(list(f_zeile_i))
f_matrix = sp.Matrix(f_zeilen)
f = f_matrix
A_ohne_zahlen = f_matrix.jacobian([dX, dY, dZ, m, e1, e2, e3])
# Parameterschätzung
schwellenwert = 1e-4
anzahl_iterationen = 0
alle_kleiner_vorherige_iteration = False
l_vektor = sp.Matrix([koord for P in liste_punkte_zielsystem for koord in P])
l = l_vektor
P_mat = sp.eye(3 * anzahl_gemeinsame_punkte)
l_berechnet_0 = None
while True:
if anzahl_iterationen == 0:
zahlen_0 = {
dX: float(Translation0[0]),
dY: float(Translation0[1]),
dZ: float(Translation0[2]),
m: float(m0),
e1: float(e1_0),
e2: float(e2_0),
e3: float(e3_0)
}
x0 = sp.Matrix([zahlen_0[dX], zahlen_0[dY], zahlen_0[dZ],
zahlen_0[m], zahlen_0[e1], zahlen_0[e2], zahlen_0[e3]])
l_berechnet_0 = f.subs(zahlen_0).evalf(n=30)
dl_0 = l_vektor - l_berechnet_0
A_0 = A_ohne_zahlen.subs(zahlen_0).evalf(n=30)
N = A_0.T * P_mat * A_0
n_0 = A_0.T * P_mat * dl_0
Qxx_0 = N.inv()
dx = Qxx_0 * n_0
x = x0 + dx
x = sp.N(x, 30)
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
else:
zahlen_i = {
dX: float(x[0]),
dY: float(x[1]),
dZ: float(x[2]),
m: float(x[3]),
e1: float(x[4]),
e2: float(x[5]),
e3: float(x[6])
}
l_berechnet_i = f.subs(zahlen_i).evalf(n=30)
dl_i = l_vektor - l_berechnet_i
A_i = A_ohne_zahlen.subs(zahlen_i).evalf(n=30)
N_i = A_i.T * P_mat * A_i
Qxx_i = N_i.inv()
n_i = A_i.T * P_mat * dl_i
dx = Qxx_i * n_i
x = sp.Matrix(x + dx)
anzahl_iterationen += 1
print(f"Iteration Nr.{anzahl_iterationen} abgeschlossen")
print(dx.evalf(n=3))
alle_kleiner = True
for i in range(dx.rows):
wert = float(dx[i])
if abs(wert) > schwellenwert:
alle_kleiner = False
if (alle_kleiner and alle_kleiner_vorherige_iteration) or anzahl_iterationen == 100:
break
alle_kleiner_vorherige_iteration = alle_kleiner
print(l.evalf(n=3))
print(l_berechnet_0.evalf(n=3))
print(f"x = {x.evalf(n=3)}")
# --- Neuberechnung Zielsystem ---
zahlen_final = {
dX: float(x[0]),
dY: float(x[1]),
dZ: float(x[2]),
m: float(x[3]),
e1: float(x[4]),
e2: float(x[5]),
e3: float(x[6])
}
l_berechnet_final = f.subs(zahlen_final).evalf(n=30)
liste_l_berechnet_final = []
for i in range(anzahl_gemeinsame_punkte):
Xi = l_berechnet_final[3 * i + 0]
Yi = l_berechnet_final[3 * i + 1]
Zi = l_berechnet_final[3 * i + 2]
liste_l_berechnet_final.append(sp.Matrix([Xi, Yi, Zi]))
print("")
print("l_berechnet_final:")
for punktnummer, l_fin in zip(gemeinsame_punktnummern, liste_l_berechnet_final):
print(f"{punktnummer}: {float(l_fin[0]):.3f}, {float(l_fin[1]):.3f}, {float(l_fin[2]):.3f}")
print("Streckendifferenzen:")
streckendifferenzen = [
(punkt_zielsys - l_final).norm()
for punkt_zielsys, l_final in zip(liste_punkte_zielsystem, liste_l_berechnet_final)
]
print([round(float(s), 6) for s in streckendifferenzen])
Schwerpunkt_Zielsystem = sum(liste_punkte_zielsystem, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunkt_berechnet = sum(liste_l_berechnet_final, sp.Matrix([0, 0, 0])) / anzahl_gemeinsame_punkte
Schwerpunktsdifferenz = Schwerpunkt_Zielsystem - Schwerpunkt_berechnet
print("\nDifferenz Schwerpunkt (Vektor):")
print(Schwerpunktsdifferenz.evalf(3))
print("Betrag der Schwerpunkt-Differenz:")
print(f"{float(Schwerpunktsdifferenz.norm()):.3f}m")