From 215b044ff01149208a94ae77e22af272567928e2 Mon Sep 17 00:00:00 2001 From: shb-png Date: Mon, 11 May 2026 22:05:13 +0000 Subject: [PATCH 01/17] separate launch scripts --- ...gup_pi.launch.py => bringup_all.launch.py} | 0 .../launch/bringup_jetson.launch.py | 80 +++++++++++++++++++ .../launch/bringup_rubik.launch.py | 32 ++++++++ 3 files changed, 112 insertions(+) rename src/autonomous_kart/autonomous_kart/launch/{bringup_pi.launch.py => bringup_all.launch.py} (100%) create mode 100644 src/autonomous_kart/autonomous_kart/launch/bringup_jetson.launch.py create mode 100644 src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_pi.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_all.launch.py similarity index 100% rename from src/autonomous_kart/autonomous_kart/launch/bringup_pi.launch.py rename to src/autonomous_kart/autonomous_kart/launch/bringup_all.launch.py diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_jetson.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_jetson.launch.py new file mode 100644 index 00000000..b63d91cd --- /dev/null +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_jetson.launch.py @@ -0,0 +1,80 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, SetParameter, SetParametersFromFile +from ament_index_python.packages import get_package_share_directory +import os + + +def generate_launch_description(): + pkg_share = get_package_share_directory("autonomous_kart") + + camera_yaml = os.path.join(pkg_share, "params", "camera.yaml") + gps_yaml = os.path.join(pkg_share, "params", "gps.yaml") + safety_yaml = os.path.join(pkg_share, "params", "safety.yaml") + system_yaml = os.path.join(pkg_share, "params", "system.yaml") + pathfinder_yaml = os.path.join(pkg_share, "params", "pathfinder.yaml") + localization_yaml = os.path.join(pkg_share, "params", "localization.yaml") + imu_yaml = os.path.join(pkg_share, "params", "imu.yaml") + e_comms_yaml = os.path.join(pkg_share, "params", "e_comms.yaml") + + sim_mode = LaunchConfiguration("simulation_mode") + + return LaunchDescription( + [ + DeclareLaunchArgument("simulation_mode", default_value="false"), + GroupAction( + [ + SetParametersFromFile(system_yaml), + SetParameter(name="simulation_mode", value=sim_mode), + Node( + package="autonomous_kart", + executable="camera_node", + name="camera_node", + parameters=[camera_yaml], + ), + Node( + package="autonomous_kart", + executable="pathfinder_node", + name="pathfinder_node", + parameters=[pathfinder_yaml, safety_yaml], + ), + Node( + package="autonomous_kart", + executable="opencv_pathfinder_node", + name="opencv_pathfinder_node", + parameters=[gps_yaml], + ), + Node( + package="autonomous_kart", + executable="master_api", + name="master_api", + ), + Node( + package="autonomous_kart", + executable="localization_node", + name="localization_node", + parameters=[pathfinder_yaml, localization_yaml], + ), + Node( + package="autonomous_kart", + executable="e_comms_node", + name="e_comms_node", + parameters=[e_comms_yaml], + ), + Node( + package="autonomous_kart", + executable="gps_node", + name="gps_node", + parameters=[gps_yaml], + ), + Node( + package="autonomous_kart", + executable="imu_node", + name="imu_node", + parameters=[imu_yaml], + ), + ] + ), + ] + ) diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py new file mode 100644 index 00000000..8c69847e --- /dev/null +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py @@ -0,0 +1,32 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, SetParameter, SetParametersFromFile +from ament_index_python.packages import get_package_share_directory +import os + + +def generate_launch_description(): + pkg_share = get_package_share_directory("autonomous_kart") + + system_yaml = os.path.join(pkg_share, "params", "system.yaml") + + sim_mode = LaunchConfiguration("simulation_mode") + + return LaunchDescription( + [ + DeclareLaunchArgument("simulation_mode", default_value="false"), + GroupAction( + [ + SetParametersFromFile(system_yaml), + SetParameter(name="simulation_mode", value=sim_mode), + + Node( + package="autonomous_kart", + executable="metrics_node", + name="metrics_node", + ), + ] + ), + ] + ) From 13cbbb45d480ebcabd653128e55e29a6302387f9 Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 15:09:29 +0000 Subject: [PATCH 02/17] ignore logs --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2734fac9..a054c13f 100644 --- a/.gitignore +++ b/.gitignore @@ -176,4 +176,6 @@ cython_debug/ # Ros build ignored install log +logs +kart_logs build From 50476aa78d5b5a2598848f0777702747357a985d Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 18:51:17 +0000 Subject: [PATCH 03/17] move rosbags to exclusively rubik bringup --- scripts/start.bash | 20 +---------------- .../launch/bringup_rubik.launch.py | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/scripts/start.bash b/scripts/start.bash index af0722e6..e73cf390 100755 --- a/scripts/start.bash +++ b/scripts/start.bash @@ -9,22 +9,4 @@ pkill -f master_api || true # Lat/long are approx west lafayette, not important to be highly accurate mkdir -p /ws/logs -str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & - -# Logs supervisor - crash tolerant -( - while true; do - RUN_NAME="run_$(date -u +%Y%m%d_%H%M%S)" - ros2 bag record -a \ - --storage mcap \ - --storage-preset-profile zstd_fast \ - --max-bag-duration 60 \ - --exclude '/camera/.*' \ - --output "/ws/logs/$RUN_NAME" \ - >> /ws/logs/recorder.log 2>&1 - echo "[$(date -u +%FT%TZ)] recorder exited, restarting in 1s" >> /ws/logs/recorder.log - sleep 1 - done -) & - -sleep infinity \ No newline at end of file +str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & \ No newline at end of file diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py index 8c69847e..58a3190a 100644 --- a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py @@ -1,17 +1,31 @@ from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, GroupAction +from launch.actions import DeclareLaunchArgument, GroupAction, ExecuteProcess from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node, SetParameter, SetParametersFromFile from ament_index_python.packages import get_package_share_directory +from datetime import datetime import os def generate_launch_description(): pkg_share = get_package_share_directory("autonomous_kart") - system_yaml = os.path.join(pkg_share, "params", "system.yaml") - + run_name = datetime.now().strftime("run_%Y%m%d_%H%M%S") + bag_output_path = os.path.join("/ws/logs", run_name) sim_mode = LaunchConfiguration("simulation_mode") + rosbag_recorder = ExecuteProcess( + cmd=[ + 'ros2', 'bag', 'record', '-a', + '--storage', 'mcap', + '--storage-preset-profile', 'zstd_fast', + '--max-bag-duration', '60', + '--exclude', '/camera/.*', + '--output', bag_output_path + ], + output='screen', + respawn=True, + respawn_delay=1.0 + ) return LaunchDescription( [ @@ -21,6 +35,8 @@ def generate_launch_description(): SetParametersFromFile(system_yaml), SetParameter(name="simulation_mode", value=sim_mode), + rosbag_recorder, + Node( package="autonomous_kart", executable="metrics_node", From 8926fdd9ab4bf5825506be9d6044ea01e9711294 Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 15:05:48 -0400 Subject: [PATCH 04/17] ROS_LOCALHOST_ONLY = 0 --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 36a8b6e3..198b41ad 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "workspaceFolder": "/ws", "containerEnv": { "ROS_DOMAIN_ID": "42", - "ROS_LOCALHOST_ONLY": "1", + "ROS_LOCALHOST_ONLY": "0", "CYCLONEDDS_URI": "file:///ws/docker/cyclonedds.xml" }, "customizations": { From 604fa22bf341411b839743feb03a0e11f3c05e40 Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 15:54:01 -0400 Subject: [PATCH 05/17] idk --- .devcontainer/devcontainer.json | 2 +- scripts/start.bash | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 198b41ad..36a8b6e3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "workspaceFolder": "/ws", "containerEnv": { "ROS_DOMAIN_ID": "42", - "ROS_LOCALHOST_ONLY": "0", + "ROS_LOCALHOST_ONLY": "1", "CYCLONEDDS_URI": "file:///ws/docker/cyclonedds.xml" }, "customizations": { diff --git a/scripts/start.bash b/scripts/start.bash index e73cf390..c2fa9a27 100755 --- a/scripts/start.bash +++ b/scripts/start.bash @@ -9,4 +9,6 @@ pkill -f master_api || true # Lat/long are approx west lafayette, not important to be highly accurate mkdir -p /ws/logs -str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & \ No newline at end of file +str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & + +sleep infinity \ No newline at end of file From d5d31a9be58026fbace0ffa76479080e50e620e6 Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 19:57:35 +0000 Subject: [PATCH 06/17] idk --- .devcontainer/devcontainer.json | 2 +- compose/docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 36a8b6e3..198b41ad 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "workspaceFolder": "/ws", "containerEnv": { "ROS_DOMAIN_ID": "42", - "ROS_LOCALHOST_ONLY": "1", + "ROS_LOCALHOST_ONLY": "0", "CYCLONEDDS_URI": "file:///ws/docker/cyclonedds.xml" }, "customizations": { diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index e2530e7c..8e5ec688 100644 --- a/compose/docker-compose.yml +++ b/compose/docker-compose.yml @@ -7,11 +7,11 @@ services: volumes: - ../:/ws - ros_cache:/root/.cache - - /data/kart_logs:/ws/logs # logs location + - ../data/kart_logs:/ws/logs # logs location environment: - ROS_DISTRO=humble - ROS_DOMAIN_ID=42 - - ROS_LOCALHOST_ONLY=1 + - ROS_LOCALHOST_ONLY=0 - CYCLONEDDS_URI=file:///ws/docker/cyclonedds.xml ports: - "8000:8000" From 0883be8bd346bde3b139856e795c4a5f908908bf Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 20:14:27 +0000 Subject: [PATCH 07/17] remote jetson launch --- .../launch/bringup_rubik.launch.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py index 58a3190a..3fb3ca1b 100644 --- a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py @@ -10,9 +10,11 @@ def generate_launch_description(): pkg_share = get_package_share_directory("autonomous_kart") system_yaml = os.path.join(pkg_share, "params", "system.yaml") + sim_mode = LaunchConfiguration("simulation_mode") + + # rosbag recording run_name = datetime.now().strftime("run_%Y%m%d_%H%M%S") bag_output_path = os.path.join("/ws/logs", run_name) - sim_mode = LaunchConfiguration("simulation_mode") rosbag_recorder = ExecuteProcess( cmd=[ 'ros2', 'bag', 'record', '-a', @@ -26,6 +28,24 @@ def generate_launch_description(): respawn=True, respawn_delay=1.0 ) + + # remote launch on jetson + jetson_ip = "0.0.0.0" # have to set static ip on jetson + jetson_user = "evc" # jetson username + + # pull --> build --> launch + remote_command = ( + "git pull && " + "source /opt/ros/humble/setup.bash &&" + "colcon build && " + "ros2 launch autonomous_kart jetson_bringup.launch.py" + ) + + launch_jetson = ExecuteProcess( + cmd=['ssh', f'{jetson_user}@{jetson_ip}', f'bash -l -c "{remote_command}"'], + output='screen', + shell=True + ) return LaunchDescription( [ @@ -44,5 +64,6 @@ def generate_launch_description(): ), ] ), + launch_jetson, ] ) From 5703db4a466dd36b4db4f09a6325e8743caa6227 Mon Sep 17 00:00:00 2001 From: shb-png Date: Thu, 14 May 2026 22:02:30 +0000 Subject: [PATCH 08/17] master api endpoints for jetson launch --- compose/docker-compose.yml | 3 +- scripts/start.bash | 20 +++++++++- .../launch/bringup_rubik.launch.py | 38 ------------------- .../nodes/master/master_api.py | 9 +++++ .../nodes/master/master_node.py | 21 ++++++++++ .../autonomous_kart/params/system.yaml | 2 + 6 files changed, 52 insertions(+), 41 deletions(-) diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index 8e5ec688..fc04eb93 100644 --- a/compose/docker-compose.yml +++ b/compose/docker-compose.yml @@ -7,11 +7,12 @@ services: volumes: - ../:/ws - ros_cache:/root/.cache - - ../data/kart_logs:/ws/logs # logs location + - /data/kart_logs:/ws/logs # logs location environment: - ROS_DISTRO=humble - ROS_DOMAIN_ID=42 - ROS_LOCALHOST_ONLY=0 + - TARGET_DEVICE=jetson # set target device for build (ex: jetson, rubik) - CYCLONEDDS_URI=file:///ws/docker/cyclonedds.xml ports: - "8000:8000" diff --git a/scripts/start.bash b/scripts/start.bash index c2fa9a27..bb047013 100755 --- a/scripts/start.bash +++ b/scripts/start.bash @@ -7,8 +7,24 @@ source /opt/ros/humble/setup.bash colcon build pkill -f master_api || true -# Lat/long are approx west lafayette, not important to be highly accurate mkdir -p /ws/logs -str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & +if [ "$TARGET_DEVICE" == "jetson" ]; then + # Lat/long are approx west lafayette, not important to be highly accurate + str2str -in ntrip://shayman1:shayman1@108.59.49.226:9000/MSM4_VRS -out tcpsvr://:9195 -b 1 -p 40.4376975222 -86.9444409756 200 >> /ws/logs/str2str.log 2>&1 & +elif [ "$TARGET_DEVICE" == "rubik" ]; then + ( + while true; do + RUN_NAME="run_$(date -u +%Y%m%d_%H%M%S)" + ros2 bag record -a \ + --storage mcap \ + --storage-preset-profile zstd_fast \ + --max-bag-duration 60 \ + --exclude '/camera/.*' \ + --output "/ws/logs/$RUN_NAME" \ + >> /ws/logs/recorder.log 2>&1 + echo "[$(date -u +%FT%TZ)] recorder exited, restarting in 1s" >> /ws/logs/recorder.log + sleep 1 + done + ) & sleep infinity \ No newline at end of file diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py index 3fb3ca1b..d00b71d2 100644 --- a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py @@ -12,41 +12,6 @@ def generate_launch_description(): system_yaml = os.path.join(pkg_share, "params", "system.yaml") sim_mode = LaunchConfiguration("simulation_mode") - # rosbag recording - run_name = datetime.now().strftime("run_%Y%m%d_%H%M%S") - bag_output_path = os.path.join("/ws/logs", run_name) - rosbag_recorder = ExecuteProcess( - cmd=[ - 'ros2', 'bag', 'record', '-a', - '--storage', 'mcap', - '--storage-preset-profile', 'zstd_fast', - '--max-bag-duration', '60', - '--exclude', '/camera/.*', - '--output', bag_output_path - ], - output='screen', - respawn=True, - respawn_delay=1.0 - ) - - # remote launch on jetson - jetson_ip = "0.0.0.0" # have to set static ip on jetson - jetson_user = "evc" # jetson username - - # pull --> build --> launch - remote_command = ( - "git pull && " - "source /opt/ros/humble/setup.bash &&" - "colcon build && " - "ros2 launch autonomous_kart jetson_bringup.launch.py" - ) - - launch_jetson = ExecuteProcess( - cmd=['ssh', f'{jetson_user}@{jetson_ip}', f'bash -l -c "{remote_command}"'], - output='screen', - shell=True - ) - return LaunchDescription( [ DeclareLaunchArgument("simulation_mode", default_value="false"), @@ -55,8 +20,6 @@ def generate_launch_description(): SetParametersFromFile(system_yaml), SetParameter(name="simulation_mode", value=sim_mode), - rosbag_recorder, - Node( package="autonomous_kart", executable="metrics_node", @@ -64,6 +27,5 @@ def generate_launch_description(): ), ] ), - launch_jetson, ] ) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py index 102d5e0e..655bc9f5 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py @@ -191,6 +191,15 @@ def lines_endpoint(): "static": static_xy, "dynamic": master_node.get_dynamic_line(), }) + +@app.route("/jetson/start", methods=["POST"]) +def jetson_start(): + if not master_node: + return jsonify({"error": "not initialized"}), 500 + + master_node.start_jetson() + + return True, "jetson launch started" def start(node: MasterNode) -> None: diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index 3a1bec37..717ab68d 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -1,6 +1,7 @@ import json import threading import time +import subprocess from enum import Enum import math @@ -35,6 +36,8 @@ def __init__(self): self.yaw_cal_tick_dt = self.get_parameter("yaw_cal_tick_dt").value self.yaw_cal_accel_floor = self.get_parameter("yaw_cal_accel_floor").value self.yaw_cal_min_samples = self.get_parameter("yaw_cal_min_samples").value + self.jetson_ip = self.get_parameter("jetson_ip").value + self.jetson_user = self.get_parameter("jetson_username").value assert self.state in [s.value for s in STATES] @@ -331,6 +334,24 @@ def get_gps_status(self): with self._lock: return dict(self.gps_status_data) + def start_jetson(self): + with self._lock: + remote_command = ( + "bash ~/run_remote.sh --update" + ) + + ssh_command = [ + "ssh", + "-t", + f"{self.jetson_user}@{self.jetson_ip}", + remote_command, + ] + + self.logger.info("Starting Jetson bringup...") + + subprocess.Popen([ + ssh_command, + ]) def main(args=None): rclpy.init(args=args) diff --git a/src/autonomous_kart/autonomous_kart/params/system.yaml b/src/autonomous_kart/autonomous_kart/params/system.yaml index e0a7ca61..7b55e1d2 100644 --- a/src/autonomous_kart/autonomous_kart/params/system.yaml +++ b/src/autonomous_kart/autonomous_kart/params/system.yaml @@ -5,3 +5,5 @@ system_state: "IDLE" # "STOPPED", "IDLE", "MANUAL", "AUTONOMOUS" system_frequency: 60 # Speed of hot loop (Hertz) line_path: "/ws/data/racing_line/line14.csv" + jetson_ip: "0.0.0.0" + jetson_username: "evc" \ No newline at end of file From 1d46025f2ca3ee33ed41192b03e76899a791a4db Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 01:48:21 +0000 Subject: [PATCH 09/17] jetson tailscape addr --- src/autonomous_kart/autonomous_kart/params/system.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autonomous_kart/autonomous_kart/params/system.yaml b/src/autonomous_kart/autonomous_kart/params/system.yaml index 7b55e1d2..d7b29ae2 100644 --- a/src/autonomous_kart/autonomous_kart/params/system.yaml +++ b/src/autonomous_kart/autonomous_kart/params/system.yaml @@ -5,5 +5,5 @@ system_state: "IDLE" # "STOPPED", "IDLE", "MANUAL", "AUTONOMOUS" system_frequency: 60 # Speed of hot loop (Hertz) line_path: "/ws/data/racing_line/line14.csv" - jetson_ip: "0.0.0.0" + jetson_ip: "evc-jetson.tail73145c.ts.net" jetson_username: "evc" \ No newline at end of file From ddf8bf33e28b74f0912586b88f3ef2b428fe791b Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 02:09:46 +0000 Subject: [PATCH 10/17] Add master_api node to bringup_rubik launch file --- .../autonomous_kart/launch/bringup_rubik.launch.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py index d00b71d2..3fb824b8 100644 --- a/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py +++ b/src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py @@ -25,6 +25,12 @@ def generate_launch_description(): executable="metrics_node", name="metrics_node", ), + Node( + package="autonomous_kart", + executable="master_api", + name="master_api", + parameters=[system_yaml], + ), ] ), ] From 0d3ef44aee45fe90398b7ca02596d909f0b16a08 Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 02:14:42 +0000 Subject: [PATCH 11/17] pls work --- .../autonomous_kart/nodes/master/master_node.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index 717ab68d..ff8d969d 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -349,9 +349,7 @@ def start_jetson(self): self.logger.info("Starting Jetson bringup...") - subprocess.Popen([ - ssh_command, - ]) + subprocess.Popen(ssh_command) def main(args=None): rclpy.init(args=args) From 0cc69162c41585a07efb8d0a44d1f54f4985bc01 Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 02:16:39 +0000 Subject: [PATCH 12/17] ssh --- docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index eca954a4..5b5141f7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,6 +20,7 @@ RUN apt-get -o Acquire::http::Pipeline-Depth="0" \ ros-${ROS_DISTRO}-rclpy ros-${ROS_DISTRO}-ros2bag \ ros-${ROS_DISTRO}-tf2-tools ros-${ROS_DISTRO}-image-transport \ ros-${ROS_DISTRO}-cv-bridge \ + openssh-client \ git curl nano \ socat rtklib i2c-tools && \ rm -rf /var/lib/apt/lists/* From 9bc8d0d0fc38b4444576812fe67c2952e2c3974c Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 02:30:52 +0000 Subject: [PATCH 13/17] jsonify it!! --- .../autonomous_kart/nodes/master/master_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py index 655bc9f5..7a171b4e 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py @@ -199,7 +199,9 @@ def jetson_start(): master_node.start_jetson() - return True, "jetson launch started" + return jsonify({ + "message": "jetson launch started" + }) def start(node: MasterNode) -> None: From d770149d6bf67efd43defc92eaa852d703afb630 Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 03:04:09 +0000 Subject: [PATCH 14/17] restart and update jetston endpoint --- .../nodes/master/master_api.py | 18 +++++++++-- .../nodes/master/master_node.py | 31 +++++++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py index 7a171b4e..a0f3f5c0 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py @@ -192,15 +192,27 @@ def lines_endpoint(): "dynamic": master_node.get_dynamic_line(), }) -@app.route("/jetson/start", methods=["POST"]) +@app.route("/jetson/restart", methods=["POST"]) def jetson_start(): if not master_node: return jsonify({"error": "not initialized"}), 500 - master_node.start_jetson() + master_node.restart_jetson() return jsonify({ - "message": "jetson launch started" + "message": "jetson restart started" + }) + + +@app.route("/jetson/update", methods=["POST"]) +def jetson_update(): + if not master_node: + return jsonify({"error": "not initialized"}), 500 + + master_node.update_jetson() + + return jsonify({ + "message": "jetson update started" }) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index ff8d969d..2f29f2cd 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -334,7 +334,8 @@ def get_gps_status(self): with self._lock: return dict(self.gps_status_data) - def start_jetson(self): + # pull latest code, rebuild container, and restart bringup on jetson + def restart_jetson(self): with self._lock: remote_command = ( "bash ~/run_remote.sh --update" @@ -347,7 +348,33 @@ def start_jetson(self): remote_command, ] - self.logger.info("Starting Jetson bringup...") + self.logger.info("Rebuilding Jetson container and pulling latest code...") + + subprocess.Popen(ssh_command) + + # only pull latest code, dont rebuild container + def update_jetson(self): + with self._lock: + remote_command = ( + "cd ~/AutonomousKart && " + "git pull && " + "docker exec ros2-dev bash -lc '" + "source /opt/ros/humble/setup.bash && " + "cd /ws && " + "colcon build --symlink-install && " + "source install/setup.bash && " + "ros2 launch autonomous_kart bringup_jetson.launch.py" + "'" + ) + + ssh_command = [ + "ssh", + "-t", + f"{self.jetson_user}@{self.jetson_ip}", + remote_command, + ] + + self.logger.info("Starting Jetson and pulling latest code...") subprocess.Popen(ssh_command) From ed2cf21a05c9036fd76ba437a85166ba4e7d00dc Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 12:32:42 -0400 Subject: [PATCH 15/17] remove --symlink-install from colcon build --- src/autonomous_kart/autonomous_kart/nodes/master/master_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index 2f29f2cd..9914ec5b 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -361,7 +361,7 @@ def update_jetson(self): "docker exec ros2-dev bash -lc '" "source /opt/ros/humble/setup.bash && " "cd /ws && " - "colcon build --symlink-install && " + "colcon build && " "source install/setup.bash && " "ros2 launch autonomous_kart bringup_jetson.launch.py" "'" From 41813af7ca774f67c2df9069b6db1db008ae1193 Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 13:13:54 -0400 Subject: [PATCH 16/17] copilot comments --- scripts/start.bash | 1 + .../nodes/master/master_api.py | 26 ++++++- .../nodes/master/master_node.py | 78 +++++++++---------- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/scripts/start.bash b/scripts/start.bash index bb047013..9ca04e74 100755 --- a/scripts/start.bash +++ b/scripts/start.bash @@ -27,4 +27,5 @@ elif [ "$TARGET_DEVICE" == "rubik" ]; then sleep 1 done ) & +fi sleep infinity \ No newline at end of file diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py index a0f3f5c0..473572f4 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_api.py @@ -192,8 +192,20 @@ def lines_endpoint(): "dynamic": master_node.get_dynamic_line(), }) +@app.route("/jetson/hotswap", methods=["POST"]) +def jetson_hotswap(): + if not master_node: + return jsonify({"error": "not initialized"}), 500 + + master_node.hotswap_jetson() + + return jsonify({ + "message": "jetson hotswap started" + }) + + @app.route("/jetson/restart", methods=["POST"]) -def jetson_start(): +def jetson_restart(): if not master_node: return jsonify({"error": "not initialized"}), 500 @@ -216,6 +228,18 @@ def jetson_update(): }) +@app.route("/jetson/rebuild", methods=["POST"]) +def jetson_rebuild(): + if not master_node: + return jsonify({"error": "not initialized"}), 500 + + master_node.rebuild_jetson() + + return jsonify({ + "message": "jetson rebuild started" + }) + + def start(node: MasterNode) -> None: try: rclpy.spin(node) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index 9914ec5b..4dcd26c2 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -334,49 +334,49 @@ def get_gps_status(self): with self._lock: return dict(self.gps_status_data) - # pull latest code, rebuild container, and restart bringup on jetson - def restart_jetson(self): - with self._lock: - remote_command = ( - "bash ~/run_remote.sh --update" - ) - - ssh_command = [ - "ssh", - "-t", - f"{self.jetson_user}@{self.jetson_ip}", - remote_command, - ] - - self.logger.info("Rebuilding Jetson container and pulling latest code...") + # Jetson remote management + + def _ssh_fire(self, remote_command: str, log_msg: str): + """Shared helper: open a non-blocking SSH subprocess to the Jetson.""" + ssh_command = [ + "ssh", "-t", + f"{self.jetson_user}@{self.jetson_ip}", + remote_command, + ] + self.logger.info(log_msg) + subprocess.Popen(ssh_command) + + def hotswap_jetson(self): + """Git-pull and rebuild inside the *running* container, then relaunch bringup_rubik. + The container is never stopped — fastest way to pick up new code mid-session.""" + self._ssh_fire( + "bash ~/run_remote.sh --hotswap", + "Hotswapping: pulling latest code and relaunching inside running container...", + ) - subprocess.Popen(ssh_command) + def restart_jetson(self): + """Stop the container, rebuild the ROS workspace, and relaunch bringup. + No git pull, no Docker image pull — fastest way to bounce the stack.""" + self._ssh_fire( + "bash ~/run_remote.sh --restart", + "Restarting Jetson container (no code update)...", + ) - # only pull latest code, dont rebuild container def update_jetson(self): - with self._lock: - remote_command = ( - "cd ~/AutonomousKart && " - "git pull && " - "docker exec ros2-dev bash -lc '" - "source /opt/ros/humble/setup.bash && " - "cd /ws && " - "colcon build && " - "source install/setup.bash && " - "ros2 launch autonomous_kart bringup_jetson.launch.py" - "'" - ) - - ssh_command = [ - "ssh", - "-t", - f"{self.jetson_user}@{self.jetson_ip}", - remote_command, - ] - - self.logger.info("Starting Jetson and pulling latest code...") + """Git-pull the latest code, then restart. + Use this before a session to pick up new commits without a full image rebuild.""" + self._ssh_fire( + "bash ~/run_remote.sh --update", + "Pulling latest code and restarting Jetson...", + ) - subprocess.Popen(ssh_command) + def rebuild_jetson(self): + """Pull the latest Docker image, git-pull code, then restart. + Use this after a Docker image has been pushed (e.g. new deps or base OS change).""" + self._ssh_fire( + "bash ~/run_remote.sh --rebuild", + "Pulling latest Docker image + code and rebuilding Jetson...", + ) def main(args=None): rclpy.init(args=args) From d37d01391151431c172523b0fb518f105709e18e Mon Sep 17 00:00:00 2001 From: shb-png Date: Fri, 15 May 2026 13:59:16 -0400 Subject: [PATCH 17/17] auto accept host key --- .../autonomous_kart/nodes/master/master_node.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py index 4dcd26c2..700c0b7a 100644 --- a/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py +++ b/src/autonomous_kart/autonomous_kart/nodes/master/master_node.py @@ -339,10 +339,13 @@ def get_gps_status(self): def _ssh_fire(self, remote_command: str, log_msg: str): """Shared helper: open a non-blocking SSH subprocess to the Jetson.""" ssh_command = [ - "ssh", "-t", - f"{self.jetson_user}@{self.jetson_ip}", - remote_command, - ] + "ssh", "-t", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + f"{self.jetson_user}@{self.jetson_ip}", + remote_command, + ] + self.logger.info(log_msg) subprocess.Popen(ssh_command)