From 852ea3238d719d56d2fdf0f07e0ac57556d8ade6 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 12:15:47 -0400 Subject: [PATCH 01/27] Begin port to 24.04 --- factory/24.04/hello_robot_audio.desktop | 6 + factory/24.04/hello_robot_audio.sh | 6 + .../24.04/hello_robot_gamepad_teleop.desktop | 6 + factory/24.04/hello_robot_gamepad_teleop.sh | 6 + factory/24.04/hello_robot_lrf_off.desktop | 6 + factory/24.04/hello_robot_lrf_off.py | 10 + factory/24.04/hello_robot_pimu_ping.desktop | 6 + factory/24.04/hello_robot_pimu_ping.py | 18 ++ factory/24.04/hello_robot_pimu_ping.sh | 7 + factory/24.04/hello_robot_xbox_teleop.desktop | 6 + factory/24.04/hello_robot_xbox_teleop.sh | 6 + factory/24.04/hello_sudoers | 1 + factory/24.04/iron2humble_migration.md | 45 +++++ factory/24.04/motd/00-header | 30 +++ factory/24.04/motd/10-help-text | 25 +++ factory/24.04/motd/10dpkg_options | 4 + factory/24.04/package-lock.json | 6 + factory/24.04/stretch_about.png | Bin 0 -> 39299 bytes .../24.04/stretch_create_ament_workspace.sh | 130 ++++++++++++ factory/24.04/stretch_initial_setup.sh | 186 ++++++++++++++++++ factory/24.04/stretch_install_arduino.sh | 7 + factory/24.04/stretch_install_dev_tools.sh | 66 +++++++ factory/24.04/stretch_install_firmware.sh | 17 ++ factory/24.04/stretch_install_system.sh | 178 +++++++++++++++++ factory/24.04/stretch_ros2_humble.repos | 42 ++++ factory/24.04/sunshine.service | 12 ++ factory/24.04/sunshine_apps.json | 17 ++ 27 files changed, 849 insertions(+) create mode 100644 factory/24.04/hello_robot_audio.desktop create mode 100755 factory/24.04/hello_robot_audio.sh create mode 100644 factory/24.04/hello_robot_gamepad_teleop.desktop create mode 100755 factory/24.04/hello_robot_gamepad_teleop.sh create mode 100644 factory/24.04/hello_robot_lrf_off.desktop create mode 100755 factory/24.04/hello_robot_lrf_off.py create mode 100644 factory/24.04/hello_robot_pimu_ping.desktop create mode 100755 factory/24.04/hello_robot_pimu_ping.py create mode 100755 factory/24.04/hello_robot_pimu_ping.sh create mode 100644 factory/24.04/hello_robot_xbox_teleop.desktop create mode 100755 factory/24.04/hello_robot_xbox_teleop.sh create mode 100644 factory/24.04/hello_sudoers create mode 100644 factory/24.04/iron2humble_migration.md create mode 100644 factory/24.04/motd/00-header create mode 100644 factory/24.04/motd/10-help-text create mode 100644 factory/24.04/motd/10dpkg_options create mode 100644 factory/24.04/package-lock.json create mode 100644 factory/24.04/stretch_about.png create mode 100755 factory/24.04/stretch_create_ament_workspace.sh create mode 100755 factory/24.04/stretch_initial_setup.sh create mode 100755 factory/24.04/stretch_install_arduino.sh create mode 100755 factory/24.04/stretch_install_dev_tools.sh create mode 100755 factory/24.04/stretch_install_firmware.sh create mode 100755 factory/24.04/stretch_install_system.sh create mode 100644 factory/24.04/stretch_ros2_humble.repos create mode 100644 factory/24.04/sunshine.service create mode 100644 factory/24.04/sunshine_apps.json diff --git a/factory/24.04/hello_robot_audio.desktop b/factory/24.04/hello_robot_audio.desktop new file mode 100644 index 0000000..2d99cb8 --- /dev/null +++ b/factory/24.04/hello_robot_audio.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=hello_robot_audio +Exec=/usr/bin/hello_robot_audio.sh +Hidden=false +X-GNOME-Autostart-enabled=true diff --git a/factory/24.04/hello_robot_audio.sh b/factory/24.04/hello_robot_audio.sh new file mode 100755 index 0000000..b1a35ea --- /dev/null +++ b/factory/24.04/hello_robot_audio.sh @@ -0,0 +1,6 @@ +#! /bin/bash +sleep 5 +pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo +amixer set Master 80% +paplay --device=alsa_output.pci-0000_00_1f.3.analog-stereo /usr/share/sounds/ubuntu/stereo/desktop-login.ogg + diff --git a/factory/24.04/hello_robot_gamepad_teleop.desktop b/factory/24.04/hello_robot_gamepad_teleop.desktop new file mode 100644 index 0000000..924c0a1 --- /dev/null +++ b/factory/24.04/hello_robot_gamepad_teleop.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=hello_robot_gamepad_teleop +Exec=/usr/bin/hello_robot_gamepad_teleop.sh +Hidden=false +X-GNOME-Autostart-enabled=false diff --git a/factory/24.04/hello_robot_gamepad_teleop.sh b/factory/24.04/hello_robot_gamepad_teleop.sh new file mode 100755 index 0000000..eda051b --- /dev/null +++ b/factory/24.04/hello_robot_gamepad_teleop.sh @@ -0,0 +1,6 @@ +#! /bin/bash + +. /etc/hello-robot/hello-robot.conf +export HELLO_FLEET_ID HELLO_FLEET_ID +export HELLO_FLEET_PATH=$HOME/stretch_user +/usr/bin/python3 $HOME/.local/bin/stretch_gamepad_teleop.py &>> "$HOME/stretch_user/log/hello_robot_gamepad_teleop.`date '+%Y%m%d%H%M'`.log" diff --git a/factory/24.04/hello_robot_lrf_off.desktop b/factory/24.04/hello_robot_lrf_off.desktop new file mode 100644 index 0000000..aec0cb9 --- /dev/null +++ b/factory/24.04/hello_robot_lrf_off.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=hello_robot_lrf_off +Exec= /usr/bin/hello_robot_lrf_off.py +Hidden=false +X-GNOME-Autostart-enabled=true diff --git a/factory/24.04/hello_robot_lrf_off.py b/factory/24.04/hello_robot_lrf_off.py new file mode 100755 index 0000000..9edee8f --- /dev/null +++ b/factory/24.04/hello_robot_lrf_off.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +import serial +import time +#This script keeps the lrf port open while the computer is on +#This has the effect of keeping the DTR line high to the LRF, which in turn turns the motor off +#So long as one port is open on the serail the DTR remains high (see HUPcL in termios) +#Other programs can still open/close/read/write the port independent of this script +sp=serial.Serial('/dev/hello-lrf',115200,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1, dsrdtr=True) +while True: + time.sleep(1.0) diff --git a/factory/24.04/hello_robot_pimu_ping.desktop b/factory/24.04/hello_robot_pimu_ping.desktop new file mode 100644 index 0000000..dd16232 --- /dev/null +++ b/factory/24.04/hello_robot_pimu_ping.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=hello_robot_pimu_ping +Exec= /usr/bin/hello_robot_pimu_ping.sh +Hidden=false +X-GNOME-Autostart-enabled=true diff --git a/factory/24.04/hello_robot_pimu_ping.py b/factory/24.04/hello_robot_pimu_ping.py new file mode 100755 index 0000000..ae128bf --- /dev/null +++ b/factory/24.04/hello_robot_pimu_ping.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +import stretch_body.pimu +import time +#This script establishes communications with the Pimu at launch then shutsdown +#This has the effect of letting the Pimu know that Ubuntu Desktop is live (which drives its LightBar state machine) + +print('Starting up Pimu Ping on %s'%time.asctime()) +ping_success=False +for i in range(10): + p = stretch_body.pimu.Pimu() + if p.startup(): + print('Successful ping of Pimu on try %d'%i) + p.stop() + exit(0) + p.stop() + time.sleep(1.0) + +print('Failed to ping Pimu') diff --git a/factory/24.04/hello_robot_pimu_ping.sh b/factory/24.04/hello_robot_pimu_ping.sh new file mode 100755 index 0000000..a6fe92e --- /dev/null +++ b/factory/24.04/hello_robot_pimu_ping.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +. /etc/hello-robot/hello-robot.conf +export HELLO_FLEET_ID HELLO_FLEET_ID +export HELLO_FLEET_PATH=$HOME/stretch_user + +/usr/bin/python3 /usr/bin/hello_robot_pimu_ping.py &>> "$HOME/stretch_user/log/hello_robot_pimu_ping.`date '+%Y%m%d%H%M'`.log" diff --git a/factory/24.04/hello_robot_xbox_teleop.desktop b/factory/24.04/hello_robot_xbox_teleop.desktop new file mode 100644 index 0000000..8af1ad7 --- /dev/null +++ b/factory/24.04/hello_robot_xbox_teleop.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=hello_robot_xbox_teleop +Exec=/usr/bin/hello_robot_xbox_teleop.sh +Hidden=false +X-GNOME-Autostart-enabled=false diff --git a/factory/24.04/hello_robot_xbox_teleop.sh b/factory/24.04/hello_robot_xbox_teleop.sh new file mode 100755 index 0000000..6a95511 --- /dev/null +++ b/factory/24.04/hello_robot_xbox_teleop.sh @@ -0,0 +1,6 @@ +#! /bin/bash + +. /etc/hello-robot/hello-robot.conf +export HELLO_FLEET_ID HELLO_FLEET_ID +export HELLO_FLEET_PATH=$HOME/stretch_user +/usr/bin/python3 $HOME/.local/bin/stretch_xbox_controller_teleop.py &>> "$HOME/stretch_user/log/hello_robot_xbox_teleop.`date '+%Y%m%d%H%M'`.log" diff --git a/factory/24.04/hello_sudoers b/factory/24.04/hello_sudoers new file mode 100644 index 0000000..41457ab --- /dev/null +++ b/factory/24.04/hello_sudoers @@ -0,0 +1 @@ +ALL ALL=(ALL) NOPASSWD: /sbin/shutdown diff --git a/factory/24.04/iron2humble_migration.md b/factory/24.04/iron2humble_migration.md new file mode 100644 index 0000000..6722e3a --- /dev/null +++ b/factory/24.04/iron2humble_migration.md @@ -0,0 +1,45 @@ +# Iron -> Humble Migration + +The Stretch Install repo has switched the version of ROS2 installed with the Ubuntu 22.04 software stack. On Sept 7th, 2023, we switched it to ROS2 Iron. On Sept 14, 2023, we switched it back to ROS2 Humble. We expect some users to have upgraded in that week, so this guide serves to help migrate from Iron -> Humble. + +## Steps + + 0. Verify this guide applies to you + ``` + lsb_release -d + echo $ROS_DISTRO + ``` + You should see "Description: Ubuntu 22.04.* LTS" and "iron". If not, you do not need this guide. + + 1. Clone Stretch Install into your home folder: + ``` + cd ~/ + git clone https://github.com/hello-robot/stretch_install.git + cd ~/stretch_install + git pull + ``` + + 2. Run the system install script + ``` + ./factory/22.04/stretch_install_system.sh + ``` + The script can fails silently. If the last line printed out isn't "Install librealsense2 packages", something went wrong. Contact Hello Robot Support. + + 3. Update the .bashrc script + ``` + gedit ~/.bashrc + ``` + A text editor will open. Scroll to the bottom of the file, and look for a section called "STRETCH BASHRC SETUP". Under this section, you'll see a line that says `source /opt/ros/iron/setup.bash`. Change this line to `source /opt/ros/humble/setup.bash`. Next, look for a line that says `source $HOME/ament_ws/install/setup.bash` or similar. Delete this line. Now save the file and close the editor. + + 4. Refresh your terminal by closing your current terminal and opening a new one + + 5. Run the ament workspace creation script + ``` + cd ~/stretch_install + ./factory/22.04/stretch_create_ament_workspace.sh + ``` + The script can fails silently. If the last line printed out isn't "Setup calibrated robot URDF...", something went wrong. Contact Hello Robot Support. + + 6. Refresh your terminal by closing your current terminal and opening a new one + +Success! Your robot is now set up with ROS2 Humble correctly. diff --git a/factory/24.04/motd/00-header b/factory/24.04/motd/00-header new file mode 100644 index 0000000..407bfcf --- /dev/null +++ b/factory/24.04/motd/00-header @@ -0,0 +1,30 @@ +#!/bin/sh +# +# 00-header - create the header of the MOTD +# Copyright (C) 2009-2010 Canonical Ltd. +# +# Authors: Dustin Kirkland +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +[ -r /etc/lsb-release ] && . /etc/lsb-release + +if [ -z "$DISTRIB_DESCRIPTION" ] && [ -x /usr/bin/lsb_release ]; then + # Fall back to using the very slow lsb_release utility + DISTRIB_DESCRIPTION=$(lsb_release -s -d) +fi + +printf "Welcome to Stretch's %s Distribution\n" "$DISTRIB_RELEASE" +printf "based on %s (%s %s %s)\n" "$DISTRIB_DESCRIPTION" "$(uname -o)" "$(uname -r)" "$(uname -m)" diff --git a/factory/24.04/motd/10-help-text b/factory/24.04/motd/10-help-text new file mode 100644 index 0000000..c74d280 --- /dev/null +++ b/factory/24.04/motd/10-help-text @@ -0,0 +1,25 @@ +#!/bin/sh +# +# 10-help-text - print the help text associated with the distro +# Copyright (C) 2009-2010 Canonical Ltd. +# +# Authors: Dustin Kirkland , +# Brian Murray +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +printf "\n" +printf " * Documentation: https://docs.hello-robot.com/\n" +printf " * Support: https://forum.hello-robot.com/\n\n" diff --git a/factory/24.04/motd/10dpkg_options b/factory/24.04/motd/10dpkg_options new file mode 100644 index 0000000..5f58776 --- /dev/null +++ b/factory/24.04/motd/10dpkg_options @@ -0,0 +1,4 @@ +Dpkg::Options { + "--force-confdef"; + "--force-confold"; +} diff --git a/factory/24.04/package-lock.json b/factory/24.04/package-lock.json new file mode 100644 index 0000000..2a31535 --- /dev/null +++ b/factory/24.04/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "22.04", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/factory/24.04/stretch_about.png b/factory/24.04/stretch_about.png new file mode 100644 index 0000000000000000000000000000000000000000..eeee30a6104fa067068edd86f949902c2b4b2693 GIT binary patch literal 39299 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

DccU795iZBr5r`Z@yf;@ytD>y=O*eF80>;9m{P! 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 literal 0 HcmV?d00001 diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh new file mode 100755 index 0000000..579216b --- /dev/null +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -0,0 +1,130 @@ +#!/bin/bash +set -e +export COLCON_EXTENSION_BLOCKLIST=colcon_core.event_handler.desktop_notification + +REDIRECT_LOGDIR="$HOME/stretch_user/log" +AMENT_WSDIR="$HOME/ament_ws" +while getopts l:w: opt; do + case $opt in + l) + if [[ -d $OPTARG ]]; then + REDIRECT_LOGDIR=$OPTARG + fi + ;; + w) + AMENT_WSDIR=$OPTARG + ;; + esac +done +REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_create_ament_workspace.`date '+%Y%m%d%H%M'`_redirected.txt" + +echo "###########################################" +echo "CREATING HUMBLE AMENT WORKSPACE at $AMENT_WSDIR" +echo "###########################################" + +echo "Ensuring correct version of ROS is sourced..." +if [[ $ROS_DISTRO && ! $ROS_DISTRO = "humble" ]]; then + echo "Cannot create workspace while a conflicting ROS version is sourced. Exiting." + exit 1 +fi +source /opt/ros/humble/setup.bash + +if [[ -d $AMENT_WSDIR ]]; then + echo "You are about to delete and replace the existing ament workspace. If you have any personal data in the workspace, please create a back up before proceeding." + prompt_yes_no(){ + read -p "Do you want to continue? Press (y/n for yes/no): " x + if [ $x = "n" ]; then + echo "Exiting the script." + exit 1 + elif [ $x = "y" ]; then + echo "Continuing to create a new ament workspace." + else + echo "Press 'y' for yes or 'n' for no." + prompt_yes_no + fi + } + prompt_yes_no +fi + +echo "Downgrade to numpy 1.26.4..." +pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE + +export PATH=${PATH}:~/.local/bin +. /etc/hello-robot/hello-robot.conf +export HELLO_FLEET_ID=$HELLO_FLEET_ID +export HELLO_FLEET_PATH=${HOME}/stretch_user +echo "Updating rosdep indices..." +rosdep update --include-eol-distros &>> $REDIRECT_LOGFILE +echo "Deleting $AMENT_WSDIR if it already exists..." +sudo rm -rf $AMENT_WSDIR +echo "Creating the workspace directory..." +mkdir -p $AMENT_WSDIR/src +echo "Cloning the workspace's packages..." +cd $AMENT_WSDIR/src +vcs import --input ~/stretch_install/factory/22.04/stretch_ros2_humble.repos &>> $REDIRECT_LOGFILE +echo "Fetch ROS packages' dependencies (this might take a while)..." +cd $AMENT_WSDIR/ +# The rosdep flags below have been chosen very carefully. Please review the docs before changing them. +# https://docs.ros.org/en/independent/api/rosdep/html/commands.html +rosdep install --rosdistro=humble -iy --skip-keys="librealsense2 realsense2_camera" --from-paths src &>> $REDIRECT_LOGFILE +sudo apt remove -y ros-humble-librealsense2 ros-humble-realsense2-camera ros-humble-realsense2-camera-msgs &>> $REDIRECT_LOGFILE +pip3 cache purge &>> $REDIRECT_LOGFILE + +echo "Install web interface dependencies..." +cd $AMENT_WSDIR/src/stretch_web_teleop +pip3 install -r requirements.txt &>> $REDIRECT_LOGFILE +npm install --force &>> $REDIRECT_LOGFILE +npx playwright install &>> $REDIRECT_LOGFILE +echo "Generating web interface certs..." +cd $AMENT_WSDIR/src/stretch_web_teleop/certificates +curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" &>> $REDIRECT_LOGFILE +chmod +x mkcert-v*-linux-amd64 +sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert +CAROOT=`pwd` mkcert --install &>> $REDIRECT_LOGFILE +mkdir -p ~/.local/share/mkcert +rm -rf ~/.local/share/mkcert/root* +cp root* ~/.local/share/mkcert +mkcert ${HELLO_FLEET_ID} ${HELLO_FLEET_ID}.local ${HELLO_FLEET_ID}.dev localhost 127.0.0.1 0.0.0.0 ::1 &>> $REDIRECT_LOGFILE +rm mkcert-v*-linux-amd64 +cd $AMENT_WSDIR/src/stretch_web_teleop +touch .env +echo certfile=${HELLO_FLEET_ID}+6.pem >> .env +echo keyfile=${HELLO_FLEET_ID}+6-key.pem >> .env +cd $AMENT_WSDIR/ + +echo "Install FUNMAP dependencies..." +curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE +cd $AMENT_WSDIR/src/stretch_ros2/stretch_funmap +uv venv --system-site-packages --allow-existing .venv &>> $REDIRECT_LOGFILE +uv sync --frozen &>> $REDIRECT_LOGFILE +echo "Compile cython modules..." +pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP +uv run cythonize stretch_funmap/cython_min_cost_path.pyx -3 -i &>> $REDIRECT_LOGFILE +cd $AMENT_WSDIR/ + +echo "Compile the workspace (this might take a while)..." +pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP +pip3 uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE +colcon build --symlink-install &>> $REDIRECT_LOGFILE +echo "Source setup.bash file..." +source $AMENT_WSDIR/install/setup.bash +echo "Updating port privledges..." +sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80 &>> $REDIRECT_LOGFILE +echo net.ipv4.ip_unprivileged_port_start=80 | sudo tee --append /etc/sysctl.d/99-sysctl.conf &>> $REDIRECT_LOGFILE +echo "Update ~/.bashrc dotfile to source workspace..." +echo "source $AMENT_WSDIR/install/setup.bash" >> ~/.bashrc +echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc +echo "Updating meshes and xacros to ROS from stretch_urdf package." +/home/$USER/.local/bin/stretch_urdf_ros_update.py -y -v >> $REDIRECT_LOGFILE +echo "Setup uncalibrated robot URDF..." +ros2 run stretch_calibration update_uncalibrated_urdf >> $REDIRECT_LOGFILE +echo "Setup calibrated robot URDF..." +ros2 run stretch_calibration update_with_most_recent_calibration >> $REDIRECT_LOGFILE +colcon build --symlink-install &>> $REDIRECT_LOGFILE + +echo "Amend FUNMAP executables to use venv..." +pip3 install -U hello-robot-stretch-factory &>> $REDIRECT_LOGFILE # Necessary for the below CLI +REx_amend_venv_execs.py stretch_funmap &>> $REDIRECT_LOGFILE + +echo "Downgrade to numpy 1.26.4..." +pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE diff --git a/factory/24.04/stretch_initial_setup.sh b/factory/24.04/stretch_initial_setup.sh new file mode 100755 index 0000000..68d9a82 --- /dev/null +++ b/factory/24.04/stretch_initial_setup.sh @@ -0,0 +1,186 @@ +#!/bin/bash +set -e + +do_factory_install=${1:-'false'} + +if $do_factory_install; then + echo "WARNING: Running a FACTORY install. This is only meant to be run at Hello Robot HQ." +fi +echo "WARNING: Run this installation for fresh Ubuntu installs only." + +# Process automation variables from parent script +if [[ -n "$SETUP_FLEET_ID" ]]; then + echo "Using SETUP_FLEET_ID from environment: $SETUP_FLEET_ID" + HELLO_FLEET_ID="$SETUP_FLEET_ID" +elif [[ -n "$SETUP_MODEL" && -n "$SETUP_FLEET_NUMBER" ]]; then + HELLO_FLEET_ID="${SETUP_MODEL}-${SETUP_FLEET_NUMBER}" + echo "Using HELLO_FLEET_ID constructed from environment variables: $HELLO_FLEET_ID" +else + # Original interactive prompt if no automation variables provided + echo "Checking ~/.bashrc doesn't already define HELLO_FLEET_ID..." + if [[ $HELLO_FLEET_ID ]]; then + echo "Expecting var HELLO_FLEET_ID to be undefined. Check end of ~/.bashrc file, delete all lines in 'STRETCH BASHRC SETUP' section, and open a new terminal. Exiting." + exit 1 + fi + + PS3="Select model type: " + + select model in stretch-re1 stretch-re2 stretch-se3 + do + echo "Selected model: $model" + + if [[ "$model" == "stretch-re1" ]] + then + break + fi + + if [[ "$model" == "stretch-re2" ]] + then + break + fi + + if [[ "$model" == "stretch-se3" ]] + then + break + fi + + done + + pre=$model"-" + + echo -n "Enter fleet id xxxx for $pre""xxxx> " + read id + if [[ ! $id =~ ^[0-9]{4}$ ]]; then + echo "Input should be four digits. Exiting." + exit 1 + fi + + HELLO_FLEET_ID="$pre$id" +fi + +echo "HELLO_FLEET_ID will be $HELLO_FLEET_ID." +echo "Plug in charger & attach clip-clamp before proceeding." +read -p "Ready to proceed with installation (y/n)? " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Confirmation failed. Will not proceed with installation." + exit 1 +fi + +DIR=`pwd` +echo "Checking Stretch Install cloned to right place..." +if [[ ! -d "$HOME/stretch_install" ]]; then + echo "Expecting Stretch Install to be in home folder. Exiting." + exit 1 +fi + +if [ $do_factory_install = 'false' ]; then + echo "Checking robot calibration data in home folder..." + if [[ ! -d "$HOME/$HELLO_FLEET_ID" ]]; then + echo "Expecting robot calibration $HELLO_FLEET_ID to be present in the the home folder. Exiting." + exit 1 + fi +fi +chmod -R a+r $HOME/$HELLO_FLEET_ID + +echo "Waiting to get online..." +while ! timeout 0.2 ping -c 1 -n google.com &> /dev/null +do + sleep 1 + printf "%c" "." +done + +echo "Checking install repo is up-to-date..." +git remote update &> /dev/null +LOCAL=$(git rev-parse @) +REMOTE=$(git rev-parse @{u}) +if [ ! $LOCAL = $REMOTE ]; then + echo "Repo not up-to-date. Please perform a 'git pull'. Exiting." + exit 1 +fi + +echo "Waiting for apt lock..." +while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do + sleep 1 + printf "%c" "." +done + +echo "Setting up /etc/hello-robot directory..." +echo "HELLO_FLEET_ID=$HELLO_FLEET_ID">>hello-robot.conf +if [ -d "/etc/hello-robot" ]; then + sudo rm -rf /etc/hello-robot +fi +sudo mkdir /etc/hello-robot +sudo mv hello-robot.conf /etc/hello-robot +sudo cp $DIR/stretch_about.png /etc/hello-robot/ + +if $do_factory_install; then + echo "Fetching robot's calibration data from Github..." + cd ~/ + git config --global credential.helper store + git clone https://github.com/hello-robot/stretch_fleet.git + sudo cp -rf ~/stretch_fleet/robots/$HELLO_FLEET_ID /etc/hello-robot/ + rm -rf stretch_fleet +else + echo "Fetching robot's calibration data locally from $HOME/$HELLO_FLEET_ID directory..." + sudo cp -rf ~/$HELLO_FLEET_ID /etc/hello-robot + rm -rf ~/$HELLO_FLEET_ID +fi + +echo "Setting up UDEV rules..." +sudo cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d +sudo udevadm control --reload + +echo "Allow shutdown without password..." +sudo cp $DIR/hello_sudoers /etc/sudoers.d/ + +echo "Setting up startup scripts..." +mkdir -p ~/.local/bin +sudo cp $DIR/hello_robot_audio.sh /usr/bin/ +sudo cp $DIR/hello_robot_lrf_off.py /usr/bin/ +sudo cp $DIR/hello_robot_pimu_ping.py /usr/bin/ +sudo cp $DIR/hello_robot_pimu_ping.sh /usr/bin/ +sudo cp $DIR/hello_robot_gamepad_teleop.sh /usr/bin/ + +echo "Setting up apt retries..." +echo 'Acquire::Retries "3";' > 80-retries +sudo mv 80-retries /etc/apt/apt.conf.d/ + +echo "Prevent screen dimming..." +gsettings set org.gnome.desktop.session idle-delay 0 &> /dev/null || true +gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 0 &> /dev/null || true +gsettings set org.gnome.settings-daemon.plugins.power idle-dim false &> /dev/null || true + +echo "Setting up motd..." +if [ -d /etc/update-motd.d/ ]; then + sudo cp $DIR/motd/00-header /etc/update-motd.d/00-header + sudo chmod +x /etc/update-motd.d/00-header + sudo cp $DIR/motd/10-help-text /etc/update-motd.d/10-help-text + sudo chmod +x /etc/update-motd.d/10-help-text + sudo cp $DIR/motd/10dpkg_options /etc/apt/apt.conf.d/10dpkg_options +fi +if [ -f /etc/update-motd.d/50-motd-news ]; then + sudo chmod -x /etc/update-motd.d/50-motd-news +fi +if [ -f /etc/update-motd.d/88-esm-announce ]; then + sudo chmod -x /etc/update-motd.d/88-esm-announce +fi +if [ -f /etc/update-motd.d/90-updates-available ]; then + sudo chmod -x /etc/update-motd.d/90-updates-available +fi +if [ -f /etc/update-motd.d/91-release-upgrade ]; then + sudo chmod -x /etc/update-motd.d/91-release-upgrade +fi +if [ -f /etc/update-motd.d/95-hwe-eol ]; then + sudo chmod -x /etc/update-motd.d/95-hwe-eol +fi + +echo "Switching from Wayland to X11..." +if [ -f /etc/gdm3/custom.conf ]; then + sudo sed -i -e 's/#WaylandEnable=false/WaylandEnable=false/g' /etc/gdm3/custom.conf +fi + +echo 'Blacklisting hid_nintendo that conflicts with Xpad driver...' +echo '# Nintendo conflicts with Xpad controller driver' | sudo tee -a /etc/modprobe.d/blacklist.conf +echo 'blacklist hid_nintendo' | sudo tee -a /etc/modprobe.d/blacklist.conf +sudo update-initramfs -u diff --git a/factory/24.04/stretch_install_arduino.sh b/factory/24.04/stretch_install_arduino.sh new file mode 100755 index 0000000..435ff79 --- /dev/null +++ b/factory/24.04/stretch_install_arduino.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=$HOME/.local/bin/ sh +$HOME/.local/bin/arduino-cli config init +$HOME/.local/bin/arduino-cli core install arduino:samd@1.6.21 +sed -i -e 's#Arduino#repos/stretch_firmware/arduino#g' ~/.arduino15/arduino-cli.yaml + diff --git a/factory/24.04/stretch_install_dev_tools.sh b/factory/24.04/stretch_install_dev_tools.sh new file mode 100755 index 0000000..03bae35 --- /dev/null +++ b/factory/24.04/stretch_install_dev_tools.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -e + +REDIRECT_LOGDIR="$HOME/stretch_user/log" +if getopts ":l:" opt && [[ $opt == "l" && -d $OPTARG ]]; then + REDIRECT_LOGDIR=$OPTARG +fi +REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_install_dev_tools.`date '+%Y%m%d%H%M'`_redirected.txt" + +echo "#############################################" +echo "INSTALLATION OF DEV TOOLS FOR HELLO ROBOT INTERNAL PRODUCTION" +echo "#############################################" + +echo "Install gh" +function install_gh { + type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) + curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && sudo apt update \ + && sudo apt install gh -y +} +install_gh &>> $REDIRECT_LOGFILE + +echo "Install Typora" +sudo snap install typora >> $REDIRECT_LOGFILE + +echo "Install PyCharm" +sudo snap install pycharm-community --classic >> $REDIRECT_LOGFILE + +echo "Install VS Code" +sudo snap install code --classic >> $REDIRECT_LOGFILE + +echo "Install tools for system QC and bringup" +pip3 install -q --no-warn-script-location twine &>> $REDIRECT_LOGFILE +pip3 install -q --no-warn-script-location gspread &>> $REDIRECT_LOGFILE +pip3 install -q --no-warn-script-location gspread-formatting &>> $REDIRECT_LOGFILE +pip3 install -q --no-warn-script-location oauth2client rsa==3.4 &>> $REDIRECT_LOGFILE +pip3 install -q --no-warn-script-location mkdocs mkdocs-material mkdocstrings==0.17.0 pytkdocs[numpy-style] jinja2==3.0.3 &>> $REDIRECT_LOGFILE + +echo "Cloning repos" +cd ~/repos/ +git clone https://github.com/hello-robot/stretch_install.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_factory.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_body.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_firmware.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_fleet.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_production_tools.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_production_data.git >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/hello-robot.github.io >> $REDIRECT_LOGFILE +git clone https://github.com/hello-robot/stretch_docs >> $REDIRECT_LOGFILE + +echo "Install stretch_production_tools" +cd stretch_production_tools/python +pip3 install -e . + +# update .bashrc to add body code directory to the Python path + +#echo "export PATH=\${PATH}:~/repos/stretch_body/python/bin" >> ~/.bashrc +#echo "export PATH=\${PATH}:~/repos/stretch_factory/python/tools" >> ~/.bashrc + +#echo "export PYTHONPATH=\${PYTHONPATH}:~/repos/stretch_factory/python/tools" >> ~/.bashrc +#echo "export PYTHONPATH=\${PYTHONPATH}:~/repos/stretch_body/python" >> ~/.bashrc + +#echo "Source new bashrc now...Done." +#echo "" diff --git a/factory/24.04/stretch_install_firmware.sh b/factory/24.04/stretch_install_firmware.sh new file mode 100755 index 0000000..a701d9f --- /dev/null +++ b/factory/24.04/stretch_install_firmware.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +REDIRECT_LOGDIR="$HOME/stretch_user/log" +if getopts ":l:" opt && [[ $opt == "l" && -d $OPTARG ]]; then + REDIRECT_LOGDIR=$OPTARG +fi +REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_install_firmware.`date '+%Y%m%d%H%M'`_redirected.txt" + +echo "###########################################" +echo "INSTALLATION OF STRETCH FIRMWARE" +echo "###########################################" +source ~/.bashrc +echo "Read ttyACMx mapping" +REx_firmware_flash.py --map &>> $REDIRECT_LOGFILE +echo "Perform firmware update (this might take a while)" +REx_firmware_updater.py --install --no_prompts &>> $REDIRECT_LOGFILE diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh new file mode 100755 index 0000000..ff02d3c --- /dev/null +++ b/factory/24.04/stretch_install_system.sh @@ -0,0 +1,178 @@ +#!/bin/bash +set -e + +REDIRECT_LOGDIR="$HOME/stretch_user/log" +if getopts ":l:" opt && [[ $opt == "l" && -d $OPTARG ]]; then + REDIRECT_LOGDIR=$OPTARG +fi +REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_install_system.`date '+%Y%m%d%H%M'`_redirected.txt" + +function install { + sudo apt-get install -y "$@" >> $REDIRECT_LOGFILE +} + +echo "###########################################" +echo "INSTALLATION OF SYSTEM WIDE PACKAGES" +echo "###########################################" +echo "Apt update & upgrade (this might take a while)" +sudo apt-add-repository universe -y >> $REDIRECT_LOGFILE +sudo apt-get --yes update >> $REDIRECT_LOGFILE +sudo apt-get --yes upgrade &>> $REDIRECT_LOGFILE +echo "Install zip & unzip" +install zip unzip +echo "Install Curl" +install curl +echo "Install ca-certificates" +install ca-certificates +echo "Install gnupg" +install gnupg +echo "Install Git" +install git +echo "Install rpl" +install rpl +echo "Install ipython3" +install ipython3 +echo "Install pip3" +install python3-pip +echo "Install Emacs packages" +install emacs yaml-mode +echo "Install nettools" +install net-tools +echo "Install wget" +install wget +echo "Install vim" +install vim +echo "Install pyserial" +install python3-serial +echo "Install Port Audio" +install portaudio19-dev +echo "Install lm-sensors & nvme-cli" +install lm-sensors +install nvme-cli +echo "Install cheese for camera testing" +install cheese +echo "Install SSH Server" +install ssh +echo "Install Chromium" +install chromium-browser +echo "Install htop" +install htop +echo "Install Ubuntu Sounds" +install ubuntu-sounds +echo "Install BleachBit" +install bleachbit +echo "Install APT HTTPS" +install apt-transport-https +echo "Install Network Security Services libraries" +install libnss3-tools +echo "Install uv" +curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE +echo "" + +# https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html +echo "###########################################" +echo "INSTALLATION OF ROS 2 HUMBLE" +echo "###########################################" +echo "Setting up keys" +sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg +echo "Setting up sources.list" +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null +echo "Apt update" +sudo apt-get --yes update >> $REDIRECT_LOGFILE +echo "Install ROS 2 Humble (this might take a while)" +install ros-humble-desktop-full +# https://discourse.ros.org/t/ros-developer-tools-now-in-binary-form/29802 +echo "Install ROS 2 Dev Tools" +install ros-dev-tools +echo "Install colcon" +install python3-colcon-common-extensions +install python3-colcon-clean +echo "Install rosdep" +install python3-rosdep +echo "Configure rosdep" +if [ -f "/etc/ros/rosdep/sources.list.d/20-default.list" ]; then + sudo rm /etc/ros/rosdep/sources.list.d/20-default.list +fi +sudo rosdep init >> $REDIRECT_LOGFILE +echo "Install vcstool" +install python3-vcstool +echo "" + +echo "###########################################" +echo "INSTALLATION OF ADDITIONAL ROS HUMBLE PKGS" +echo "###########################################" +echo "Install packages to work with URDFs" +install liburdfdom-tools meshlab +install ros-humble-urdfdom-py +echo "Install joint state GUI package" +install ros-humble-joint-state-publisher-gui +echo "Install IMU visualization plugin for RViz and IMU filter" +install ros-humble-rviz-imu-plugin ros-humble-imu-filter-madgwick +echo "Install robot localization package for use with IMU and wheel odometry" +install ros-humble-robot-localization +echo "Install teleop packages" +install ros-humble-teleop-twist-keyboard +echo "" + +echo "###########################################" +echo "INSTALLATION OF INTEL D435i" +echo "###########################################" +echo "Register the librealsense APT server's public key" +function register_librealsense_apt_server { + curl -sSf https://librealsense.intel.com/Debian/librealsense.pgp | sudo tee /etc/apt/keyrings/librealsense.pgp > /dev/null +} +register_librealsense_apt_server &>> $REDIRECT_LOGFILE +echo "Add the librealsense APT server to the list of APT repositories" +function add_librealsense_apt_server { + echo "deb [signed-by=/etc/apt/keyrings/librealsense.pgp] https://librealsense.intel.com/Debian/apt-repo `lsb_release -cs` main" | sudo tee /etc/apt/sources.list.d/librealsense.list +} +add_librealsense_apt_server &>> $REDIRECT_LOGFILE +echo "Remove old records in case of upgrading" +sudo rm -f /etc/apt/sources.list.d/realsense-public.list +echo "Apt update" +sudo apt-get --yes update >> $REDIRECT_LOGFILE +echo "Install librealsense2 packages" +install librealsense2 librealsense2-udev-rules librealsense2-utils librealsense2-dev librealsense2-dbg +echo "" + +echo "###########################################" +echo "INSTALLATION OF WEB INTERFACE" +echo "###########################################" +echo "Register the nodesource APT server's public key" +function register_nodesource_apt_server { + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg +} +register_nodesource_apt_server &>> $REDIRECT_LOGFILE +echo "Add the nodesource APT server to the list of APT respositories" +function add_nodesource_apt_server { + NODE_MAJOR=21 + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg, arch=amd64] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list +} +add_nodesource_apt_server &>> $REDIRECT_LOGFILE +echo "Apt update" +sudo apt-get --yes update >> $REDIRECT_LOGFILE +echo "Install NodeJS" +install nodejs +echo "Install PyPCL and PyKDL" +install python3-pcl python3-pykdl screen +echo "Install PM2" +sudo npm install -g pm2 &>> $REDIRECT_LOGFILE +echo "" + +echo "###########################################" +echo "INSTALLATION OF REMOTE DESKTOP" +echo "###########################################" +echo "Install Sunshine Remote Desktop Server" +wget https://github.com/LizardByte/Sunshine/releases/download/v0.21.0/sunshine-ubuntu-22.04-amd64.deb -O /tmp/sunshine.deb &>> $REDIRECT_LOGFILE +install /tmp/sunshine.deb +echo "Add Sunshine uinput udev rule" +function add_sunshine_uinput_rule { + echo 'KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/85-sunshine.rules +} +add_sunshine_uinput_rule &>> $REDIRECT_LOGFILE +echo "Setup Sunshine apps" +sudo cp ./sunshine_apps.json /usr/share/sunshine/apps.json +echo "Setup Sunshine systemd unit" +mkdir -p ~/.config/systemd/user/ +cp ./sunshine.service ~/.config/systemd/user/sunshine.service + diff --git a/factory/24.04/stretch_ros2_humble.repos b/factory/24.04/stretch_ros2_humble.repos new file mode 100644 index 0000000..87f145b --- /dev/null +++ b/factory/24.04/stretch_ros2_humble.repos @@ -0,0 +1,42 @@ +repositories: + ros2_numpy: + type: git + url: https://github.com/hello-binit/ros2_numpy + version: humble + realsense-ros: + type: git + url: https://github.com/IntelRealSense/realsense-ros.git + version: 4.55.1 + stretch_ros2: + type: git + url: https://github.com/hello-robot/stretch_ros2.git + version: humble + sllidar_ros2: + type: git + url: https://github.com/Slamtec/sllidar_ros2.git + version: 34300099fadfc772965962dec837bf436706188f + stretch_tutorials: + type: git + url: https://github.com/hello-robot/stretch_tutorials.git + version: humble + respeaker_ros2: + type: git + url: https://github.com/hello-binit/respeaker_ros2.git + version: humble + audio_common: + type: git + url: https://github.com/hello-binit/audio_common.git + version: humble + stretch_web_teleop: + type: git + url: https://github.com/hello-robot/stretch_web_teleop.git + version: master + rosbridge_suite: + type: git + url: https://github.com/hello-vinitha/rosbridge_suite.git + version: ros2-actions + tf2_web_republisher_py: + type: git + url: https://github.com/hello-vinitha/tf2_web_republisher_py.git + version: master + diff --git a/factory/24.04/sunshine.service b/factory/24.04/sunshine.service new file mode 100644 index 0000000..f10fb6e --- /dev/null +++ b/factory/24.04/sunshine.service @@ -0,0 +1,12 @@ +[Unit] +Description=Sunshine self-hosted game stream host for Moonlight. +StartLimitIntervalSec=500 +StartLimitBurst=5 + +[Service] +ExecStart=/usr/bin/sunshine +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=graphical-session.target diff --git a/factory/24.04/sunshine_apps.json b/factory/24.04/sunshine_apps.json new file mode 100644 index 0000000..a5d8244 --- /dev/null +++ b/factory/24.04/sunshine_apps.json @@ -0,0 +1,17 @@ +{ + "env": { + "PATH": "$(PATH):$(HOME)\/.local\/bin" + }, + "apps": [ + { + "name": "Desktop", + "image-path": "desktop.png", + "prep-cmd": [ + { + "do": "sh -c \"REx_xrandr_display.py --set ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}x${SUNSHINE_CLIENT_FPS}\"", + "undo": "sh -c \"REx_xrandr_display.py --revert\"" + } + ] + } + ] +} From d1b19572eb5fbcfb652073666904e5d8c55be277 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 12:26:46 -0400 Subject: [PATCH 02/27] Remove unneeded files --- factory/24.04/iron2humble_migration.md | 45 -------------------------- factory/24.04/package-lock.json | 6 ---- 2 files changed, 51 deletions(-) delete mode 100644 factory/24.04/iron2humble_migration.md delete mode 100644 factory/24.04/package-lock.json diff --git a/factory/24.04/iron2humble_migration.md b/factory/24.04/iron2humble_migration.md deleted file mode 100644 index 6722e3a..0000000 --- a/factory/24.04/iron2humble_migration.md +++ /dev/null @@ -1,45 +0,0 @@ -# Iron -> Humble Migration - -The Stretch Install repo has switched the version of ROS2 installed with the Ubuntu 22.04 software stack. On Sept 7th, 2023, we switched it to ROS2 Iron. On Sept 14, 2023, we switched it back to ROS2 Humble. We expect some users to have upgraded in that week, so this guide serves to help migrate from Iron -> Humble. - -## Steps - - 0. Verify this guide applies to you - ``` - lsb_release -d - echo $ROS_DISTRO - ``` - You should see "Description: Ubuntu 22.04.* LTS" and "iron". If not, you do not need this guide. - - 1. Clone Stretch Install into your home folder: - ``` - cd ~/ - git clone https://github.com/hello-robot/stretch_install.git - cd ~/stretch_install - git pull - ``` - - 2. Run the system install script - ``` - ./factory/22.04/stretch_install_system.sh - ``` - The script can fails silently. If the last line printed out isn't "Install librealsense2 packages", something went wrong. Contact Hello Robot Support. - - 3. Update the .bashrc script - ``` - gedit ~/.bashrc - ``` - A text editor will open. Scroll to the bottom of the file, and look for a section called "STRETCH BASHRC SETUP". Under this section, you'll see a line that says `source /opt/ros/iron/setup.bash`. Change this line to `source /opt/ros/humble/setup.bash`. Next, look for a line that says `source $HOME/ament_ws/install/setup.bash` or similar. Delete this line. Now save the file and close the editor. - - 4. Refresh your terminal by closing your current terminal and opening a new one - - 5. Run the ament workspace creation script - ``` - cd ~/stretch_install - ./factory/22.04/stretch_create_ament_workspace.sh - ``` - The script can fails silently. If the last line printed out isn't "Setup calibrated robot URDF...", something went wrong. Contact Hello Robot Support. - - 6. Refresh your terminal by closing your current terminal and opening a new one - -Success! Your robot is now set up with ROS2 Humble correctly. diff --git a/factory/24.04/package-lock.json b/factory/24.04/package-lock.json deleted file mode 100644 index 2a31535..0000000 --- a/factory/24.04/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "22.04", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} From d5226526e3088a4fb8cc381cf9dcfc8054be459f Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:07:14 -0400 Subject: [PATCH 03/27] Install ROS2 Jazzy --- factory/24.04/stretch_install_system.sh | 22 ++++++++++++++-------- stretch_new_user_install.sh | 4 ++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index ff02d3c..38717b2 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -69,19 +69,25 @@ echo "Install uv" curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE echo "" -# https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html +# https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debs.html echo "###########################################" -echo "INSTALLATION OF ROS 2 HUMBLE" +echo "INSTALLATION OF ROS 2 JAZZY" echo "###########################################" -echo "Setting up keys" -sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg -echo "Setting up sources.list" -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null +echo "Install ros-apt-source" +function install_ros_apt_source { + export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') + echo $ROS_APT_SOURCE_VERSION + curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" + sudo dpkg -i /tmp/ros2-apt-source.deb +} +install_ros_apt_source &>> $REDIRECT_LOGFILE echo "Apt update" sudo apt-get --yes update >> $REDIRECT_LOGFILE -echo "Install ROS 2 Humble (this might take a while)" -install ros-humble-desktop-full +echo "Install ROS 2 Jazzy (this might take a while)" +install ros-jazzy-desktop-full # https://discourse.ros.org/t/ros-developer-tools-now-in-binary-form/29802 +echo "Install Zenoh middleware" +install ros-jazzy-rmw-zenoh-cpp echo "Install ROS 2 Dev Tools" install ros-dev-tools echo "Install colcon" diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index 497e2be..e1fb7e9 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -43,6 +43,10 @@ else echo "export _colcon_cd_root=${HOME}/ament_ws" >> ~/.bashrc echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc fi + elif [[ $factory_osdir = "24.04" ]]; then + echo "export RMW_IMPLEMENTATION=rmw_zenoh_cpp" >> ~/.bashrc + echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc + fi fi echo "Prevent screen dimming..." From 79f82f5ae25b66edc5be1f14fafca895479ef2c1 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:11:26 -0400 Subject: [PATCH 04/27] Add additional jazzy pkgs --- factory/24.04/stretch_install_system.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index 38717b2..226fe74 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -105,19 +105,19 @@ install python3-vcstool echo "" echo "###########################################" -echo "INSTALLATION OF ADDITIONAL ROS HUMBLE PKGS" +echo "INSTALLATION OF ADDITIONAL ROS JAZZY PKGS" echo "###########################################" echo "Install packages to work with URDFs" install liburdfdom-tools meshlab -install ros-humble-urdfdom-py +install ros-jazzy-urdfdom-py echo "Install joint state GUI package" -install ros-humble-joint-state-publisher-gui +install ros-jazzy-joint-state-publisher-gui echo "Install IMU visualization plugin for RViz and IMU filter" -install ros-humble-rviz-imu-plugin ros-humble-imu-filter-madgwick +install ros-jazzy-rviz-imu-plugin ros-jazzy-imu-filter-madgwick echo "Install robot localization package for use with IMU and wheel odometry" -install ros-humble-robot-localization +install ros-jazzy-robot-localization echo "Install teleop packages" -install ros-humble-teleop-twist-keyboard +install ros-jazzy-teleop-twist-keyboard echo "" echo "###########################################" From 7dd52376391448500e0848d1e3c4f1c86e36fe56 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:18:05 -0400 Subject: [PATCH 05/27] Switch Sunshine to 24.04 --- factory/24.04/stretch_install_system.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index 226fe74..46f6f72 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -121,7 +121,7 @@ install ros-jazzy-teleop-twist-keyboard echo "" echo "###########################################" -echo "INSTALLATION OF INTEL D435i" +echo "INSTALLATION OF INTEL REALSENSE" echo "###########################################" echo "Register the librealsense APT server's public key" function register_librealsense_apt_server { @@ -169,7 +169,7 @@ echo "###########################################" echo "INSTALLATION OF REMOTE DESKTOP" echo "###########################################" echo "Install Sunshine Remote Desktop Server" -wget https://github.com/LizardByte/Sunshine/releases/download/v0.21.0/sunshine-ubuntu-22.04-amd64.deb -O /tmp/sunshine.deb &>> $REDIRECT_LOGFILE +wget https://github.com/LizardByte/Sunshine/releases/download/v2025.721.150932/sunshine-ubuntu-24.04-amd64.deb -O /tmp/sunshine.deb &>> $REDIRECT_LOGFILE install /tmp/sunshine.deb echo "Add Sunshine uinput udev rule" function add_sunshine_uinput_rule { From df678bea70b26a18ab43c42934e08d0b4aeb3cd0 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:38:00 -0400 Subject: [PATCH 06/27] Add 24.04 to switch statements --- stretch_new_robot_install.sh | 2 +- stretch_new_user_install.sh | 7 ++++--- stretch_update_ros_workspace.sh | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stretch_new_robot_install.sh b/stretch_new_robot_install.sh index 94699c8..fde68cc 100755 --- a/stretch_new_robot_install.sh +++ b/stretch_new_robot_install.sh @@ -100,7 +100,7 @@ fi source /etc/os-release factory_osdir="$VERSION_ID" -if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04)$ ]]; then +if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04|24.04)$ ]]; then echo "Could not identify OS. Please contact Hello Robot Support." exit 1 fi diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index e1fb7e9..17b03c5 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -9,7 +9,7 @@ REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_new_user_install.`date '+%Y%m%d%H%M'` source /etc/os-release factory_osdir="$VERSION_ID" -if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04)$ ]]; then +if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04|24.04)$ ]]; then echo "Could not identify OS. Please contact Hello Robot Support." exit 1 fi @@ -42,7 +42,6 @@ else elif [[ $factory_osdir = "22.04" ]]; then echo "export _colcon_cd_root=${HOME}/ament_ws" >> ~/.bashrc echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc - fi elif [[ $factory_osdir = "24.04" ]]; then echo "export RMW_IMPLEMENTATION=rmw_zenoh_cpp" >> ~/.bashrc echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc @@ -121,7 +120,7 @@ if [[ $factory_osdir = "18.04" ]]; then echo "Install opencv-python-inference-engine" python3 -m pip -q install --no-warn-script-location opencv-python-inference-engine &>> $REDIRECT_LOGFILE echo "" -elif [[ $factory_osdir = "20.04" || $factory_osdir = "22.04" ]]; then +elif [[ $factory_osdir = "20.04" || $factory_osdir = "22.04" || $factory_osdir = "24.04" ]]; then echo "###########################################" echo "INSTALLATION OF USER LEVEL PIP3 PACKAGES" echo "###########################################" @@ -199,4 +198,6 @@ elif [[ $factory_osdir = "20.04" ]]; then ~/stretch_install/factory/$factory_osdir/stretch_create_catkin_workspace.sh -w "$HOME/catkin_ws" -l $REDIRECT_LOGDIR elif [[ $factory_osdir = "22.04" ]]; then ~/stretch_install/factory/$factory_osdir/stretch_create_ament_workspace.sh -w "$HOME/ament_ws" -l $REDIRECT_LOGDIR +elif [[ $factory_osdir = "24.04" ]]; then + ~/stretch_install/factory/$factory_osdir/stretch_create_ament_workspace.sh -w "$HOME/ament_ws" -l $REDIRECT_LOGDIR fi diff --git a/stretch_update_ros_workspace.sh b/stretch_update_ros_workspace.sh index 9220c7c..2f08f78 100755 --- a/stretch_update_ros_workspace.sh +++ b/stretch_update_ros_workspace.sh @@ -4,7 +4,7 @@ set -o pipefail sudo sleep 0.1 source /etc/os-release factory_osdir="$VERSION_ID" -if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04)$ ]]; then +if [[ ! $factory_osdir =~ ^(18.04|20.04|22.04|24.04)$ ]]; then echo "Could not identify OS. Please contact Hello Robot Support." exit 1 fi @@ -27,6 +27,8 @@ elif [[ $factory_osdir = "20.04" ]]; then ./stretch_create_catkin_workspace.sh -w "$HOME/catkin_ws" elif [[ $factory_osdir = "22.04" ]]; then ./stretch_create_ament_workspace.sh -w "$HOME/ament_ws" +elif [[ $factory_osdir = "24.04" ]]; then + ./stretch_create_ament_workspace.sh -w "$HOME/ament_ws" fi if [ $? -ne 0 ]; then echo_failure_help From afd08525a3f61a379ced9b07941a91a1519aadfe Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:41:32 -0400 Subject: [PATCH 07/27] Start porting over ROS workspace --- factory/24.04/stretch_create_ament_workspace.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 579216b..e54c6a0 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -19,15 +19,15 @@ done REDIRECT_LOGFILE="$REDIRECT_LOGDIR/stretch_create_ament_workspace.`date '+%Y%m%d%H%M'`_redirected.txt" echo "###########################################" -echo "CREATING HUMBLE AMENT WORKSPACE at $AMENT_WSDIR" +echo "CREATING JAZZY AMENT WORKSPACE at $AMENT_WSDIR" echo "###########################################" echo "Ensuring correct version of ROS is sourced..." -if [[ $ROS_DISTRO && ! $ROS_DISTRO = "humble" ]]; then +if [[ $ROS_DISTRO && ! $ROS_DISTRO = "jazzy" ]]; then echo "Cannot create workspace while a conflicting ROS version is sourced. Exiting." exit 1 fi -source /opt/ros/humble/setup.bash +source /opt/ros/jazzy/setup.bash if [[ -d $AMENT_WSDIR ]]; then echo "You are about to delete and replace the existing ament workspace. If you have any personal data in the workspace, please create a back up before proceeding." From 18d5188f9ea3874e4786c4826363dd01d3602c31 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 15:48:23 -0400 Subject: [PATCH 08/27] Redirect output from blacklist to null --- factory/24.04/stretch_initial_setup.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/factory/24.04/stretch_initial_setup.sh b/factory/24.04/stretch_initial_setup.sh index 68d9a82..0f9b0a5 100755 --- a/factory/24.04/stretch_initial_setup.sh +++ b/factory/24.04/stretch_initial_setup.sh @@ -181,6 +181,9 @@ if [ -f /etc/gdm3/custom.conf ]; then fi echo 'Blacklisting hid_nintendo that conflicts with Xpad driver...' -echo '# Nintendo conflicts with Xpad controller driver' | sudo tee -a /etc/modprobe.d/blacklist.conf -echo 'blacklist hid_nintendo' | sudo tee -a /etc/modprobe.d/blacklist.conf -sudo update-initramfs -u +function blacklist_hid_nintendo { + echo '# Nintendo conflicts with Xpad controller driver' | sudo tee -a /etc/modprobe.d/blacklist.conf + echo 'blacklist hid_nintendo' | sudo tee -a /etc/modprobe.d/blacklist.conf + sudo update-initramfs -u +} +blacklist_hid_nintendo &> /dev/null From fb55221a0d811d260e5e6aef5ba93f421fe338c6 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 16:14:36 -0400 Subject: [PATCH 09/27] Emacs requires preseeding answers to interactive postfix --- factory/24.04/stretch_install_system.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index 46f6f72..75e3b4e 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -35,6 +35,8 @@ install ipython3 echo "Install pip3" install python3-pip echo "Install Emacs packages" +sudo bash -c 'echo "postfix postfix/mailname string my.hostname.example" | debconf-set-selections' +sudo bash -c 'echo "postfix postfix/main_mailer_type string '\''Internet Site'\''" | debconf-set-selections' install emacs yaml-mode echo "Install nettools" install net-tools From c652d6c7cd7c51df94aaa7c8806f304d7dea96aa Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 17:31:02 -0400 Subject: [PATCH 10/27] Comment out web teleop - pypcl not available for 24.04 --- factory/24.04/stretch_install_system.sh | 48 ++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index 75e3b4e..b3cbe13 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -101,7 +101,7 @@ echo "Configure rosdep" if [ -f "/etc/ros/rosdep/sources.list.d/20-default.list" ]; then sudo rm /etc/ros/rosdep/sources.list.d/20-default.list fi -sudo rosdep init >> $REDIRECT_LOGFILE +sudo rosdep init &>> $REDIRECT_LOGFILE echo "Install vcstool" install python3-vcstool echo "" @@ -143,29 +143,29 @@ echo "Install librealsense2 packages" install librealsense2 librealsense2-udev-rules librealsense2-utils librealsense2-dev librealsense2-dbg echo "" -echo "###########################################" -echo "INSTALLATION OF WEB INTERFACE" -echo "###########################################" -echo "Register the nodesource APT server's public key" -function register_nodesource_apt_server { - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg -} -register_nodesource_apt_server &>> $REDIRECT_LOGFILE -echo "Add the nodesource APT server to the list of APT respositories" -function add_nodesource_apt_server { - NODE_MAJOR=21 - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg, arch=amd64] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list -} -add_nodesource_apt_server &>> $REDIRECT_LOGFILE -echo "Apt update" -sudo apt-get --yes update >> $REDIRECT_LOGFILE -echo "Install NodeJS" -install nodejs -echo "Install PyPCL and PyKDL" -install python3-pcl python3-pykdl screen -echo "Install PM2" -sudo npm install -g pm2 &>> $REDIRECT_LOGFILE -echo "" +# echo "###########################################" +# echo "INSTALLATION OF WEB INTERFACE" +# echo "###########################################" +# echo "Register the nodesource APT server's public key" +# function register_nodesource_apt_server { +# curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg +# } +# register_nodesource_apt_server &>> $REDIRECT_LOGFILE +# echo "Add the nodesource APT server to the list of APT respositories" +# function add_nodesource_apt_server { +# NODE_MAJOR=21 +# echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg, arch=amd64] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list +# } +# add_nodesource_apt_server &>> $REDIRECT_LOGFILE +# echo "Apt update" +# sudo apt-get --yes update >> $REDIRECT_LOGFILE +# echo "Install NodeJS" +# install nodejs +# echo "Install PyPCL and PyKDL" +# install python3-pcl python3-pykdl screen +# echo "Install PM2" +# sudo npm install -g pm2 &>> $REDIRECT_LOGFILE +# echo "" echo "###########################################" echo "INSTALLATION OF REMOTE DESKTOP" From 14a6f849e38867d20c4c55189dd9d0e7ea258e5a Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 23 Jul 2025 19:31:03 -0400 Subject: [PATCH 11/27] Let pip manage pkgs for system interpreter --- stretch_new_user_install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index 17b03c5..29e0ec7 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -43,6 +43,7 @@ else echo "export _colcon_cd_root=${HOME}/ament_ws" >> ~/.bashrc echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc elif [[ $factory_osdir = "24.04" ]]; then + echo "export PIP_BREAK_SYSTEM_PACKAGES=1" >> ~/.bashrc echo "export RMW_IMPLEMENTATION=rmw_zenoh_cpp" >> ~/.bashrc echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc fi From 42682f50a950fea42219b7771e88c111c01ecb0d Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 11:56:12 -0400 Subject: [PATCH 12/27] Separate out pip from 24.04 --- stretch_new_user_install.sh | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index 29e0ec7..ef2130b 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -121,7 +121,33 @@ if [[ $factory_osdir = "18.04" ]]; then echo "Install opencv-python-inference-engine" python3 -m pip -q install --no-warn-script-location opencv-python-inference-engine &>> $REDIRECT_LOGFILE echo "" -elif [[ $factory_osdir = "20.04" || $factory_osdir = "22.04" || $factory_osdir = "24.04" ]]; then +elif [[ $factory_osdir = "20.04" || $factory_osdir = "22.04" ]]; then + echo "###########################################" + echo "INSTALLATION OF USER LEVEL PIP3 PACKAGES" + echo "###########################################" + echo "Upgrade pip3" + python3 -m pip -q install --no-warn-script-location --user --upgrade pip &>> $REDIRECT_LOGFILE + echo "Clear pip cache" + python3 -m pip cache purge &>> $REDIRECT_LOGFILE + echo "Install Stretch Body" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-body &>> $REDIRECT_LOGFILE + echo "Install Stretch Body Tools" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-body-tools &>> $REDIRECT_LOGFILE + echo "Install Stretch Factory" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-factory &>> $REDIRECT_LOGFILE + echo "Install Stretch Tool Share" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-tool-share &>> $REDIRECT_LOGFILE + echo "Install Stretch Diagnostics" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-diagnostics &>> $REDIRECT_LOGFILE + echo "Install Stretch URDF" + python3 -m pip -q install --no-warn-script-location --upgrade hello-robot-stretch-urdf &>> $REDIRECT_LOGFILE + echo "Upgrade prompt_toolkit" + python3 -m pip -q install --no-warn-script-location -U prompt_toolkit &>> $REDIRECT_LOGFILE + echo "Remove setuptools-scm" + python3 -m pip -q uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE + echo "" +elif [[ $factory_osdir = "24.04" ]]; then + export PIP_BREAK_SYSTEM_PACKAGES=1 echo "###########################################" echo "INSTALLATION OF USER LEVEL PIP3 PACKAGES" echo "###########################################" From 2a8ee6ba77b697d028296c3c6d9fc4b29d038658 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:12:23 -0400 Subject: [PATCH 13/27] Redirect stderr from arduino cli --- stretch_new_user_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index ef2130b..aa2bb53 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -92,7 +92,7 @@ echo "Updating media assets..." sudo cp $HOME/stretch_install/factory/$factory_osdir/stretch_about.png /etc/hello-robot echo "Installing Arduino CLI..." -~/stretch_install/factory/$factory_osdir/stretch_install_arduino.sh >> $REDIRECT_LOGFILE +~/stretch_install/factory/$factory_osdir/stretch_install_arduino.sh &>> $REDIRECT_LOGFILE echo "Adding user to the dialout group to access Arduino..." sudo adduser $USER dialout >> $REDIRECT_LOGFILE From 2edc69bab5fdffab71d29772d65c37c855924255 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:19:29 -0400 Subject: [PATCH 14/27] Clean up param migration --- stretch_new_user_install.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/stretch_new_user_install.sh b/stretch_new_user_install.sh index aa2bb53..ecbabf5 100755 --- a/stretch_new_user_install.sh +++ b/stretch_new_user_install.sh @@ -174,13 +174,14 @@ elif [[ $factory_osdir = "24.04" ]]; then echo "" fi +echo "###########################################" +echo "CONFIGURATION OF ROBOT PARAMETERS" +echo "###########################################" +echo "Checking params migrated..." export PATH=${PATH}:~/.local/bin export HELLO_FLEET_ID=$HELLO_FLEET_ID export HELLO_FLEET_PATH=${HOME}/stretch_user - -echo "Ensuring correct version of params present..." params_dir_path=$HELLO_FLEET_PATH/$HELLO_FLEET_ID -echo "Checking params directory path: $params_dir_path" # Define file paths based on the provided directory user_params="$params_dir_path/stretch_re1_user_params.yaml" @@ -193,7 +194,7 @@ if [[ ! -f $new_config_params && ! -f $new_user_params ]]; then # Check if the original RE1 files exist if [[ -f $user_params && -f $factory_params ]]; then echo "Old RE1 params are present. Starting param migration..." - /home/$USER/.local/bin/RE1_migrate_params.py --path $dir_path + /home/$USER/.local/bin/RE1_migrate_params.py # Check if the script ran successfully if [ $? -eq 0 ]; then echo "Migration script ran successfully." @@ -203,7 +204,7 @@ if [[ ! -f $new_config_params && ! -f $new_user_params ]]; then fi echo "Migrating contact params..." - /home/$USER/.local/bin/RE1_migrate_contacts.py --path $dir_path + /home/$USER/.local/bin/RE1_migrate_contacts.py # Check if the script ran successfully if [ $? -eq 0 ]; then echo "Migration script ran successfully." @@ -218,6 +219,7 @@ if [[ ! -f $new_config_params && ! -f $new_user_params ]]; then else echo "Required parameter files are present in the directory. Skipping migration." fi +echo "" if [[ $factory_osdir = "18.04" ]]; then ~/stretch_install/factory/$factory_osdir/stretch_create_catkin_workspace.sh -w "$HOME/catkin_ws" -l $REDIRECT_LOGDIR From c38ba9f08c430a393d8bb6aa6651410963b6ade1 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:31:52 -0400 Subject: [PATCH 15/27] Replace humble vcs with jazzy --- factory/24.04/stretch_ros2_humble.repos | 42 ------------------------- factory/24.04/stretch_ros2_jazzy.repos | 21 +++++++++++++ 2 files changed, 21 insertions(+), 42 deletions(-) delete mode 100644 factory/24.04/stretch_ros2_humble.repos create mode 100644 factory/24.04/stretch_ros2_jazzy.repos diff --git a/factory/24.04/stretch_ros2_humble.repos b/factory/24.04/stretch_ros2_humble.repos deleted file mode 100644 index 87f145b..0000000 --- a/factory/24.04/stretch_ros2_humble.repos +++ /dev/null @@ -1,42 +0,0 @@ -repositories: - ros2_numpy: - type: git - url: https://github.com/hello-binit/ros2_numpy - version: humble - realsense-ros: - type: git - url: https://github.com/IntelRealSense/realsense-ros.git - version: 4.55.1 - stretch_ros2: - type: git - url: https://github.com/hello-robot/stretch_ros2.git - version: humble - sllidar_ros2: - type: git - url: https://github.com/Slamtec/sllidar_ros2.git - version: 34300099fadfc772965962dec837bf436706188f - stretch_tutorials: - type: git - url: https://github.com/hello-robot/stretch_tutorials.git - version: humble - respeaker_ros2: - type: git - url: https://github.com/hello-binit/respeaker_ros2.git - version: humble - audio_common: - type: git - url: https://github.com/hello-binit/audio_common.git - version: humble - stretch_web_teleop: - type: git - url: https://github.com/hello-robot/stretch_web_teleop.git - version: master - rosbridge_suite: - type: git - url: https://github.com/hello-vinitha/rosbridge_suite.git - version: ros2-actions - tf2_web_republisher_py: - type: git - url: https://github.com/hello-vinitha/tf2_web_republisher_py.git - version: master - diff --git a/factory/24.04/stretch_ros2_jazzy.repos b/factory/24.04/stretch_ros2_jazzy.repos new file mode 100644 index 0000000..134c0d2 --- /dev/null +++ b/factory/24.04/stretch_ros2_jazzy.repos @@ -0,0 +1,21 @@ +repositories: + ros2_numpy: + type: git + url: https://github.com/Box-Robotics/ros2_numpy + version: jazzy + stretch_ros2: + type: git + url: https://github.com/hello-robot/stretch_ros2.git + version: humble + sllidar_ros2: + type: git + url: https://github.com/Slamtec/sllidar_ros2.git + version: 34300099fadfc772965962dec837bf436706188f + respeaker_ros2: + type: git + url: https://github.com/hello-binit/respeaker_ros2.git + version: humble + audio_common: + type: git + url: https://github.com/hello-binit/audio_common.git + version: humble From b049041a44fab8f29e51615ae0d37bb478fa834b Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:34:58 -0400 Subject: [PATCH 16/27] rosdep for jazzy --- factory/24.04/stretch_create_ament_workspace.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index e54c6a0..64466c5 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -61,13 +61,12 @@ echo "Creating the workspace directory..." mkdir -p $AMENT_WSDIR/src echo "Cloning the workspace's packages..." cd $AMENT_WSDIR/src -vcs import --input ~/stretch_install/factory/22.04/stretch_ros2_humble.repos &>> $REDIRECT_LOGFILE +vcs import --input ~/stretch_install/factory/24.04/stretch_ros2_jazzy.repos &>> $REDIRECT_LOGFILE echo "Fetch ROS packages' dependencies (this might take a while)..." cd $AMENT_WSDIR/ # The rosdep flags below have been chosen very carefully. Please review the docs before changing them. # https://docs.ros.org/en/independent/api/rosdep/html/commands.html -rosdep install --rosdistro=humble -iy --skip-keys="librealsense2 realsense2_camera" --from-paths src &>> $REDIRECT_LOGFILE -sudo apt remove -y ros-humble-librealsense2 ros-humble-realsense2-camera ros-humble-realsense2-camera-msgs &>> $REDIRECT_LOGFILE +rosdep install --rosdistro=jazzy -iy --from-paths src &>> $REDIRECT_LOGFILE pip3 cache purge &>> $REDIRECT_LOGFILE echo "Install web interface dependencies..." From 93ca92ac460220ebad3024b50c759015f1fd4d63 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:45:29 -0400 Subject: [PATCH 17/27] Comment out web teleop workspace setup --- .../24.04/stretch_create_ament_workspace.sh | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 64466c5..2f42aa2 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -47,6 +47,7 @@ if [[ -d $AMENT_WSDIR ]]; then fi echo "Downgrade to numpy 1.26.4..." +export PIP_BREAK_SYSTEM_PACKAGES=1 pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE export PATH=${PATH}:~/.local/bin @@ -69,27 +70,27 @@ cd $AMENT_WSDIR/ rosdep install --rosdistro=jazzy -iy --from-paths src &>> $REDIRECT_LOGFILE pip3 cache purge &>> $REDIRECT_LOGFILE -echo "Install web interface dependencies..." -cd $AMENT_WSDIR/src/stretch_web_teleop -pip3 install -r requirements.txt &>> $REDIRECT_LOGFILE -npm install --force &>> $REDIRECT_LOGFILE -npx playwright install &>> $REDIRECT_LOGFILE -echo "Generating web interface certs..." -cd $AMENT_WSDIR/src/stretch_web_teleop/certificates -curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" &>> $REDIRECT_LOGFILE -chmod +x mkcert-v*-linux-amd64 -sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert -CAROOT=`pwd` mkcert --install &>> $REDIRECT_LOGFILE -mkdir -p ~/.local/share/mkcert -rm -rf ~/.local/share/mkcert/root* -cp root* ~/.local/share/mkcert -mkcert ${HELLO_FLEET_ID} ${HELLO_FLEET_ID}.local ${HELLO_FLEET_ID}.dev localhost 127.0.0.1 0.0.0.0 ::1 &>> $REDIRECT_LOGFILE -rm mkcert-v*-linux-amd64 -cd $AMENT_WSDIR/src/stretch_web_teleop -touch .env -echo certfile=${HELLO_FLEET_ID}+6.pem >> .env -echo keyfile=${HELLO_FLEET_ID}+6-key.pem >> .env -cd $AMENT_WSDIR/ +# echo "Install web interface dependencies..." +# cd $AMENT_WSDIR/src/stretch_web_teleop +# pip3 install -r requirements.txt &>> $REDIRECT_LOGFILE +# npm install --force &>> $REDIRECT_LOGFILE +# npx playwright install &>> $REDIRECT_LOGFILE +# echo "Generating web interface certs..." +# cd $AMENT_WSDIR/src/stretch_web_teleop/certificates +# curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" &>> $REDIRECT_LOGFILE +# chmod +x mkcert-v*-linux-amd64 +# sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert +# CAROOT=`pwd` mkcert --install &>> $REDIRECT_LOGFILE +# mkdir -p ~/.local/share/mkcert +# rm -rf ~/.local/share/mkcert/root* +# cp root* ~/.local/share/mkcert +# mkcert ${HELLO_FLEET_ID} ${HELLO_FLEET_ID}.local ${HELLO_FLEET_ID}.dev localhost 127.0.0.1 0.0.0.0 ::1 &>> $REDIRECT_LOGFILE +# rm mkcert-v*-linux-amd64 +# cd $AMENT_WSDIR/src/stretch_web_teleop +# touch .env +# echo certfile=${HELLO_FLEET_ID}+6.pem >> .env +# echo keyfile=${HELLO_FLEET_ID}+6-key.pem >> .env +# cd $AMENT_WSDIR/ echo "Install FUNMAP dependencies..." curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE From 084c2cc0c11bbd5fb52cc474141651b19f862a9e Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 12:53:06 -0400 Subject: [PATCH 18/27] Comment out FUNMAP setup --- .../24.04/stretch_create_ament_workspace.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 2f42aa2..e247250 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -92,15 +92,15 @@ pip3 cache purge &>> $REDIRECT_LOGFILE # echo keyfile=${HELLO_FLEET_ID}+6-key.pem >> .env # cd $AMENT_WSDIR/ -echo "Install FUNMAP dependencies..." -curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE -cd $AMENT_WSDIR/src/stretch_ros2/stretch_funmap -uv venv --system-site-packages --allow-existing .venv &>> $REDIRECT_LOGFILE -uv sync --frozen &>> $REDIRECT_LOGFILE -echo "Compile cython modules..." -pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP -uv run cythonize stretch_funmap/cython_min_cost_path.pyx -3 -i &>> $REDIRECT_LOGFILE -cd $AMENT_WSDIR/ +# echo "Install FUNMAP dependencies..." +# curl -LsSf https://astral.sh/uv/install.sh | sh &>> $REDIRECT_LOGFILE +# cd $AMENT_WSDIR/src/stretch_ros2/stretch_funmap +# uv venv --system-site-packages --allow-existing .venv &>> $REDIRECT_LOGFILE +# uv sync --frozen &>> $REDIRECT_LOGFILE +# echo "Compile cython modules..." +# pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP +# uv run cythonize stretch_funmap/cython_min_cost_path.pyx -3 -i &>> $REDIRECT_LOGFILE +# cd $AMENT_WSDIR/ echo "Compile the workspace (this might take a while)..." pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP From 0ddb88c9f7f867253061549ff0e5308e23fc70b1 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 14:24:00 -0400 Subject: [PATCH 19/27] Comment out FUNMAP setup pt. 2 --- factory/24.04/stretch_create_ament_workspace.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index e247250..8845ffd 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -103,8 +103,8 @@ pip3 cache purge &>> $REDIRECT_LOGFILE # cd $AMENT_WSDIR/ echo "Compile the workspace (this might take a while)..." -pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP -pip3 uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE +# pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP +# pip3 uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE colcon build --symlink-install &>> $REDIRECT_LOGFILE echo "Source setup.bash file..." source $AMENT_WSDIR/install/setup.bash From 3670a26c48b791dd0313ecfd215203ad6a291d1f Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 14:32:49 -0400 Subject: [PATCH 20/27] Cant editable install, cant build funmap --- factory/24.04/stretch_create_ament_workspace.sh | 2 +- factory/24.04/stretch_ros2_jazzy.repos | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 8845ffd..5c0549c 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -105,7 +105,7 @@ pip3 cache purge &>> $REDIRECT_LOGFILE echo "Compile the workspace (this might take a while)..." # pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP # pip3 uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE -colcon build --symlink-install &>> $REDIRECT_LOGFILE +colcon build &>> $REDIRECT_LOGFILE echo "Source setup.bash file..." source $AMENT_WSDIR/install/setup.bash echo "Updating port privledges..." diff --git a/factory/24.04/stretch_ros2_jazzy.repos b/factory/24.04/stretch_ros2_jazzy.repos index 134c0d2..4f0e62d 100644 --- a/factory/24.04/stretch_ros2_jazzy.repos +++ b/factory/24.04/stretch_ros2_jazzy.repos @@ -6,7 +6,7 @@ repositories: stretch_ros2: type: git url: https://github.com/hello-robot/stretch_ros2.git - version: humble + version: jazzy sllidar_ros2: type: git url: https://github.com/Slamtec/sllidar_ros2.git From 82035b69e801f67c877121732dd68ca5cc1d29c8 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 14:46:37 -0400 Subject: [PATCH 21/27] Cant editable install, comment out FUNMAP pt. 3 --- factory/24.04/stretch_create_ament_workspace.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 5c0549c..827d688 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -120,11 +120,11 @@ echo "Setup uncalibrated robot URDF..." ros2 run stretch_calibration update_uncalibrated_urdf >> $REDIRECT_LOGFILE echo "Setup calibrated robot URDF..." ros2 run stretch_calibration update_with_most_recent_calibration >> $REDIRECT_LOGFILE -colcon build --symlink-install &>> $REDIRECT_LOGFILE +colcon build &>> $REDIRECT_LOGFILE -echo "Amend FUNMAP executables to use venv..." -pip3 install -U hello-robot-stretch-factory &>> $REDIRECT_LOGFILE # Necessary for the below CLI -REx_amend_venv_execs.py stretch_funmap &>> $REDIRECT_LOGFILE +# echo "Amend FUNMAP executables to use venv..." +# pip3 install -U hello-robot-stretch-factory &>> $REDIRECT_LOGFILE # Necessary for the below CLI +# REx_amend_venv_execs.py stretch_funmap &>> $REDIRECT_LOGFILE echo "Downgrade to numpy 1.26.4..." pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE From 11988f67f1c19c243a1a03332bec5050ea6e67d4 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Thu, 24 Jul 2025 15:27:23 -0400 Subject: [PATCH 22/27] Must remove nose --- factory/24.04/stretch_create_ament_workspace.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index 827d688..cc993b8 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -105,6 +105,7 @@ pip3 cache purge &>> $REDIRECT_LOGFILE echo "Compile the workspace (this might take a while)..." # pip3 install setuptools==59.6.0 &>> $REDIRECT_LOGFILE # must use <61 for Colcon to correctly build Stretch FUNMAP # pip3 uninstall -y setuptools-scm &>> $REDIRECT_LOGFILE +pip3 uninstall -y nose &>> $REDIRECT_LOGFILE colcon build &>> $REDIRECT_LOGFILE echo "Source setup.bash file..." source $AMENT_WSDIR/install/setup.bash From 45fb333bb2b1f5230de8fdb6961a114ef039404b Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Tue, 5 Aug 2025 15:00:24 -0400 Subject: [PATCH 23/27] July release of Sunshine seems to have been deleted --- factory/24.04/stretch_install_system.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/factory/24.04/stretch_install_system.sh b/factory/24.04/stretch_install_system.sh index b3cbe13..6313ab7 100755 --- a/factory/24.04/stretch_install_system.sh +++ b/factory/24.04/stretch_install_system.sh @@ -171,7 +171,7 @@ echo "###########################################" echo "INSTALLATION OF REMOTE DESKTOP" echo "###########################################" echo "Install Sunshine Remote Desktop Server" -wget https://github.com/LizardByte/Sunshine/releases/download/v2025.721.150932/sunshine-ubuntu-24.04-amd64.deb -O /tmp/sunshine.deb &>> $REDIRECT_LOGFILE +wget https://github.com/LizardByte/Sunshine/releases/download/v2025.628.4510/sunshine-ubuntu-24.04-amd64.deb -O /tmp/sunshine.deb &>> $REDIRECT_LOGFILE install /tmp/sunshine.deb echo "Add Sunshine uinput udev rule" function add_sunshine_uinput_rule { From d0cfc19df241ddf8afd7dcabf3d60a65b45f25d3 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Tue, 5 Aug 2025 15:19:32 -0400 Subject: [PATCH 24/27] New post install link --- stretch_new_robot_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stretch_new_robot_install.sh b/stretch_new_robot_install.sh index fde68cc..efb01ca 100755 --- a/stretch_new_robot_install.sh +++ b/stretch_new_robot_install.sh @@ -177,6 +177,6 @@ zip -r $logzip $logdir/ > /dev/null echo "" echo "#############################################" echo "DONE! INSTALLATION COMPLETED SUCCESSFULLY." -echo "Perform post install steps: https://docs.hello-robot.com/0.2/stretch-install/docs/robot_install/#post-install-steps" +echo "Perform post install steps: https://docs.hello-robot.com/0.3/installation/robot_install/#post-install-steps" echo "#############################################" echo "" From a61b69fe7c1871c8104d82188894b58d1bc4487a Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Sun, 26 Oct 2025 16:33:14 -0400 Subject: [PATCH 25/27] Fix udev rules copy (https://superuser.com/a/918334) --- factory/18.04/stretch_initial_setup.sh | 2 +- factory/20.04/stretch_initial_setup.sh | 2 +- factory/22.04/stretch_initial_setup.sh | 2 +- factory/24.04/stretch_initial_setup.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/factory/18.04/stretch_initial_setup.sh b/factory/18.04/stretch_initial_setup.sh index e0895de..82d5579 100755 --- a/factory/18.04/stretch_initial_setup.sh +++ b/factory/18.04/stretch_initial_setup.sh @@ -129,7 +129,7 @@ else fi echo "Setting up UDEV rules..." -sudo cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d +sudo sh -c "cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d" sudo udevadm control --reload echo "Allow shutdown without password..." diff --git a/factory/20.04/stretch_initial_setup.sh b/factory/20.04/stretch_initial_setup.sh index a506e17..60508c7 100755 --- a/factory/20.04/stretch_initial_setup.sh +++ b/factory/20.04/stretch_initial_setup.sh @@ -128,7 +128,7 @@ else fi echo "Setting up UDEV rules..." -sudo cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d +sudo sh -c "cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d" sudo udevadm control --reload echo "Allow shutdown without password..." diff --git a/factory/22.04/stretch_initial_setup.sh b/factory/22.04/stretch_initial_setup.sh index 68d9a82..dadd922 100755 --- a/factory/22.04/stretch_initial_setup.sh +++ b/factory/22.04/stretch_initial_setup.sh @@ -128,7 +128,7 @@ else fi echo "Setting up UDEV rules..." -sudo cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d +sudo sh -c "cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d" sudo udevadm control --reload echo "Allow shutdown without password..." diff --git a/factory/24.04/stretch_initial_setup.sh b/factory/24.04/stretch_initial_setup.sh index 0f9b0a5..036bcd4 100755 --- a/factory/24.04/stretch_initial_setup.sh +++ b/factory/24.04/stretch_initial_setup.sh @@ -128,7 +128,7 @@ else fi echo "Setting up UDEV rules..." -sudo cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d +sudo sh -c "cp /etc/hello-robot/$HELLO_FLEET_ID/udev/*.rules /etc/udev/rules.d" sudo udevadm control --reload echo "Allow shutdown without password..." From e8e2798ba29dfec5b1b6678036ba850bc39dfa42 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Tue, 28 Oct 2025 15:51:23 -0400 Subject: [PATCH 26/27] Update apt cache before creating ROS workspace --- factory/18.04/stretch_create_catkin_workspace.sh | 2 ++ factory/20.04/stretch_create_catkin_workspace.sh | 2 ++ factory/22.04/stretch_create_ament_workspace.sh | 3 +++ factory/24.04/stretch_create_ament_workspace.sh | 3 +++ 4 files changed, 10 insertions(+) diff --git a/factory/18.04/stretch_create_catkin_workspace.sh b/factory/18.04/stretch_create_catkin_workspace.sh index 7b1d9df..1082bda 100755 --- a/factory/18.04/stretch_create_catkin_workspace.sh +++ b/factory/18.04/stretch_create_catkin_workspace.sh @@ -45,6 +45,8 @@ if [[ -d $CATKIN_WSDIR ]]; then prompt_yes_no fi +echo "Apt update..." +sudo apt-get --yes update >> $REDIRECT_LOGFILE echo "Deleting $CATKIN_WSDIR if it already exists..." sudo rm -rf $CATKIN_WSDIR # see http://wiki.ros.org/ROS/Tutorials/InstallingandConfiguringROSEnvironment for details diff --git a/factory/20.04/stretch_create_catkin_workspace.sh b/factory/20.04/stretch_create_catkin_workspace.sh index 334b271..e013727 100755 --- a/factory/20.04/stretch_create_catkin_workspace.sh +++ b/factory/20.04/stretch_create_catkin_workspace.sh @@ -45,6 +45,8 @@ if [[ -d $CATKIN_WSDIR ]]; then prompt_yes_no fi +echo "Apt update..." +sudo apt-get --yes update >> $REDIRECT_LOGFILE echo "Updating rosdep indices..." rosdep update --include-eol-distros &>> $REDIRECT_LOGFILE echo "Deleting $CATKIN_WSDIR if it already exists..." diff --git a/factory/22.04/stretch_create_ament_workspace.sh b/factory/22.04/stretch_create_ament_workspace.sh index 579216b..589d426 100755 --- a/factory/22.04/stretch_create_ament_workspace.sh +++ b/factory/22.04/stretch_create_ament_workspace.sh @@ -46,6 +46,9 @@ if [[ -d $AMENT_WSDIR ]]; then prompt_yes_no fi +echo "Apt update..." +sudo apt-get --yes update >> $REDIRECT_LOGFILE + echo "Downgrade to numpy 1.26.4..." pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE diff --git a/factory/24.04/stretch_create_ament_workspace.sh b/factory/24.04/stretch_create_ament_workspace.sh index cc993b8..8890399 100755 --- a/factory/24.04/stretch_create_ament_workspace.sh +++ b/factory/24.04/stretch_create_ament_workspace.sh @@ -46,6 +46,9 @@ if [[ -d $AMENT_WSDIR ]]; then prompt_yes_no fi +echo "Apt update..." +sudo apt-get --yes update >> $REDIRECT_LOGFILE + echo "Downgrade to numpy 1.26.4..." export PIP_BREAK_SYSTEM_PACKAGES=1 pip3 install numpy==1.26.4 &>> $REDIRECT_LOGFILE From 2adbd3a01c228189ee350dd25141456ccb536262 Mon Sep 17 00:00:00 2001 From: Binit Shah Date: Wed, 29 Oct 2025 13:08:03 -0400 Subject: [PATCH 27/27] chmod on calibration data in home folder only if not factory install --- factory/18.04/stretch_initial_setup.sh | 2 +- factory/20.04/stretch_initial_setup.sh | 2 +- factory/22.04/stretch_initial_setup.sh | 2 +- factory/24.04/stretch_initial_setup.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/factory/18.04/stretch_initial_setup.sh b/factory/18.04/stretch_initial_setup.sh index 82d5579..47f914b 100755 --- a/factory/18.04/stretch_initial_setup.sh +++ b/factory/18.04/stretch_initial_setup.sh @@ -81,8 +81,8 @@ if [ $do_factory_install = 'false' ]; then echo "Expecting robot calibration $HELLO_FLEET_ID to be present in the the home folder. Exiting." exit 1 fi + chmod -R a+r $HOME/$HELLO_FLEET_ID fi -chmod -R a+r $HOME/$HELLO_FLEET_ID echo "Waiting to get online..." while ! timeout 0.2 ping -c 1 -n google.com &> /dev/null diff --git a/factory/20.04/stretch_initial_setup.sh b/factory/20.04/stretch_initial_setup.sh index 60508c7..91dc609 100755 --- a/factory/20.04/stretch_initial_setup.sh +++ b/factory/20.04/stretch_initial_setup.sh @@ -80,8 +80,8 @@ if [ $do_factory_install = 'false' ]; then echo "Expecting robot calibration $HELLO_FLEET_ID to be present in the the home folder. Exiting." exit 1 fi + chmod -R a+r $HOME/$HELLO_FLEET_ID fi -chmod -R a+r $HOME/$HELLO_FLEET_ID echo "Waiting to get online..." while ! timeout 0.2 ping -c 1 -n google.com &> /dev/null diff --git a/factory/22.04/stretch_initial_setup.sh b/factory/22.04/stretch_initial_setup.sh index dadd922..7e6695b 100755 --- a/factory/22.04/stretch_initial_setup.sh +++ b/factory/22.04/stretch_initial_setup.sh @@ -80,8 +80,8 @@ if [ $do_factory_install = 'false' ]; then echo "Expecting robot calibration $HELLO_FLEET_ID to be present in the the home folder. Exiting." exit 1 fi + chmod -R a+r $HOME/$HELLO_FLEET_ID fi -chmod -R a+r $HOME/$HELLO_FLEET_ID echo "Waiting to get online..." while ! timeout 0.2 ping -c 1 -n google.com &> /dev/null diff --git a/factory/24.04/stretch_initial_setup.sh b/factory/24.04/stretch_initial_setup.sh index 036bcd4..fbf9372 100755 --- a/factory/24.04/stretch_initial_setup.sh +++ b/factory/24.04/stretch_initial_setup.sh @@ -80,8 +80,8 @@ if [ $do_factory_install = 'false' ]; then echo "Expecting robot calibration $HELLO_FLEET_ID to be present in the the home folder. Exiting." exit 1 fi + chmod -R a+r $HOME/$HELLO_FLEET_ID fi -chmod -R a+r $HOME/$HELLO_FLEET_ID echo "Waiting to get online..." while ! timeout 0.2 ping -c 1 -n google.com &> /dev/null