Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,6 @@ cython_debug/
# Ros build ignored
install
log
logs
kart_logs
build
3 changes: 2 additions & 1 deletion compose/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ services:
environment:
- ROS_DISTRO=humble
- ROS_DOMAIN_ID=42
- ROS_LOCALHOST_ONLY=1
- 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"
Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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/*
Expand Down
37 changes: 19 additions & 18 deletions scripts/start.bash
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ 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 &

# 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
) &

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
) &
Comment thread
shb-png marked this conversation as resolved.
fi
sleep infinity
Original file line number Diff line number Diff line change
@@ -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],
),
]
),
]
)
37 changes: 37 additions & 0 deletions src/autonomous_kart/autonomous_kart/launch/bringup_rubik.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from launch import LaunchDescription
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")
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",
),
Node(
package="autonomous_kart",
executable="master_api",
name="master_api",
parameters=[system_yaml],
),
]
),
]
)
47 changes: 47 additions & 0 deletions src/autonomous_kart/autonomous_kart/nodes/master/master_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,53 @@ def lines_endpoint():
"static": static_xy,
"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_restart():
if not master_node:
return jsonify({"error": "not initialized"}), 500

master_node.restart_jetson()

return jsonify({
"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"
})
Comment on lines +207 to +228


@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:
Expand Down
49 changes: 49 additions & 0 deletions src/autonomous_kart/autonomous_kart/nodes/master/master_node.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import threading
import time
import subprocess
from enum import Enum

import math
Expand Down Expand Up @@ -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]

Expand Down Expand Up @@ -331,6 +334,52 @@ def get_gps_status(self):
with self._lock:
return dict(self.gps_status_data)

# 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",
"-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)

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...",
)

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)...",
)

def update_jetson(self):
"""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...",
)

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)
Expand Down
2 changes: 2 additions & 0 deletions src/autonomous_kart/autonomous_kart/params/system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: "evc-jetson.tail73145c.ts.net"
jetson_username: "evc"
Loading