gtsam-develop 4.3a0.dev202506050756__cp313-cp313-macosx_11_0_arm64.whl → 4.3a0.dev202507301459__cp313-cp313-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gtsam-develop might be problematic. Click here for more details.

gtsam/gtsam/imuBias.pyi CHANGED
@@ -52,10 +52,16 @@ class ConstantBias:
52
52
  """
53
53
  get gyroscope bias
54
54
  """
55
+ def localCoordinates(self, b: ConstantBias) -> numpy.ndarray[tuple[typing.Literal[6], typing.Literal[1]], numpy.dtype[numpy.float64]]:
56
+ ...
55
57
  def print(self, s: str = '') -> None:
56
58
  """
57
59
  print with optional string
58
60
  """
61
+ def retract(self, v: numpy.ndarray[tuple[M, typing.Literal[1]], numpy.dtype[numpy.float64]]) -> ConstantBias:
62
+ """
63
+ The retract function.
64
+ """
59
65
  def serialize(self) -> str:
60
66
  ...
61
67
  def vector(self) -> numpy.ndarray[tuple[typing.Literal[6], typing.Literal[1]], numpy.dtype[numpy.float64]]:
Binary file
@@ -91,8 +91,8 @@ class TestCal3Fisheye(GtsamTestCase):
91
91
  noise_model = gtsam.noiseModel.Isotropic.Sigma(2, 1)
92
92
  pose_key, point_key = P(0), L(0)
93
93
  k = gtsam.Cal3Fisheye()
94
- state.insert_pose3(pose_key, gtsam.Pose3())
95
- state.insert_point3(point_key, self.obj_point)
94
+ state.insert(pose_key, gtsam.Pose3())
95
+ state.insertPoint3(point_key, self.obj_point)
96
96
  factor = gtsam.GenericProjectionFactorCal3Fisheye(measured, noise_model, pose_key, point_key, k)
97
97
  graph.add(factor)
98
98
  score = graph.error(state)
@@ -106,9 +106,9 @@ class TestCal3Fisheye(GtsamTestCase):
106
106
  noise_model = gtsam.noiseModel.Isotropic.Sigma(2, 1)
107
107
  camera_key, pose_key, landmark_key = K(0), P(0), L(0)
108
108
  k = gtsam.Cal3Fisheye()
109
- state.insert_cal3fisheye(camera_key, k)
110
- state.insert_pose3(pose_key, gtsam.Pose3())
111
- state.insert_point3(landmark_key, gtsam.Point3(self.obj_point))
109
+ state.insert(camera_key, k)
110
+ state.insert(pose_key, gtsam.Pose3())
111
+ state.insertPoint3(landmark_key, gtsam.Point3(self.obj_point))
112
112
  factor = gtsam.GeneralSFMFactor2Cal3Fisheye(measured, noise_model, pose_key, landmark_key, camera_key)
113
113
  graph.add(factor)
114
114
  score = graph.error(state)
@@ -168,8 +168,8 @@ class TestCal3Fisheye(GtsamTestCase):
168
168
  camera = gtsam.Cal3Fisheye()
169
169
  state = gtsam.Values()
170
170
  pose_key, landmark_key = P(0), L(0)
171
- state.insert_point3(landmark_key, obj_point)
172
- state.insert_pose3(pose_key, pose)
171
+ state.insertPoint3(landmark_key, obj_point)
172
+ state.insert(pose_key, pose)
173
173
  g = gtsam.NonlinearFactorGraph()
174
174
  noise_model = gtsam.noiseModel.Unit.Create(2)
175
175
  factor = gtsam.GenericProjectionFactorCal3Fisheye(img_point, noise_model, pose_key, landmark_key, camera)
@@ -93,8 +93,8 @@ class TestCal3Unified(GtsamTestCase):
93
93
  noise_model = gtsam.noiseModel.Isotropic.Sigma(2, 1)
94
94
  pose_key, point_key = P(0), L(0)
95
95
  k = self.stereographic
96
- state.insert_pose3(pose_key, gtsam.Pose3())
97
- state.insert_point3(point_key, self.obj_point)
96
+ state.insert(pose_key, gtsam.Pose3())
97
+ state.insertPoint3(point_key, self.obj_point)
98
98
  factor = gtsam.GenericProjectionFactorCal3Unified(measured, noise_model, pose_key, point_key, k)
99
99
  graph.add(factor)
100
100
  score = graph.error(state)
@@ -109,9 +109,9 @@ class TestCal3Unified(GtsamTestCase):
109
109
  noise_model = gtsam.noiseModel.Isotropic.Sigma(2, 1)
110
110
  camera_key, pose_key, landmark_key = K(0), P(0), L(0)
111
111
  k = self.stereographic
112
- state.insert_cal3unified(camera_key, k)
113
- state.insert_pose3(pose_key, gtsam.Pose3())
114
- state.insert_point3(landmark_key, self.obj_point)
112
+ state.insert(camera_key, k)
113
+ state.insert(pose_key, gtsam.Pose3())
114
+ state.insertPoint3(landmark_key, self.obj_point)
115
115
  factor = gtsam.GeneralSFMFactor2Cal3Unified(measured, noise_model, pose_key, landmark_key, camera_key)
116
116
  graph.add(factor)
117
117
  score = graph.error(state)
@@ -125,9 +125,9 @@ class TestCal3Unified(GtsamTestCase):
125
125
  camera = gtsam.Cal3Unified()
126
126
  state = gtsam.Values()
127
127
  camera_key, pose_key, landmark_key = K(0), P(0), L(0)
128
- state.insert_cal3unified(camera_key, camera)
129
- state.insert_point3(landmark_key, obj_point_on_axis)
130
- state.insert_pose3(pose_key, pose)
128
+ state.insert(camera_key, camera)
129
+ state.insertPoint3(landmark_key, obj_point_on_axis)
130
+ state.insert(pose_key, pose)
131
131
  g = gtsam.NonlinearFactorGraph()
132
132
  noise_model = gtsam.noiseModel.Unit.Create(2)
133
133
  factor = gtsam.GeneralSFMFactor2Cal3Unified(img_point, noise_model, pose_key, landmark_key, camera_key)
@@ -12,8 +12,24 @@ Author: Frank Dellaert
12
12
  import unittest
13
13
 
14
14
  import numpy as np
15
- from gtsam import (Rot3, SO3, SO4, FrobeniusBetweenFactorSO4, FrobeniusFactorSO4,
16
- ShonanFactor3, SOn)
15
+ from gtsam import (
16
+ Rot3,
17
+ SO3,
18
+ SO4,
19
+ FrobeniusBetweenFactorSO4,
20
+ FrobeniusFactorSO4,
21
+ ShonanFactor3,
22
+ SOn,
23
+ Similarity2,
24
+ Similarity3,
25
+ FrobeniusFactorSimilarity2,
26
+ FrobeniusBetweenFactorSimilarity2,
27
+ FrobeniusFactorSimilarity3,
28
+ FrobeniusBetweenFactorSimilarity3,
29
+ Gal3,
30
+ FrobeniusFactorGal3,
31
+ FrobeniusBetweenFactorGal3,
32
+ )
17
33
 
18
34
  id = SO4()
19
35
  v1 = np.array([0, 0, 0, 0.1, 0, 0])
@@ -21,6 +37,15 @@ Q1 = SO4.Expmap(v1)
21
37
  v2 = np.array([0, 0, 0, 0.01, 0.02, 0.03])
22
38
  Q2 = SO4.Expmap(v2)
23
39
 
40
+ P1_sim2 = Similarity2.Expmap(np.array([0.1, 0.2, 0.3, 0.4]))
41
+ P2_sim2 = Similarity2.Expmap(np.array([0.2, 0.3, 0.4, 0.5]))
42
+
43
+ P1_sim3 = Similarity3.Expmap(np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7]))
44
+ P2_sim3 = Similarity3.Expmap(np.array([0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]))
45
+
46
+ G1_gal3 = Gal3(Rot3.Rz(0.1), np.array([0.2, 0.3, 0.4]), np.array([0.5, 0.6, 0.7]), 0.8)
47
+ G2_gal3 = Gal3(Rot3.Rz(0.2), np.array([0.3, 0.4, 0.5]), np.array([0.6, 0.7, 0.8]), 0.9)
48
+
24
49
 
25
50
  class TestFrobeniusFactorSO4(unittest.TestCase):
26
51
  """Test FrobeniusFactor factors."""
@@ -29,7 +54,7 @@ class TestFrobeniusFactorSO4(unittest.TestCase):
29
54
  """Test creation of a factor that calculates the Frobenius norm."""
30
55
  factor = FrobeniusFactorSO4(1, 2)
31
56
  actual = factor.evaluateError(Q1, Q2)
32
- expected = (Q2.matrix()-Q1.matrix()).transpose().reshape((16,))
57
+ expected = (Q2.matrix() - Q1.matrix()).transpose().reshape((16,))
33
58
  np.testing.assert_array_equal(actual, expected)
34
59
 
35
60
  def test_frobenius_between_factor(self):
@@ -52,5 +77,47 @@ class TestFrobeniusFactorSO4(unittest.TestCase):
52
77
  np.testing.assert_array_almost_equal(actual, expected, decimal=4)
53
78
 
54
79
 
80
+ class TestFrobeniusFactorSimilarity2(unittest.TestCase):
81
+ def test_frobenius_factor(self):
82
+ factor = FrobeniusFactorSimilarity2(1, 2)
83
+ actual = factor.evaluateError(P1_sim2, P2_sim2)
84
+ expected = (P2_sim2.matrix() - P1_sim2.matrix()).transpose().reshape((9,))
85
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
86
+
87
+ def test_frobenius_between_factor(self):
88
+ factor = FrobeniusBetweenFactorSimilarity2(1, 2, P1_sim2.between(P2_sim2))
89
+ actual = factor.evaluateError(P1_sim2, P2_sim2)
90
+ expected = np.zeros((9,))
91
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
92
+
93
+
94
+ class TestFrobeniusFactorSimilarity3(unittest.TestCase):
95
+ def test_frobenius_factor(self):
96
+ factor = FrobeniusFactorSimilarity3(1, 2)
97
+ actual = factor.evaluateError(P1_sim3, P2_sim3)
98
+ expected = (P2_sim3.matrix() - P1_sim3.matrix()).transpose().reshape((16,))
99
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
100
+
101
+ def test_frobenius_between_factor(self):
102
+ factor = FrobeniusBetweenFactorSimilarity3(1, 2, P1_sim3.between(P2_sim3))
103
+ actual = factor.evaluateError(P1_sim3, P2_sim3)
104
+ expected = np.zeros((16,))
105
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
106
+
107
+
108
+ class TestFrobeniusFactorGal3(unittest.TestCase):
109
+ def test_frobenius_factor(self):
110
+ factor = FrobeniusFactorGal3(1, 2)
111
+ actual = factor.evaluateError(G1_gal3, G2_gal3)
112
+ expected = (G2_gal3.matrix() - G1_gal3.matrix()).transpose().reshape((25,))
113
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
114
+
115
+ def test_frobenius_between_factor(self):
116
+ factor = FrobeniusBetweenFactorGal3(1, 2, G1_gal3.between(G2_gal3))
117
+ actual = factor.evaluateError(G1_gal3, G2_gal3)
118
+ expected = np.zeros((25,))
119
+ np.testing.assert_allclose(actual, expected, atol=1e-9)
120
+
121
+
55
122
  if __name__ == "__main__":
56
123
  unittest.main()
@@ -29,9 +29,9 @@ class TestGraphvizFormatting(GtsamTestCase):
29
29
  self.graph.add(gtsam.BetweenFactorPose2(1, 2, odometry, odometryNoise))
30
30
 
31
31
  self.values = gtsam.Values()
32
- self.values.insert_pose2(0, gtsam.Pose2(0., 0., 0.))
33
- self.values.insert_pose2(1, gtsam.Pose2(2., 0., 0.))
34
- self.values.insert_pose2(2, gtsam.Pose2(4., 0., 0.))
32
+ self.values.insert(0, gtsam.Pose2(0., 0., 0.))
33
+ self.values.insert(1, gtsam.Pose2(2., 0., 0.))
34
+ self.values.insert(2, gtsam.Pose2(4., 0., 0.))
35
35
 
36
36
  def test_default(self):
37
37
  """Test with default GraphvizFormatting"""
@@ -40,8 +40,8 @@ class TestHybridGaussianFactorGraph(GtsamTestCase):
40
40
  nlfg.push_back(gtsam.HybridNonlinearFactor(mode, factors))
41
41
  nlfg.push_back(gtsam.DecisionTreeFactor(mode, "1 3"))
42
42
  values = gtsam.Values()
43
- values.insert_point3(1, Point3(0, 0, 0))
44
- values.insert_point3(2, Point3(2, 3, 1))
43
+ values.insertPoint3(1, Point3(0, 0, 0))
44
+ values.insertPoint3(2, Point3(2, 3, 1))
45
45
  hfg = nlfg.linearize(values)
46
46
  hbn = hfg.eliminateSequential()
47
47
  hbv = hbn.optimize()
gtsam/tests/test_Sim2.py CHANGED
@@ -8,6 +8,7 @@ See LICENSE for the license information
8
8
  Sim3 unit tests.
9
9
  Author: John Lambert
10
10
  """
11
+
11
12
  # pylint: disable=no-name-in-module
12
13
  import unittest
13
14
 
@@ -27,12 +28,12 @@ class TestSim2(GtsamTestCase):
27
28
  3 object poses
28
29
  same scale (no gauge ambiguity)
29
30
  world frame has poses rotated about 180 degrees.
30
- world and egovehicle frame translated by 15 meters w.r.t. each other
31
+ world and ego-vehicle frame translated by 15 meters w.r.t. each other
31
32
  """
32
33
  R180 = Rot2.fromDegrees(180)
33
34
 
34
- # Create source poses (three objects o1, o2, o3 living in the egovehicle "e" frame)
35
- # Suppose they are 3d cuboids detected by an onboard sensor in the egovehicle frame
35
+ # Create source poses (three objects o1, o2, o3 living in the ego-vehicle "e" frame)
36
+ # Suppose they are 3d cuboids detected by an onboard sensor in the ego-vehicle frame
36
37
  eTo0 = Pose2(Rot2(), np.array([5, 0]))
37
38
  eTo1 = Pose2(Rot2(), np.array([10, 0]))
38
39
  eTo2 = Pose2(Rot2(), np.array([15, 0]))
@@ -49,7 +50,7 @@ class TestSim2(GtsamTestCase):
49
50
 
50
51
  we_pairs = list(zip(wToi_list, eToi_list))
51
52
 
52
- # Recover the transformation wSe (i.e. world_S_egovehicle)
53
+ # Recover the transformation wSe (i.e. world_S_ego-vehicle)
53
54
  wSe = Similarity2.Align(we_pairs)
54
55
 
55
56
  for wToi, eToi in zip(wToi_list, eToi_list):
@@ -62,12 +63,12 @@ class TestSim2(GtsamTestCase):
62
63
  3 object poses
63
64
  with gauge ambiguity (2x scale)
64
65
  world frame has poses rotated by 90 degrees.
65
- world and egovehicle frame translated by 11 meters w.r.t. each other
66
+ world and ego-vehicle frame translated by 11 meters w.r.t. each other
66
67
  """
67
68
  R90 = Rot2.fromDegrees(90)
68
69
 
69
- # Create source poses (three objects o1, o2, o3 living in the egovehicle "e" frame)
70
- # Suppose they are 3d cuboids detected by an onboard sensor in the egovehicle frame
70
+ # Create source poses (three objects o1, o2, o3 living in the ego-vehicle "e" frame)
71
+ # Suppose they are 3d cuboids detected by an onboard sensor in the ego-vehicle frame
71
72
  eTo0 = Pose2(Rot2(), np.array([1, 0]))
72
73
  eTo1 = Pose2(Rot2(), np.array([2, 0]))
73
74
  eTo2 = Pose2(Rot2(), np.array([4, 0]))
@@ -84,7 +85,7 @@ class TestSim2(GtsamTestCase):
84
85
 
85
86
  we_pairs = list(zip(wToi_list, eToi_list))
86
87
 
87
- # Recover the transformation wSe (i.e. world_S_egovehicle)
88
+ # Recover the transformation wSe (i.e. world_S_ego-vehicle)
88
89
  wSe = Similarity2.Align(we_pairs)
89
90
 
90
91
  for wToi, eToi in zip(wToi_list, eToi_list):
@@ -122,7 +123,7 @@ class TestSim2(GtsamTestCase):
122
123
 
123
124
  ab_pairs = list(zip(aTi_list, bTi_list))
124
125
 
125
- # Recover the transformation wSe (i.e. world_S_egovehicle)
126
+ # Recover the transformation wSe (i.e. world_S_ego-vehicle)
126
127
  aSb = Similarity2.Align(ab_pairs)
127
128
 
128
129
  for aTi, bTi in zip(aTi_list, bTi_list):
@@ -190,6 +191,89 @@ class TestSim2(GtsamTestCase):
190
191
  bSa = Similarity2(R=bRa, t=bta, s=bsa)
191
192
  self.assertEqual(bSa.scale(), 3.0)
192
193
 
194
+ def test_compose(self) -> None:
195
+ """Test group operation: compose two Similarity2 elements."""
196
+ R1 = Rot2.fromDegrees(30)
197
+ t1 = np.array([1, 2])
198
+ s1 = 2.0
199
+ S1 = Similarity2(R=R1, t=t1, s=s1)
200
+
201
+ R2 = Rot2.fromDegrees(45)
202
+ t2 = np.array([-1, 1])
203
+ s2 = 4.0
204
+ S2 = Similarity2(R=R2, t=t2, s=s2)
205
+
206
+ S3 = S1.compose(S2)
207
+
208
+ # Compose manually
209
+ expected_R = R1.compose(R2)
210
+ expected_s = s1 * s2
211
+ expected_t = t1 / s2 + R1.matrix() @ t2
212
+ expected_S3 = Similarity2(R=expected_R, t=expected_t, s=expected_s)
213
+
214
+ self.gtsamAssertEquals(S3, expected_S3)
215
+ self.gtsamAssertEquals(S1 * S2, expected_S3)
216
+ self.gtsamAssertEquals(S1.matrix() @ S2.matrix(), S3.matrix())
217
+
218
+ def test_inverse(self) -> None:
219
+ """Test group operation: inverse of a Similarity2 element."""
220
+ R = Rot2.fromDegrees(60)
221
+ t = np.array([3, -2])
222
+ s = 4.0
223
+ S = Similarity2(R=R, t=t, s=s)
224
+ S_inv = S.inverse()
225
+
226
+ # Check that S * S_inv is identity
227
+ I_sim = S.compose(S_inv)
228
+ expected_I = Similarity2()
229
+ self.gtsamAssertEquals(I_sim, expected_I)
230
+
231
+ def test_identity(self) -> None:
232
+ """Test that the identity Similarity2 acts as expected."""
233
+ S_id = Similarity2()
234
+ R = Rot2.fromDegrees(10)
235
+ t = np.array([5, 7])
236
+ s = 2.5
237
+ S = Similarity2(R=R, t=t, s=s)
238
+
239
+ # Compose with identity
240
+ self.gtsamAssertEquals(S.compose(S_id), S)
241
+ self.gtsamAssertEquals(S_id.compose(S), S)
242
+
243
+ def test_transform_from_point2(self):
244
+ """Test Similarity2.transformFrom with a Point2."""
245
+ R = Rot2.fromAngle(np.pi / 4) # 45 degrees
246
+ t = np.array([1.0, 2.0])
247
+ s = 3.0
248
+ sim = Similarity2(R, t, s)
249
+
250
+ p = np.array([2.0, 0.0])
251
+
252
+ # Expected: s * (R * p + t)
253
+ expected = s * (R.matrix() @ p + t)
254
+ actual = sim.transformFrom(p)
255
+
256
+ np.testing.assert_allclose(expected, actual, atol=1e-9)
257
+
258
+ def test_transform_from_pose2(self):
259
+ """Test Similarity2.transformFrom with a Pose2."""
260
+ R_sim = Rot2.fromAngle(np.pi / 6) # 30 degrees
261
+ t_sim = np.array([1.0, -1.0])
262
+ s_sim = 2.0
263
+ sim = Similarity2(R_sim, t_sim, s_sim)
264
+
265
+ R_pose = Rot2.fromAngle(-np.pi / 4) # -45 degrees
266
+ t_pose = np.array([3.0, 4.0])
267
+ pose = Pose2(R_pose, t_pose)
268
+
269
+ expected_R = R_sim.compose(R_pose)
270
+ expected_t = s_sim * (R_sim.matrix() @ t_pose + t_sim)
271
+ expected = Pose2(expected_R, expected_t)
272
+
273
+ actual = sim.transformFrom(pose)
274
+
275
+ self.gtsamAssertEquals(actual, expected)
276
+
193
277
 
194
278
  if __name__ == "__main__":
195
279
  unittest.main()
gtsam/tests/test_Sim3.py CHANGED
@@ -8,6 +8,7 @@ See LICENSE for the license information
8
8
  Sim3 unit tests.
9
9
  Author: John Lambert
10
10
  """
11
+
11
12
  # pylint: disable=no-name-in-module
12
13
  import unittest
13
14
  from typing import List, Optional
@@ -16,12 +17,88 @@ import numpy as np
16
17
  from gtsam.utils.test_case import GtsamTestCase
17
18
 
18
19
  import gtsam
19
- from gtsam import Point3, Pose3, Rot3, Similarity3, BetweenFactorSimilarity3, NonlinearFactorGraph, Values, LevenbergMarquardtOptimizer, LevenbergMarquardtParams
20
+ from gtsam import (
21
+ BetweenFactorSimilarity3,
22
+ LevenbergMarquardtOptimizer,
23
+ LevenbergMarquardtParams,
24
+ NonlinearFactorGraph,
25
+ Point3,
26
+ Pose3,
27
+ Rot3,
28
+ Similarity3,
29
+ Values,
30
+ )
20
31
 
21
32
 
22
33
  class TestSim3(GtsamTestCase):
23
34
  """Test selected Sim3 methods."""
24
35
 
36
+ # Class constants for reuse
37
+ R1 = Rot3.RzRyRx(0.1, 0.2, 0.3)
38
+ t1 = Point3(1, 2, 3)
39
+ s1 = 2.0
40
+ S1 = Similarity3(R1, t1, s1)
41
+
42
+ R2 = Rot3.RzRyRx(0.4, 0.5, 0.6)
43
+ t2 = Point3(-1, 1, 0)
44
+ s2 = 4.0
45
+ S2 = Similarity3(R2, t2, s2)
46
+
47
+ def test_compose(self) -> None:
48
+ """Test group operation: compose two Similarity3 elements."""
49
+ S3 = self.S1.compose(self.S2)
50
+
51
+ # Compose manually
52
+ expected_R = self.R1.compose(self.R2)
53
+ expected_s = self.s1 * self.s2
54
+ expected_t = self.t1 / self.s2 + self.R1.matrix() @ self.t2
55
+ expected_S3 = Similarity3(expected_R, expected_t, expected_s)
56
+
57
+ self.gtsamAssertEquals(S3, expected_S3)
58
+ self.gtsamAssertEquals(self.S1 * self.S2, expected_S3)
59
+ self.gtsamAssertEquals(self.S1.matrix() @ self.S2.matrix(), S3.matrix())
60
+
61
+ def test_inverse(self) -> None:
62
+ """Test group operation: inverse of a Similarity3 element."""
63
+ S_inv = self.S1.inverse()
64
+
65
+ # Check that S1 * S1_inv is identity
66
+ I_sim = self.S1.compose(S_inv)
67
+ expected_I = Similarity3()
68
+ self.gtsamAssertEquals(I_sim, expected_I)
69
+
70
+ def test_identity(self) -> None:
71
+ """Test that the identity Similarity3 acts as expected."""
72
+ S_id = Similarity3()
73
+
74
+ # Compose with identity
75
+ self.gtsamAssertEquals(self.S1.compose(S_id), self.S1)
76
+ self.gtsamAssertEquals(S_id.compose(self.S1), self.S1)
77
+
78
+ def test_transform_from_point3(self):
79
+ """Test Similarity3.transformFrom with a Point3."""
80
+ p = Point3(2.0, 0.0, 1.0)
81
+
82
+ # Expected: s * (R * p + t)
83
+ expected = self.s1 * (self.R1.matrix() @ p + self.t1)
84
+ actual = self.S1.transformFrom(p)
85
+
86
+ np.testing.assert_allclose(expected, actual, atol=1e-9)
87
+
88
+ def test_transform_from_pose3(self):
89
+ """Test Similarity3.transformFrom with a Pose3."""
90
+ R_pose = Rot3.RzRyRx(0.2, -0.1, 0.3)
91
+ t_pose = Point3(3.0, 4.0, 5.0)
92
+ pose = Pose3(R_pose, t_pose)
93
+
94
+ expected_R = self.R1.compose(R_pose)
95
+ expected_t = self.s1 * (self.R1.matrix() @ t_pose + self.t1)
96
+ expected = Pose3(expected_R, expected_t)
97
+
98
+ actual = self.S1.transformFrom(pose)
99
+
100
+ self.gtsamAssertEquals(actual, expected)
101
+
25
102
  def test_align_poses_along_straight_line(self):
26
103
  """Test Pose3 Align method.
27
104
 
@@ -130,7 +207,7 @@ class TestSim3(GtsamTestCase):
130
207
  for aTi, bTi in zip(aTi_list, bTi_list):
131
208
  self.gtsamAssertEquals(aTi, aSb.transformFrom(bTi))
132
209
 
133
- def test_sim3_optimization(self)->None:
210
+ def test_sim3_optimization(self) -> None:
134
211
  # Create a PriorFactor with a Sim3 prior
135
212
  prior = Similarity3(Rot3.Ypr(0.1, 0.2, 0.3), Point3(1, 2, 3), 4)
136
213
  model = gtsam.noiseModel.Isotropic.Sigma(7, 1)
@@ -154,14 +231,22 @@ class TestSim3(GtsamTestCase):
154
231
  def test_sim3_optimization2(self) -> None:
155
232
  prior = Similarity3()
156
233
  m1 = Similarity3(Rot3.Ypr(np.pi / 4.0, 0, 0), Point3(2.0, 0, 0), 1.0)
157
- m2 = Similarity3(Rot3.Ypr(np.pi / 2.0, 0, 0), Point3(np.sqrt(8) * 0.9, 0, 0), 1.0)
158
- m3 = Similarity3(Rot3.Ypr(3 * np.pi / 4.0, 0, 0), Point3(np.sqrt(32) * 0.8, 0, 0), 1.0)
234
+ m2 = Similarity3(
235
+ Rot3.Ypr(np.pi / 2.0, 0, 0), Point3(np.sqrt(8) * 0.9, 0, 0), 1.0
236
+ )
237
+ m3 = Similarity3(
238
+ Rot3.Ypr(3 * np.pi / 4.0, 0, 0), Point3(np.sqrt(32) * 0.8, 0, 0), 1.0
239
+ )
159
240
  m4 = Similarity3(Rot3.Ypr(np.pi / 2.0, 0, 0), Point3(6 * 0.7, 0, 0), 1.0)
160
241
  loop = Similarity3(1.42)
161
242
 
162
243
  priorNoise = gtsam.noiseModel.Isotropic.Sigma(7, 0.01)
163
- betweenNoise = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 10]))
164
- betweenNoise2 = gtsam.noiseModel.Diagonal.Sigmas(np.array([ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 1.0]))
244
+ betweenNoise = gtsam.noiseModel.Diagonal.Sigmas(
245
+ np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 10])
246
+ )
247
+ betweenNoise2 = gtsam.noiseModel.Diagonal.Sigmas(
248
+ np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 1.0])
249
+ )
165
250
  b1 = BetweenFactorSimilarity3(1, 2, m1, betweenNoise)
166
251
  b2 = BetweenFactorSimilarity3(2, 3, m2, betweenNoise)
167
252
  b3 = BetweenFactorSimilarity3(3, 4, m3, betweenNoise)
@@ -178,13 +263,20 @@ class TestSim3(GtsamTestCase):
178
263
  graph.add(lc)
179
264
 
180
265
  # graph.print("Full Graph\n");
181
- initial=Values()
266
+ initial = Values()
182
267
  initial.insert(1, prior)
183
- initial.insert(2, Similarity3(Rot3.Ypr(np.pi / 2.0, 0, 0), Point3(1, 0, 0), 1.1))
184
- initial.insert(3, Similarity3(Rot3.Ypr(2.0 * np.pi / 2.0, 0, 0), Point3(0.9, 1.1, 0), 1.2))
185
- initial.insert(4, Similarity3(Rot3.Ypr(3.0 * np.pi / 2.0, 0, 0), Point3(0, 1, 0), 1.3))
186
- initial.insert(5, Similarity3(Rot3.Ypr(4.0 * np.pi / 2.0, 0, 0), Point3(0, 0, 0), 1.0))
187
-
268
+ initial.insert(
269
+ 2, Similarity3(Rot3.Ypr(np.pi / 2.0, 0, 0), Point3(1, 0, 0), 1.1)
270
+ )
271
+ initial.insert(
272
+ 3, Similarity3(Rot3.Ypr(2.0 * np.pi / 2.0, 0, 0), Point3(0.9, 1.1, 0), 1.2)
273
+ )
274
+ initial.insert(
275
+ 4, Similarity3(Rot3.Ypr(3.0 * np.pi / 2.0, 0, 0), Point3(0, 1, 0), 1.3)
276
+ )
277
+ initial.insert(
278
+ 5, Similarity3(Rot3.Ypr(4.0 * np.pi / 2.0, 0, 0), Point3(0, 0, 0), 1.0)
279
+ )
188
280
 
189
281
  result = LevenbergMarquardtOptimizer(graph, initial).optimizeSafely()
190
282
  self.gtsamAssertEquals(Similarity3(0.7), result.atSimilarity3(5), 0.4)
@@ -695,11 +787,15 @@ class TestSim3(GtsamTestCase):
695
787
  wTi = unaligned_pose_dict.get(i, None)
696
788
  unaligned_pose_list.append(wTi)
697
789
  # GT poses are the reference/target
698
- rSe = align_poses_sim3_ignore_missing(aTi_list=poses_gt, bTi_list=unaligned_pose_list)
790
+ rSe = align_poses_sim3_ignore_missing(
791
+ aTi_list=poses_gt, bTi_list=unaligned_pose_list
792
+ )
699
793
  assert rSe.scale() >= 0
700
794
 
701
795
 
702
- def align_poses_sim3_ignore_missing(aTi_list: List[Optional[Pose3]], bTi_list: List[Optional[Pose3]]) -> Similarity3:
796
+ def align_poses_sim3_ignore_missing(
797
+ aTi_list: List[Optional[Pose3]], bTi_list: List[Optional[Pose3]]
798
+ ) -> Similarity3:
703
799
  """Align by similarity transformation, but allow missing estimated poses in the input.
704
800
 
705
801
  Note: this is a wrapper for align_poses_sim3() that allows for missing poses/dropped cameras.
@@ -763,7 +859,9 @@ def align_poses_sim3(aTi_list: List[Pose3], bTi_list: List[Pose3]) -> Similarity
763
859
 
764
860
  # fit a single translation motion to the centroid
765
861
  aTi_centroid = np.array([aTi.translation() for aTi in aTi_list]).mean(axis=0)
766
- aTi_rot_aligned_centroid = np.array([aTi.translation() for aTi in aTi_list_rot_aligned]).mean(axis=0)
862
+ aTi_rot_aligned_centroid = np.array(
863
+ [aTi.translation() for aTi in aTi_list_rot_aligned]
864
+ ).mean(axis=0)
767
865
 
768
866
  # construct the final SIM3 transform
769
867
  aSb = Similarity3(aSb.rotation(), aTi_centroid - aTi_rot_aligned_centroid, 1.0)
@@ -0,0 +1,100 @@
1
+ """
2
+ Generates a CSV file with ground-truth trajectory data for testing a
3
+ DiscreteScenario in GTSAM.
4
+
5
+ The trajectory is a simple horizontal circle at a constant height and speed.
6
+
7
+ CSV Format:
8
+ timestamp,px,py,pz,qw,qx,qy,qz,vx,vy,vz,omegax,omegay,omegaz,ax,ay,az
9
+ """
10
+ import numpy as np
11
+ import csv
12
+ from gtsam import Rot3
13
+
14
+ # --- Trajectory Parameters ---
15
+ RADIUS = 5.0 # meters
16
+ HEIGHT = 1.0 # meters
17
+ OMEGA_Z = 0.2 # rad/s (angular velocity around Z-axis)
18
+ DURATION = 20.0 # seconds
19
+ FREQUENCY = 100 # Hz
20
+ FILENAME = "trajectory.csv"
21
+
22
+
23
+ def generate_data():
24
+ """Generates and saves the trajectory data."""
25
+
26
+ # Time vector
27
+ t = np.arange(0.0, DURATION, 1.0 / FREQUENCY)
28
+
29
+ # Angle at each time step
30
+ angle = OMEGA_Z * t
31
+
32
+ # Position (p_n) in navigation frame
33
+ px = RADIUS * np.cos(angle)
34
+ py = RADIUS * np.sin(angle)
35
+ pz = np.full_like(t, HEIGHT)
36
+
37
+ # Velocity (v_n) in navigation frame (derivative of position)
38
+ speed = RADIUS * OMEGA_Z
39
+ vx = -speed * np.sin(angle)
40
+ vy = speed * np.cos(angle)
41
+ vz = np.zeros_like(t)
42
+
43
+ # Acceleration (a_n) in navigation frame (derivative of velocity)
44
+ # This is the centripetal acceleration.
45
+ ax = -speed * OMEGA_Z * np.cos(angle)
46
+ ay = -speed * OMEGA_Z * np.sin(angle)
47
+ az = np.zeros_like(t)
48
+
49
+ # Angular velocity (omega_b) in the body frame
50
+ # The body is only rotating around its z-axis to face forward.
51
+ omegax = np.zeros_like(t)
52
+ omegay = np.zeros_like(t)
53
+ omegaz = np.full_like(t, OMEGA_Z)
54
+
55
+ # Orientation (quaternion q_n_b)
56
+ # We use gtsam.Rot3 to easily get the quaternion
57
+ quaternions = [Rot3.Yaw(a).toQuaternion() for a in angle]
58
+
59
+ # --- Write to CSV ---
60
+ header = [
61
+ 'timestamp',
62
+ 'px',
63
+ 'py',
64
+ 'pz', # Position
65
+ 'qw',
66
+ 'qx',
67
+ 'qy',
68
+ 'qz', # Quaternion (orientation)
69
+ 'vx',
70
+ 'vy',
71
+ 'vz', # Velocity (nav frame)
72
+ 'omegax',
73
+ 'omegay',
74
+ 'omegaz', # Angular velocity (body frame)
75
+ 'ax',
76
+ 'ay',
77
+ 'az' # Acceleration (nav frame)
78
+ ]
79
+
80
+ with open(FILENAME, 'w', newline='') as f:
81
+ writer = csv.writer(f)
82
+ writer.writerow(header)
83
+
84
+ for i in range(len(t)):
85
+ q = quaternions[i]
86
+ row = [
87
+ t[i], px[i], py[i], pz[i],
88
+ q.w(),
89
+ q.x(),
90
+ q.y(),
91
+ q.z(), vx[i], vy[i], vz[i], omegax[i], omegay[i], omegaz[i],
92
+ ax[i], ay[i], az[i]
93
+ ]
94
+ writer.writerow(row)
95
+
96
+ print(f"Successfully generated trajectory data at '{FILENAME}'")
97
+
98
+
99
+ if __name__ == "__main__":
100
+ generate_data()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gtsam-develop
3
- Version: 4.3a0.dev202506050756
3
+ Version: 4.3a0.dev202507301459
4
4
  Summary: Georgia Tech Smoothing And Mapping library
5
5
  Home-page: https://gtsam.org/
6
6
  Author: Frank Dellaert et. al.