diff --git a/airstack.sh b/airstack.sh index 1497db034..0c6558a23 100755 --- a/airstack.sh +++ b/airstack.sh @@ -193,6 +193,24 @@ function check_docker { log_error "Docker daemon is not running." exit 1 fi + + # Check Docker Compose version + if command -v docker &> /dev/null && docker compose version &> /dev/null; then + local compose_version=$(docker compose version --short 2>/dev/null) + if [ -z "$compose_version" ]; then + # Fallback for parsing + compose_version=$(docker compose version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + fi + + if [ -n "$compose_version" ]; then + local major_ver=$(echo "$compose_version" | cut -d. -f1) + # Check if major version is a number before comparing + if [[ "$major_ver" =~ ^[0-9]+$ ]] && [ "$major_ver" -lt 5 ]; then + log_error "Docker Compose version $compose_version is less than v5.0.0. This script requires v5.0.0 or greater." + exit 1 + fi + fi + fi } # Find container by partial name using regex diff --git a/common/ros_packages/robot_descriptions/iris_with_sensors_description/urdf/iris_with_sensors.pegasus.robot.urdf b/common/ros_packages/robot_descriptions/iris_with_sensors_description/urdf/iris_with_sensors.pegasus.robot.urdf index 7f4f5d72d..13f71262a 100644 --- a/common/ros_packages/robot_descriptions/iris_with_sensors_description/urdf/iris_with_sensors.pegasus.robot.urdf +++ b/common/ros_packages/robot_descriptions/iris_with_sensors_description/urdf/iris_with_sensors.pegasus.robot.urdf @@ -40,7 +40,7 @@ - + diff --git a/config/isaac_sim_config.yaml b/config/isaac_sim_config.yaml index 3fbbc3c86..daf256e22 100644 --- a/config/isaac_sim_config.yaml +++ b/config/isaac_sim_config.yaml @@ -6,7 +6,6 @@ isaac_sim: enabled_extensions: - "airlab.airstack" - "pegasus.simulator" - - "omni.physx.forcefields" # Whether to start simulation playback automatically play_on_start: true diff --git a/gcs/docker/gcs-base-docker-compose.yaml b/gcs/docker/gcs-base-docker-compose.yaml index 800fcc920..c1502d4ab 100644 --- a/gcs/docker/gcs-base-docker-compose.yaml +++ b/gcs/docker/gcs-base-docker-compose.yaml @@ -23,7 +23,6 @@ services: - capabilities: [gpu] count: 1 driver: nvidia - runtime: nvidia entrypoint: '' environment: - AUTOLAUNCH=${AUTOLAUNCH:-false} diff --git a/robot/docker/.bashrc b/robot/docker/.bashrc index 1899e5eb2..83f42a18c 100755 --- a/robot/docker/.bashrc +++ b/robot/docker/.bashrc @@ -23,8 +23,12 @@ function bws(){ COLCON_LOG_PATH="$ROS2_WS_DIR"/log colcon build --symlink-install --base-paths "$ROS2_WS_DIR"/ --build-base "$ROS2_WS_DIR"/build/ --install-base "$ROS2_WS_DIR"/install/ "$@" } function sws(){ - echo "Sourcing "$ROS2_WS_DIR"/install/local_setup.bash" - source "$ROS2_WS_DIR"/install/local_setup.bash || echo "Please make sure to build first with 'bws'" + if [ -f "$ROS2_WS_DIR/install/local_setup.bash" ]; then + echo "Sourcing $ROS2_WS_DIR/install/local_setup.bash" + source "$ROS2_WS_DIR/install/local_setup.bash" + else + echo "Workspace not built yet. Please make sure to build first with 'bws'" + fi } # Function to prompt user for confirmation diff --git a/robot/docker/docker-compose.yaml b/robot/docker/docker-compose.yaml index 350bf3690..be0aed4a8 100644 --- a/robot/docker/docker-compose.yaml +++ b/robot/docker/docker-compose.yaml @@ -39,7 +39,12 @@ services: # for multiple robots deploy: replicas: ${NUM_ROBOTS:-1} - runtime: nvidia + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] simple-robot: profiles: !override @@ -74,7 +79,13 @@ services: ipc: host command: > bash -c "ssh service restart; tmux new -d -s robot_bringup && tmux send-keys -t robot_bringup 'bws && sws && DATE=$(date | sed \"s/ /_/g\" | sed \"s/:/_/g\") ros2 launch ${ROBOT_LAUNCH_PACKAGE} ${ROBOT_LAUNCH_FILE} sim:="false" ' ENTER && sleep infinity" - runtime: nvidia + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] # assumes network isolation via a physical router, so uses network_mode=host network_mode: host volumes: @@ -98,8 +109,14 @@ services: && tmux send-keys -t zed_driver 'bws && sws && ros2 launch zed_wrapper zed_dual_camera.launch.py pose_cam_serial:='41591402' wire_cam_serial:='44405253' camera_name:=\"robot_1/sensors\" node_name:=\"front_stereo\" ' ENTER && sleep infinity" + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] network_mode: host - runtime: nvidia privileged: true ipc: host pid: host diff --git a/simulation/isaac-sim/docker/.bashrc b/simulation/isaac-sim/docker/.bashrc index 427f6c16f..d3714e8eb 100644 --- a/simulation/isaac-sim/docker/.bashrc +++ b/simulation/isaac-sim/docker/.bashrc @@ -121,13 +121,25 @@ fi # --- ROS2 setup --- -source /opt/ros/humble/setup.bash -source /humble_ws/install/setup.bash # isaacsim ros2 package +source /opt/ros/jazzy/setup.bash +source /jazzy_ws/install/setup.bash # isaacsim ros2 package + # needed for communication with Isaac Sim ROS2 # https://docs.omniverse.nvidia.com/isaacsim/latest/installation/install_ros.html#enabling-the-ros-bridge-extension export FASTRTPS_DEFAULT_PROFILES_FILE="/isaac-sim/fastdds.xml" export RMW_IMPLEMENTATION=rmw_fastrtps_cpp # for local development, prevent conflict with other desktops -export ROS_LOCALHOST_ONLY=1 +export ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET + +# --- Environment Separation --- +# Define the PYTHONPATH specifically for Isaac Sim (Python 3.10) +# This strips out the System ROS (Python 3.12) paths to prevent conflicts +export ISAAC_SIM_PYTHONPATH=$(echo $PYTHONPATH | tr ':' '\n' | grep -v "lib/python3.12/site-packages" | paste -sd ':' -):/isaac-sim/exts/isaacsim.ros2.bridge/jazzy/rclpy + +# Helper function to run Isaac Sim python scripts with the correct environment +run_isaac_python() { + PYTHONPATH="$ISAAC_SIM_PYTHONPATH" /isaac-sim/python.sh "$@" +} +export -f run_isaac_python # --- Isaac Setup --- alias runapp="/isaac-sim/runapp.sh --path omniverse://airlab-nucleus.andrew.cmu.edu/Library/Assets/Ascent_Aerosystems/Spirit_UAV/spirit_uav_red_yellow.prop.usd" diff --git a/simulation/isaac-sim/docker/Dockerfile.isaac-ros b/simulation/isaac-sim/docker/Dockerfile.isaac-ros index 269211a71..17f38f434 100644 --- a/simulation/isaac-sim/docker/Dockerfile.isaac-ros +++ b/simulation/isaac-sim/docker/Dockerfile.isaac-ros @@ -1,9 +1,11 @@ -ARG ISAAC_VERSION="4.5.0" +ARG ISAAC_VERSION="5.1.0" # expects context to be the root of the repository, i.e. AirStack/. this is so we can access AirStack/ros_ws/ FROM nvcr.io/nvidia/isaac-sim:${ISAAC_VERSION} ARG ISAAC_VERSION WORKDIR /isaac-sim +USER root + # isaac's ros2 launch run_isaacsim.launch.py hardcodes to search in this path, so we have to put the executables here RUN mkdir -p /root/.local/share/ov/pkg/ && \ ln -s /isaac-sim /root/.local/share/ov/pkg/isaac-sim-${ISAAC_VERSION} @@ -14,7 +16,7 @@ ENV OMNI_KIT_ALLOW_ROOT=1 # setup environment ENV LANG=C.UTF-8 ENV LC_ALL=C.UTF-8 -ENV ROS_DISTRO=humble +ENV ROS_DISTRO=jazzy # setup timezone RUN echo 'Etc/UTC' > /etc/timezone && \ @@ -64,32 +66,35 @@ RUN set -eux; \ rm -rf "$GNUPGHOME" # setup ros2 apt source -RUN echo "deb [ signed-by=/usr/share/keyrings/ros2-latest-archive-keyring.gpg ] http://packages.ros.org/ros2/ubuntu jammy main" > /etc/apt/sources.list.d/ros2-latest.list +RUN echo "deb [ signed-by=/usr/share/keyrings/ros2-latest-archive-keyring.gpg ] http://packages.ros.org/ros2/ubuntu noble main" > /etc/apt/sources.list.d/ros2-latest.list # Remove conflicting third-party apt sources that cause libbrotli conflicts RUN rm -f /etc/apt/sources.list.d/*deb.sury.org*.list || true && apt-get update -RUN apt-get update && \ - apt-get install -y --allow-downgrades --no-install-recommends libbrotli1=1.0.9-2build6 && \ - apt-mark hold libbrotli1 && \ - apt-get install -y --no-install-recommends \ - libfreetype6-dev \ - libfontconfig1-dev \ - ros-humble-desktop \ - ros-dev-tools \ - python3-rosdep \ - ros-humble-tf2* \ - ros-humble-mavros \ - ros-humble-ackermann-msgs \ - ros-humble-topic-tools \ - ros-humble-grid-map \ - ros-humble-domain-bridge \ - python3-colcon-common-extensions && \ - rm -rf /var/lib/apt/lists/* +RUN apt-get install -y --no-install-recommends libbrotli1 +RUN apt-mark hold libbrotli1 +RUN apt-get install -y --no-install-recommends libfreetype6-dev +RUN apt-get install -y --no-install-recommends libfontconfig1-dev +RUN apt-get install -y --no-install-recommends ros-jazzy-desktop +RUN apt-get install -y --no-install-recommends ros-dev-tools +RUN apt-get install -y --no-install-recommends python3-rosdep +RUN apt-get install -y --no-install-recommends ros-jazzy-tf2* +RUN apt-get install -y --no-install-recommends ros-jazzy-mavros +RUN apt-get install -y --no-install-recommends ros-jazzy-ackermann-msgs +RUN apt-get install -y --no-install-recommends ros-jazzy-topic-tools +RUN apt-get install -y --no-install-recommends ros-jazzy-grid-map +RUN apt-get install -y --no-install-recommends ros-jazzy-domain-bridge +RUN apt-get install -y --no-install-recommends ros-jazzy-moveit +RUN apt-get install -y --no-install-recommends python3-colcon-common-extensions +RUN rm -rf /var/lib/apt/lists/* # setup mavros dependencies -RUN /opt/ros/humble/lib/mavros/install_geographiclib_datasets.sh +RUN /opt/ros/jazzy/lib/mavros/install_geographiclib_datasets.sh + +# Update LD_LIBRARY_PATH to include ROS2 libraries (Fixes internal rclpy import) +ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/ros/jazzy/lib:/jazzy/lib +ENV RMW_IMPLEMENTATION=rmw_fastrtps_cpp # install python packages RUN /isaac-sim/python.sh -m pip install --upgrade pip && \ @@ -105,9 +110,9 @@ ARG isaac_dir_name="IsaacSim-ros_workspaces-IsaacSim-${ISAAC_VERSION}" RUN cd /tmp/ && \ curl -L -O https://github.com/isaac-sim/IsaacSim-ros_workspaces/archive/refs/tags/IsaacSim-${ISAAC_VERSION}.zip && \ unzip IsaacSim-${ISAAC_VERSION}.zip && \ - mv ${isaac_dir_name}/humble_ws /humble_ws && \ - cd /humble_ws && \ - . /opt/ros/humble/setup.sh && \ + mv ${isaac_dir_name}/jazzy_ws /jazzy_ws && \ + cd /jazzy_ws && \ + . /opt/ros/jazzy/setup.sh && \ colcon build --symlink-install && \ rm -rf /tmp/IsaacSim-${ISAAC_VERSION}.zip /tmp/${isaac_dir_name} @@ -133,6 +138,11 @@ WORKDIR /root/Documents/Kit/shared/exts/ RUN git clone https://github.com/castacks/PegasusSimulator-AirStack-Integration.git /tmp/PegasusSimulator && \ mv /tmp/PegasusSimulator/extensions/pegasus.simulator . +# Add extension search path configuration for Kit +# This ensures that /root/Documents/Kit/shared/exts is always searched for extensions +ARG KIT_ARGS="--ext-folder /root/Documents/Kit/shared/exts" +ENV OMNI_APP_ARGS="${KIT_ARGS}" + # rm -rf /tmp/PegasusSimulator ENV ISAACSIM_PATH=/isaac-sim ENV ACCEPT_EULA="Y" @@ -156,7 +166,7 @@ RUN git clone https://github.com/tmux-plugins/tpm /root/.tmux/plugins/tpm # copy FastDDS config COPY docker/fastdds.xml /isaac-sim/fastdds.xml -# ROS2 humble launch script seeks this +# ROS2 launch script seeks this RUN ln -s /isaac-sim /root/isaacsim # Cleanup. Prevent people accidentally doing git commits as root in Docker diff --git a/simulation/isaac-sim/docker/docker-compose.yaml b/simulation/isaac-sim/docker/docker-compose.yaml index cd7aff4d7..e08f417ff 100644 --- a/simulation/isaac-sim/docker/docker-compose.yaml +++ b/simulation/isaac-sim/docker/docker-compose.yaml @@ -17,7 +17,7 @@ services: tmux new -d -s isaac; if [ $$AUTOLAUNCH = 'true' ]; then if [ \"${ISAAC_SIM_USE_STANDALONE_SCRIPT}\" = 'true' ]; then - tmux send-keys -t isaac '/isaac-sim/python.sh /root/AirStack/simulation/isaac-sim/launch_scripts/${ISAAC_SIM_SCRIPT_NAME}' ENTER + tmux send-keys -t isaac 'run_isaac_python /root/AirStack/simulation/isaac-sim/launch_scripts/${ISAAC_SIM_SCRIPT_NAME}' ENTER else tmux send-keys -t isaac 'ros2 launch isaacsim run_isaacsim.launch.py gui:=\"${ISAAC_SIM_GUI}\" play_sim_on_start:=\"${PLAY_SIM_ON_START}\" ' ENTER fi @@ -65,7 +65,7 @@ services: - $HOME/Documents/isaac_sim_data/data:/root/.local/share/ov/data:rw - $HOME/documents/isaac_sim_data/documents:/root/Documents:rw # IMPORTANT: set the version number without the trailing .0 - - ./user.config.json:/root/.local/share/ov/data/Kit/Isaac-Sim Full/4.5/user.config.json:rw + - ./user.config.json:/root/.local/share/ov/data/Kit/Isaac-Sim Full/5.1/user.config.json:rw - ./ui.py:/isaac-sim/kit/exts/omni.kit.widget.nucleus_connector/omni/kit/widget/nucleus_connector/ui.py:rw # developer stuff - .dev:/root/.dev:rw # developer config diff --git a/simulation/isaac-sim/extensions/PegasusSimulator b/simulation/isaac-sim/extensions/PegasusSimulator index f6fae8d34..15379fac8 160000 --- a/simulation/isaac-sim/extensions/PegasusSimulator +++ b/simulation/isaac-sim/extensions/PegasusSimulator @@ -1 +1 @@ -Subproject commit f6fae8d34d9f95470c5976c11ca8a670421fbfb2 +Subproject commit 15379fac8d487b03af5a1b950541bab060b0b7bd diff --git a/simulation/isaac-sim/launch_scripts/example_one_px4_pegasus_launch_script.py b/simulation/isaac-sim/launch_scripts/example_one_px4_pegasus_launch_script.py index 60c24716a..b6c12b4e3 100755 --- a/simulation/isaac-sim/launch_scripts/example_one_px4_pegasus_launch_script.py +++ b/simulation/isaac-sim/launch_scripts/example_one_px4_pegasus_launch_script.py @@ -13,6 +13,9 @@ # Start Isaac Sim's simulation environment (Must start this before importing omni modules) simulation_app = SimulationApp({"headless": False}) +import rclpy +print(f"[Launcher] SUCCESS: rclpy imported from {rclpy.__file__}") + import omni.kit.app import omni.timeline from omni.isaac.core.world import World @@ -40,9 +43,9 @@ # Explicitly enable required extensions ext_manager = omni.kit.app.get_app().get_extension_manager() + for ext in [ - # "airlab.airstack", - "omni.physx.forcefields", + "airlab.airstack", "omni.graph.core", # Core runtime for OmniGraph engine "omni.graph.action", # Action Graph framework "omni.graph.action_nodes", # Built-in Action Graph node library @@ -52,11 +55,15 @@ "omni.graph.window.action", # Action Graph editor window "omni.graph.window.generic", # Generic graph UI tools "omni.graph.ui_nodes", # UI node building helpers - "airlab.pegasus", # Airlab extension Pegasus core extension "pegasus.simulator", ]: if not ext_manager.is_extension_enabled(ext): - ext_manager.set_extension_enabled(ext, True) + print(f"[Launcher] Enabling extension: {ext}") + # Try both methods for robustness in different Kit versions + ext_manager.set_extension_enabled_immediate(ext, True) + print(f"[Launcher] Successfully enabled extension: {ext} via immediate method") + else: + print(f"[Launcher] Extension already enabled: {ext}") class PegasusApp: @@ -75,10 +82,10 @@ def __init__(self): # Load default environment. You can replace with url or file path of any desired environment. self.pg.load_environment(SIMULATION_ENVIRONMENTS["Curved Gridroom"]) - - # Spawn a PX4 multirotor drone with a specified vehicle ID and domain ID - # PX4 udp port = 14540 + (vehicle_id) - # Domain ID is for ROS2 domain communication. As of now, it should match the vehicle id by convention. + + # # Spawn a PX4 multirotor drone with a specified vehicle ID and domain ID + # # PX4 udp port = 14540 + (vehicle_id) + # # Domain ID is for ROS2 domain communication. As of now, it should match the vehicle id by convention. graph_handle = spawn_px4_multirotor_node( pegasus_node_name="PX4Multirotor", drone_prim="/World/drone/base_link", diff --git a/simulation/isaac-sim/launch_scripts/example_two_px4_pegasus_launch_script.py b/simulation/isaac-sim/launch_scripts/example_two_px4_pegasus_launch_script.py index 329f1170c..768807739 100755 --- a/simulation/isaac-sim/launch_scripts/example_two_px4_pegasus_launch_script.py +++ b/simulation/isaac-sim/launch_scripts/example_two_px4_pegasus_launch_script.py @@ -40,9 +40,9 @@ # Explicitly enable required extensions ext_manager = omni.kit.app.get_app().get_extension_manager() + for ext in [ - # "airlab.airstack", - "omni.physx.forcefields", + "airlab.airstack", "omni.graph.core", # Core runtime for OmniGraph engine "omni.graph.action", # Action Graph framework "omni.graph.action_nodes", # Built-in Action Graph node library @@ -52,11 +52,15 @@ "omni.graph.window.action", # Action Graph editor window "omni.graph.window.generic", # Generic graph UI tools "omni.graph.ui_nodes", # UI node building helpers - "airlab.pegasus", # Airlab extension Pegasus core extension "pegasus.simulator", ]: if not ext_manager.is_extension_enabled(ext): - ext_manager.set_extension_enabled(ext, True) + print(f"[Launcher] Enabling extension: {ext}") + # Try both methods for robustness in different Kit versions + ext_manager.set_extension_enabled_immediate(ext, True) + print(f"[Launcher] Successfully enabled extension: {ext} via immediate method") + else: + print(f"[Launcher] Extension already enabled: {ext}") class PegasusApp: @@ -83,6 +87,7 @@ def __init__(self): graph_handle = spawn_px4_multirotor_node( pegasus_node_name="PX4Multirotor", drone_prim="/World/drone1/base_link", + robot_name="robot_1", vehicle_id=1, # defines MAVLink port offset domain_id=1, # defines ROS2 domain ID usd_file="/root/Documents/Kit/shared/exts/pegasus.simulator/pegasus/simulator/assets/Robots/Iris/iris.usd", @@ -94,6 +99,7 @@ def __init__(self): add_zed_stereo_camera_subgraph( parent_graph_handle=graph_handle, drone_prim="/World/drone1/base_link", + robot_name="robot_1", camera_name="ZEDCamera", camera_offset = [0.1, 0.0, 0.0], # X, Y, Z offset from drone base_link camera_rotation_offset = [0.0, 0.0, 0.0], # Rotation in degrees (roll, pitch, yaw) @@ -103,6 +109,7 @@ def __init__(self): add_ouster_lidar_subgraph( parent_graph_handle=graph_handle, drone_prim="/World/drone1/base_link", + robot_name="robot_1", lidar_name="OS1_REV6_128_10hz___512_resolution", lidar_offset = [0.0, 0.0, 0.025], # X, Y, Z offset from drone base_link lidar_rotation_offset = [0.0, 0.0, 0.0], # Rotation in degrees (roll, pitch, yaw) @@ -126,6 +133,7 @@ def __init__(self): add_zed_stereo_camera_subgraph( parent_graph_handle=graph_handle, drone_prim="/World/drone2/base_link", + robot_name="robot_2", camera_name="ZEDCamera", camera_offset = [0.1, 0.0, 0.0], # X, Y, Z offset from drone base_link camera_rotation_offset = [0.0, 0.0, 0.0], # Rotation in degrees (roll, pitch, yaw) @@ -135,6 +143,7 @@ def __init__(self): add_ouster_lidar_subgraph( parent_graph_handle=graph_handle, drone_prim="/World/drone2/base_link", + robot_name="robot_2", lidar_name="OS1_REV6_128_10hz___512_resolution", lidar_offset = [0.0, 0.0, 0.025], # X, Y, Z offset from drone base_link lidar_rotation_offset = [0.0, 0.0, 0.0], # Rotation in degrees (roll, pitch, yaw) diff --git a/simulation/simple-sim/docker/docker-compose.yaml b/simulation/simple-sim/docker/docker-compose.yaml index 07b61f6a6..65b3b6981 100644 --- a/simulation/simple-sim/docker/docker-compose.yaml +++ b/simulation/simple-sim/docker/docker-compose.yaml @@ -20,7 +20,7 @@ services: tty: true ipc: host privileged: true - runtime: nvidia + # runtime: nvidia <-- Removed deprecated runtime key networks: airstack_network: ipv4_address: 172.31.0.200