Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
cffde30
Added homework description and starter code
krishauser Jan 27, 2025
efc06c9
Merged with s2025
Jan 28, 2025
97a3de8
Merge branch 'main' into s2025_teamB
krishauser Jan 29, 2025
7c35a51
test
rjsun06 Jan 31, 2025
bcf2949
test
rjsun06 Jan 31, 2025
c76d058
Example commit for planning team A
animeshsingh98 Feb 1, 2025
be692a1
Fix setup issues and add user to the docker image
harishkumarbalaji Feb 2, 2025
9206116
Dummy commit to trigger sonarqube
harishkumarbalaji Feb 2, 2025
0fcdff4
Fix the requrements.txt to take from the current repo
harishkumarbalaji Feb 2, 2025
cff1e29
Update the docker compose service name
harishkumarbalaji Feb 2, 2025
0242671
Merge pull request #99 from krishauser/A4_Planning_PReg
udymd Feb 2, 2025
2a75f28
script for capturing paird img/points
rjsun06 Feb 2, 2025
753e5ba
Update missing depedencies and ultralytics python packagein requirements
harishkumarbalaji Feb 3, 2025
a7a2e8c
Add. Instructions to check if the container toolkit is working as exp…
harishkumarbalaji Feb 3, 2025
1f6fb93
Update. Readme with latest depedencies
harishkumarbalaji Feb 3, 2025
6618e8f
Update. Readme with post install steps
harishkumarbalaji Feb 3, 2025
a871397
Fixed launch file to work with standard perception pipeline
krishauser Feb 3, 2025
be315de
Fixed diff that was somehow committed
krishauser Feb 3, 2025
7a66dc1
Merge branch 's2025' into s2025_teamB
krishauser Feb 3, 2025
38deb79
Merge pull request #98 from krishauser/infra-b/fix_setup_issues
krishauser Feb 3, 2025
6f18feb
Update volume mounting to access the files
harishkumarbalaji Feb 3, 2025
ca430be
Merge pull request #101 from krishauser/infra-b/update_missing_depede…
krishauser Feb 3, 2025
cabe2fc
updating some ros topics to match gem e4
AadarshHegde123 Feb 4, 2025
8368354
Merge branch 'EstCal_A' of https://github.com/krishauser/GEMstack int…
rjsun06 Feb 4, 2025
b39e690
Update capture_ouster_oak.py
rjsun06 Feb 4, 2025
88bdc23
changed node name for data capturing
rjsun06 Feb 4, 2025
26f0714
Merge branch 's2025_teamA' into A4_planning
udymd Feb 4, 2025
8725e34
Update longitudinal_planning.py
udymd Feb 4, 2025
9f736bc
Merge branch 's2025_teamA' into EstCal_A
AadarshHegde123 Feb 5, 2025
2b6830c
Add Yielding planning, just stop in front of pedestrian
udymd Feb 5, 2025
63cd8f6
Fix the poses (align to absolute pose)
udymd Feb 5, 2025
e105880
lidar front camera data capture working, camera info intrinsic captur…
Feb 5, 2025
96a315e
Fixed fake_fake simulation calls and rename from YIELD -> YIELDING
krishauser Feb 6, 2025
7659c5d
Longitudinal planning setup where we the points are treated as way po…
SimonKato Feb 6, 2025
9aacb10
Fixed Nan error, modified Brake mode
udymd Feb 7, 2025
8059c53
Fix bug causing crash at end of sim
udymd Feb 7, 2025
df4ff9a
Merge branch 's2025_teamA' into A4_planning
udymd Feb 7, 2025
67348fe
More/better comments
SimonKato Feb 7, 2025
403e71c
Merge branch 'A4_planning_part1_milestones' of github.com:krishauser/…
SimonKato Feb 7, 2025
f91796b
Part1 based on V_max and Triangle profile
RohitMurali18 Feb 8, 2025
15191c7
general code clean up + adding depth subscriber + creating klampt vis…
AadarshHegde123 Feb 8, 2025
2a0f9c2
working depth image capture
AadarshHegde123 Feb 8, 2025
d0771fa
visualization for lidar + oak working, but no intrinsics considered
AadarshHegde123 Feb 10, 2025
3c6bdc1
feat: init team folder
Averyyy Feb 10, 2025
e23a5cb
script to fit objects in a line
rjsun06 Feb 11, 2025
555cb55
calibration Lidar to Vehicle. Running OK. To be improved
rjsun06 Feb 11, 2025
734f0b3
update translation_z using magic numbers we measured
rjsun06 Feb 11, 2025
6a38ed0
enhenced visualization
rjsun06 Feb 11, 2025
88ec6ba
semi correct code for icp
jeffbyju Feb 11, 2025
a9fa4c8
better line fitting
rjsun06 Feb 11, 2025
124a75b
good visuals
rjsun06 Feb 12, 2025
34f89eb
Found a bug in longitutdinal_planning which used deceleration instead…
SimonKato Feb 12, 2025
c914a41
Little twick for rx,ry calculation
rjsun06 Feb 13, 2025
a84c3e9
Merge branch 'EstCal_A' of https://github.com/krishauser/GEMstack int…
rjsun06 Feb 13, 2025
ba645ed
Your commit message
RohitMurali18 Feb 13, 2025
d2235bc
after dynamic dt
RohitMurali18 Feb 13, 2025
299768a
Fixed aborting at the end of sim.
udymd Feb 13, 2025
d3d3bf0
Merge branch 'A1_control' into s2025_teamA_control-planning
udymd Feb 13, 2025
03f46c1
Fix jitter at the end of track for simulation
udymd Feb 13, 2025
bd94404
feat: remove ibeo msgs in setup_this machine.sh
Averyyy Feb 14, 2025
7dd38e3
Modified braking function for experiment
udymd Feb 14, 2025
0831c67
Implemented Part 1&2
udymd Feb 14, 2025
0f38270
manual cali lidar to camera
rjsun06 Feb 14, 2025
b21bb65
add logic to avoid unnecessary yielding
Patrick8894 Feb 14, 2025
ca087e3
dumb bug fixed
rjsun06 Feb 14, 2025
1b82a7b
Merge branch 'EstCal_A' into s2025_teamA_control-planning
udymd Feb 14, 2025
ade0559
Modified braking function for experiment
udymd Feb 14, 2025
a825826
Merge branch 's2025_teamA_control-planning' of https://github.com/kri…
udymd Feb 14, 2025
46b6b04
modify code for integrating with other algorithm
Patrick8894 Feb 14, 2025
2b6c1d8
longitudinal plan now taking into account triangle velocity profile c…
SimonKato Feb 14, 2025
20b73b1
Adding cruising speed profile
animeshsingh98 Feb 14, 2025
450a2f8
Temporary fix of variables
udymd Feb 14, 2025
ca68b4e
Merge branch 'A4_planning_part1' into A4_planning_patrick
udymd Feb 14, 2025
6b276ed
Merging and consolidating part 1 branches
SimonKato Feb 14, 2025
b9f9e0e
Add condition that vehicle accels and ped walk to vehicle
udymd Feb 15, 2025
2872e78
Optimising the program
animeshsingh98 Feb 15, 2025
00ddf95
Update longitudinal_planning.py
animeshsingh98 Feb 15, 2025
52e56c3
tmp
Hansen1030 Feb 15, 2025
4b89d5b
Add dx
Hansen1030 Feb 15, 2025
32336da
Fixed syntax errors
udymd Feb 16, 2025
14fcec9
Merge branch 'A4_planning_part1' into A4_planning_patrick
udymd Feb 17, 2025
503a403
Fixed ROS type typo
krishauser Feb 17, 2025
4ba2efd
Merge branch 's2025' into s2025_teamA_control-planning
udymd Feb 17, 2025
080f8b0
Merge pull request #115 from krishauser/A1_control
alo-20 Feb 17, 2025
3020680
Optimising the program and adding commit by Kris
animeshsingh98 Feb 17, 2025
0cac268
Optimising the program and adding commit by Kris
animeshsingh98 Feb 17, 2025
28e22dc
Merge pull request #108 from krishauser/remove-ibeo-msg
Averyyy Feb 17, 2025
0300ba9
merge part1 longitudinal_plan and longitudinal_brake with part2 yeild…
Patrick8894 Feb 17, 2025
5755476
Merge branch 'A4_planning_patrick' of github.com:krishauser/GEMstack …
Feb 17, 2025
e5a2232
Revert "merge part1 longitudinal_plan and longitudinal_brake with par…
Patrick8894 Feb 17, 2025
bca6233
merge part1 and part2
Patrick8894 Feb 17, 2025
f3804db
blink_component file test
laneccolin Feb 17, 2025
b36a49b
Merge branch 'main' into s2025
krishauser Feb 18, 2025
7568b2c
Merge branch 's2025' into s2025_teamA
krishauser Feb 18, 2025
3a8a282
Merge branch 'A4_planning' into s2025_teamA_control-planning
udymd Feb 19, 2025
466d887
Adjust Change dynamic_dt_k for simulation
udymd Feb 19, 2025
ef3a29a
Merge branch 's2025_teamA' into s2025_teamA_control-planning
udymd Feb 19, 2025
404e393
Merge branch 'A1_control' into s2025_teamA_control-planning
udymd Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,7 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.vscode/
setup/zed_sdk.run
cuda/
Binary file added GEMstack/.DS_Store
Binary file not shown.
3 changes: 1 addition & 2 deletions GEMstack/knowledge/vehicle/gem_e4_sensor_environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ ros_topics:
top_lidar: /ouster/points
front_camera: /oak/rgb/image_raw
front_depth: /oak/stereo/image_raw
gnss: /septentrio_gnss/insnavgeod

gnss: /novatel/inspva
41 changes: 41 additions & 0 deletions GEMstack/offboard/calibration/camera_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ROS Headers
import rospy
from sensor_msgs.msg import Image,PointCloud2, CameraInfo
import sensor_msgs.point_cloud2 as pc2
import ctypes
import struct
import pickle
import image_geometry

import numpy as np
import os
import time

camera_image = None

def camera_callback(info : CameraInfo):
global camera_image
camera_image = info

def get_intrinsics():
model = image_geometry.PinholeCameraModel()
model.fromCameraInfo(camera_image)
print(model.intrinsicMatrix())

def main(folder='data',start_index=1):
rospy.init_node("capture_cam_info",disable_signals=True)
caminfo_sub = rospy.Subscriber("/oak/rgb/camera_info", CameraInfo, camera_callback)
while True:
if camera_image:
time.sleep(1)
get_intrinsics()

if __name__ == '__main__':
import sys
folder = 'data'
start_index = 1
if len(sys.argv) >= 2:
folder = sys.argv[1]
if len(sys.argv) >= 3:
start_index = int(sys.argv[2])
main(folder,start_index)
102 changes: 102 additions & 0 deletions GEMstack/offboard/calibration/capture_ouster_oak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# ROS Headers
import rospy
from sensor_msgs.msg import Image,PointCloud2
import sensor_msgs.point_cloud2 as pc2
import ctypes
import struct

# OpenCV and cv2 bridge
import cv2
from cv_bridge import CvBridge
import numpy as np
import os
import time

lidar_points = None
camera_image = None
depth = None
depth_image = None
bridge = CvBridge()

def lidar_callback(lidar : PointCloud2):
global lidar_points
lidar_points = lidar

def camera_callback(img : Image):
global camera_image
camera_image = img

def depth_callback(img : Image):
global depth_image
depth_image = img

def pc2_to_numpy(pc2_msg, want_rgb = False):
gen = pc2.read_points(pc2_msg, skip_nans=True)
if want_rgb:
xyzpack = np.array(list(gen),dtype=np.float32)
if xyzpack.shape[1] != 4:
raise ValueError("PointCloud2 does not have points")
xyzrgb = np.empty((xyzpack.shape[0],6))
xyzrgb[:,:3] = xyzpack[:,:3]
for i,x in enumerate(xyzpack):
rgb = x[3]
# cast float32 to int so that bitwise operations are possible
s = struct.pack('>f' ,rgb)
i = struct.unpack('>l',s)[0]
# you can get back the float value by the inverse operations
pack = ctypes.c_uint32(i).value
r = (pack & 0x00FF0000)>> 16
g = (pack & 0x0000FF00)>> 8
b = (pack & 0x000000FF)
#r,g,b values in the 0-255 range
xyzrgb[i,3:] = (r,g,b)
return xyzrgb
else:
return np.array(list(gen),dtype=np.float32)[:,:3]

def save_scan(lidar_fn,color_fn,depth_fn):
print("Saving scan to",lidar_fn,color_fn)

pc = pc2_to_numpy(lidar_points,want_rgb=False) # convert lidar feed to numpy
np.savez(lidar_fn,pc)
cv2.imwrite(color_fn,bridge.imgmsg_to_cv2(camera_image))

dimage = bridge.imgmsg_to_cv2(depth_image)
dimage_non_nan = dimage[np.isfinite(dimage)]
print("Depth range",np.min(dimage_non_nan),np.max(dimage_non_nan))
dimage = np.nan_to_num(dimage,nan=0,posinf=0,neginf=0)
dimage = (dimage/4000*0xffff)
print("Depth pixel range",np.min(dimage),np.max(dimage))
dimage = dimage.astype(np.uint16)
cv2.imwrite(depth_fn,dimage)

def main(folder='data',start_index=1):
rospy.init_node("capture_ouster_oak",disable_signals=True)
lidar_sub = rospy.Subscriber("/ouster/points", PointCloud2, lidar_callback)
camera_sub = rospy.Subscriber("/oak/rgb/image_raw", Image, camera_callback)
depth_sub = rospy.Subscriber("/oak/stereo/image_raw", Image, depth_callback)
index = 0
print(" Storing lidar point clouds as npz")
print(" Storing color images as png")
print(" Storing depth images as tif")
print(" Ctrl+C to quit")
while True:
if camera_image and depth_image:
cv2.imshow("result",bridge.imgmsg_to_cv2(camera_image))
time.sleep(.5)
files = [
os.path.join(folder,'lidar{}.npz'.format(index)),
os.path.join(folder,'color{}.png'.format(index)),
os.path.join(folder,'depth{}.tif'.format(index))]
save_scan(*files)
index += 1

if __name__ == '__main__':
import sys
folder = 'data'
start_index = 1
if len(sys.argv) >= 2:
folder = sys.argv[1]
if len(sys.argv) >= 3:
start_index = int(sys.argv[2])
main(folder,start_index)
179 changes: 179 additions & 0 deletions GEMstack/offboard/calibration/icp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/usr/bin/env python3

import os
import glob
import numpy as np
import cv2
import open3d as o3d

# --- Adjust these to match your intrinsics and data details ---
INTRINSICS = np.array([
[684.83331299, 0. , 573.37109375],
[ 0. , 684.60968018, 363.70092773],
[ 0. , 0. , 1. ]
], dtype=np.float32)

# Depth scale: how to go from raw .tif values to actual "Z" in meters.
# In your capture code, you used: dimage = (dimage/4000*0xffff)
# Possibly each pixel is stored with range 0..65535. Adjust as needed.
DEPTH_SCALE = 4000.0 # Example factor if your depth was in mm or a certain scale.

def depth_to_points(depth_img: np.ndarray, intrinsics: np.ndarray):
"""
Convert a single-channel depth image into an Nx3 array of 3D points
in the camera coordinate system.
- depth_img: 2D array of type uint16 or float with depth values
- intrinsics: 3x3 camera matrix
"""
fx = intrinsics[0,0]
fy = intrinsics[1,1]
cx = intrinsics[0,2]
cy = intrinsics[1,2]

# Indices of each pixel
h, w = depth_img.shape
i_range = np.arange(h)
j_range = np.arange(w)
jj, ii = np.meshgrid(j_range, i_range) # shape (h,w)

# Flatten
ii = ii.flatten().astype(np.float32)
jj = jj.flatten().astype(np.float32)
depth = depth_img.flatten().astype(np.float32)

# Filter out zero / invalid depths
valid_mask = (depth > 0)
ii = ii[valid_mask]
jj = jj[valid_mask]
depth = depth[valid_mask]

# Reproject to 3D (camera frame)
# X = (x - cx) * Z / fx, Y = (y - cy) * Z / fy, Z = depth
# Note: Z must be in meters or consistent units
z = depth / DEPTH_SCALE # Convert from your .tif scale to real meters
x = (jj - cx) * z / fx
y = (ii - cy) * z / fy
points3d = np.stack((x, y, z), axis=-1) # shape (N,3)

return points3d

def load_lidar_points(npz_file: str):
"""
Load the Nx3 LiDAR points from a .npz file created by 'np.savez'.
"""
data = np.load(npz_file)
# The capture code used: np.savez(lidar_fn, pc)
# So 'data' might have the default key 'arr_0' or 'pc' if named
# Inspect data.files to see. Let's assume 'arr_0' or single key:
arr_key = data.files[0]
points = data[arr_key]
# shape check, must be Nx3 or Nx4, ...
return points

def make_open3d_pcd(points: np.ndarray):
"""
Convert Nx3 numpy array into an Open3D point cloud object.
"""
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
return pcd

def perform_icp(camera_pcd: o3d.geometry.PointCloud,
lidar_pcd: o3d.geometry.PointCloud):
"""
Perform local ICP alignment of camera_pcd (source) to lidar_pcd (target).
Returns a transformation 4x4 that maps camera -> lidar (or vice versa).
"""
# 1) Estimate normals if you want point-to-plane
lidar_pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=1.0, max_nn=30))
camera_pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=1.0, max_nn=30))

# 2) Initial guess: identity or something better if you have one
init_guess = np.eye(4)

# 3) Choose a threshold for inlier distance
threshold = 0.5 # adjust based on your environment scale

# 4) Run ICP (point-to-plane or point-to-point)
print("[ICP] Running ICP alignment ...")
result = o3d.pipelines.registration.registration_icp(
camera_pcd, # source
lidar_pcd, # target
threshold,
init_guess,
estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPlane()
)

print("[ICP] Done. Fitness = %.4f, RMSE = %.4f" % (result.fitness, result.inlier_rmse))
print("[ICP] Transformation:\n", result.transformation)
return result.transformation

def main(folder='data'):
color_files = sorted(glob.glob(os.path.join(folder, "color*.png")))
icp_results = [] # to store (index, transform, fitness, rmse)

for color_path in color_files:
# Extract index from filename, e.g. color10.png -> 10
basename = os.path.basename(color_path)
idx_str = basename.replace("color","").replace(".png","")

if int(idx_str) in range(77):
depth_path = os.path.join(folder, f"depth{idx_str}.tif")
lidar_path = os.path.join(folder, f"lidar{idx_str}.npz")

if not (os.path.exists(depth_path) and os.path.exists(lidar_path)):
continue

# Load depth / convert to 3D
depth_img = cv2.imread(depth_path, cv2.IMREAD_UNCHANGED)
if depth_img is None:
continue
camera_points = depth_to_points(depth_img, INTRINSICS)
camera_pcd = make_open3d_pcd(camera_points)

# Load LiDAR
lidar_points = load_lidar_points(lidar_path)
lidar_pcd = make_open3d_pcd(lidar_points)

lidar_pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=1.0, # Adjust based on your scene scale
max_nn=30
)
)

# Also estimate normals on the Camera (source) cloud (recommended)
camera_pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(
radius=1.0,
max_nn=30
)
)

# Now you can run Point-to-Plane ICP
init_guess = np.eye(4)
threshold = 0.5 # or some appropriate distance
result_icp = o3d.pipelines.registration.registration_icp(
camera_pcd,
lidar_pcd,
threshold,
init_guess,
o3d.pipelines.registration.TransformationEstimationPointToPlane()
)

print("ICP result:", result_icp.transformation)
print("Fitness:", result_icp.fitness, " RMSE:", result_icp.inlier_rmse)

# After processing all frames, we can analyze or average
if len(icp_results) > 0:
# Example: pick the best frame by highest fitness
best_frame = max(icp_results, key=lambda x: x[2]) # x[2] is fitness
print("\nBest frame by fitness was index=", best_frame[0],
" with fitness=", best_frame[2], " rmse=", best_frame[3])
else:
print("No frames processed properly.")

if __name__ == "__main__":
main()
Loading