15 measurement_noise_model,
23 """Generates ground truth and simulated measurements for SLAM.
26 num_landmarks: Number of landmarks to generate.
27 world_size: Size of the square world environment.
28 robot_radius: Radius of the robot's circular path.
29 robot_angular_vel: Angular velocity of the robot (rad/step).
30 num_steps: Number of simulation steps.
31 dt: Time step duration.
32 odometry_noise_model: GTSAM noise model for odometry.
33 measurement_noise_model: GTSAM noise model for bearing-range.
34 max_sensor_range: Maximum range of the bearing-range sensor.
35 X: GTSAM symbol shorthand function for poses.
36 L: GTSAM symbol shorthand function for landmarks.
37 odom_seed: Random seed for odometry noise.
38 meas_seed: Random seed for measurement noise.
39 landmark_seed: Random seed for landmark placement.
43 - landmarks_gt_dict (dict): L(i) -> gtsam.Point2 ground truth.
44 - poses_gt (list): List of gtsam.Pose2 ground truth poses.
45 - odometry_measurements (list): List of noisy gtsam.Pose2 odometry.
46 - measurements_sim (list): List of lists, measurements_sim[k] contains
47 tuples (L(lm_id), bearing, range) for step k.
48 - landmarks_gt_array (np.array): 2xN numpy array of landmark positions.
50 np.random.seed(landmark_seed)
51 odometry_noise_sampler =
gtsam.Sampler(odometry_noise_model, odom_seed)
52 measurement_noise_sampler =
gtsam.Sampler(measurement_noise_model, meas_seed)
55 landmarks_gt_array = (np.random.rand(2, num_landmarks) - 0.5) * world_size
62 current_pose_gt =
gtsam.Pose2(robot_radius, 0, np.pi / 2)
63 poses_gt.append(current_pose_gt)
65 for _
in range(num_steps):
66 delta_theta = robot_angular_vel * dt
67 arc_length = robot_angular_vel * robot_radius * dt
68 motion_command =
gtsam.Pose2(arc_length, 0, delta_theta)
69 current_pose_gt = current_pose_gt.compose(motion_command)
70 poses_gt.append(current_pose_gt)
73 odometry_measurements = []
74 for k
in range(num_steps):
76 pose_k1 = poses_gt[k + 1]
77 true_odom = pose_k.between(pose_k1)
80 odom_noise_vec = odometry_noise_sampler.sample()
81 noisy_odom = true_odom.compose(
82 gtsam.Pose2(odom_noise_vec[0], odom_noise_vec[1], odom_noise_vec[2])
84 odometry_measurements.append(noisy_odom)
87 measurements_sim = [[]
for _
in range(num_steps + 1)]
88 for k
in range(num_steps + 1):
89 robot_pose = poses_gt[k]
90 for lm_id
in range(num_landmarks):
91 lm_gt_pt = landmarks_gt_dict[
L(lm_id)]
93 measurement_factor = gtsam.BearingRangeFactor2D(
96 robot_pose.bearing(lm_gt_pt),
97 robot_pose.range(lm_gt_pt),
98 measurement_noise_model,
100 true_range = measurement_factor.measured().
range()
101 true_bearing = measurement_factor.measured().bearing()
105 true_range <= max_sensor_range
106 and abs(true_bearing.theta()) < np.pi / 2
109 noise_vec = measurement_noise_sampler.sample()
110 noisy_bearing = true_bearing.retract(
111 np.array([noise_vec[0]])
113 noisy_range = true_range + noise_vec[1]
116 measurements_sim[k].append(
117 (
L(lm_id), noisy_bearing, noisy_range)
119 except Exception
as e:
124 print(f
"Simulation Generated: {num_landmarks} landmarks.")
126 f
"Simulation Generated: {num_steps + 1} ground truth poses and {num_steps} odometry measurements."
128 num_meas_total = sum(
len(m_list)
for m_list
in measurements_sim)
129 print(f
"Simulation Generated: {num_meas_total} bearing-range measurements.")
134 odometry_measurements,