# Ubuntu 20.04 + ROS Noetic: installation and command reference

**Different computers:** use your own disk names, partition sizes, GPU driver, Bluetooth address, username, timezone, and serial port. `~` means your home directory; `$USER` means your current username. Text such as `REPLACE_WITH_...` must be replaced. Output examples are not commands.

### Contents

- [1. Windows and disk preparation](#1-prepare-windows-and-disk-space--before-installing-ubuntu)
- [2. USB and Ubuntu installation](#2-create-the-usb-and-install-ubuntu)
- [2E. Full installation on an external HDD or SSD](#2e-full-installation-on-an-external-hdd-or-ssd)
- [3. Ubuntu updates and basic tools](#3-update-ubuntu-and-install-basic-tools)
- [4. Hardware and drivers](#4-configure-hardware-before-testing-graphical-ros-tools)
- [5. ROS Noetic installation](#5-install-ros-noetic)
- [6. Catkin workspace](#6-create-and-build-the-catkin-workspace)
- [7. Verification](#7-verify-ros-and-the-computer)
- [8. Optional development tools](#8-optional-development-software-and-serial-access)
- [9. Dual-boot preferences](#9-optional-dual-boot-preferences)
- [10. Installation troubleshooting](#10-troubleshoot-installation-and-ros-dependency-errors)
- [11. Mouse, audio, gestures, and sleep](#11-optional-mouse-touchpad-audio-and-sleep-fixes)
- [12. Maintenance](#12-maintenance-after-a-successful-setup)
- [A. ROS command reference](#appendix-a-package-installation-and-useful-ros-commands)
- [B. WSL and Docker alternatives](#appendix-b-alternatives-to-native-dual-boot)
- [C. Conditional rosdep workaround](#appendix-c-conditional-legacy-rosdep-mapping-workaround)
- [D. Advanced hardware options](#appendix-d-advanced-hardware-options-mentioned-in-the-chats)
- [E. Optional offline home migration](#appendix-e-moving-home-after-installation--optional-offline-procedure)
- [Lab guide comparison](#lab-guide-comparison)
- [Source coverage and corrections](#source-coverage-and-corrections)

## 1. Prepare Windows and disk space — before installing Ubuntu

### 1.1 Identify the boot mode and disks

**Purpose:** identify the existing Windows boot mode. Run in Windows; read **BIOS Mode** in System Information.

```powershell
msinfo32
```

### 1.2 Check BitLocker before editing Windows partitions

**Purpose:** report encryption state for every volume. Run in an Administrator terminal.

```powershell
manage-bde -status
```

Alternative, where the BitLocker PowerShell module is available:

```powershell
Get-BitLockerVolume
```

Before GParted resizes an NTFS volume, verify **Fully Decrypted** and **0% encrypted**. **Protection Off alone is insufficient**: protection can be suspended while the data is still encrypted. [Microsoft BitLocker operations](https://learn.microsoft.com/en-us/windows/security/operating-system-security/data-protection/bitlocker/operations-guide?tabs=powershell).

**decrypt a volume you intend to resize with GParted, only if it is encrypted. `C:` is an example; check each affected volume separately.** 

### 1.3 Disable Fast Startup and hibernation

**Purpose:** prevent Windows leaving NTFS in a hibernated state. Run as Administrator.

```powershell
powercfg /h off
```

This also disables Windows Hibernate. Alternatively, disable only **Turn on fast startup** in Control Panel → Power Options → Choose what the power buttons do → Change settings that are currently unavailable.

### 1.4 Check the filesystems that will be resized

**Purpose:** check the Windows filesystem online.

```powershell
chkdsk C: /f
```

If Windows asks to schedule the check, answer `Y` and restart Windows. Repeat for an affected data volume, substituting its actual letter:



## 2. Create the USB and install Ubuntu

### 2.3 Run the installer

1. Close GParted and launch **Install Ubuntu** from the live desktop; no reboot is needed first.
2. Select language, keyboard, and **Normal installation**. **Download updates while installing Ubuntu**  
3. Choose **Something else** for the manually prepared layout. Do not choose **Erase disk** when retaining Windows. “Install alongside” is another valid option only when its proposed layout matches your intention.
4. In the allocated Linux space, create/select an **ext4** partition and set its mount point to **`/`**. Format only the new Linux partition intended for installation.
5. A single `/` partition already contains `/home`; a separate `/home` is optional. It is not required for ROS and does not replace backups. On MBR disks, account for the primary-partition limit; a logical Linux partition may be needed.
6. **UEFI:** reuse the appropriate existing EFI System Partition at `/boot/efi` without formatting it. On an empty UEFI disk, let the installer create an appropriate ESP. **Legacy BIOS:** install GRUB to the intended boot disk, not a numbered Linux partition. BIOS-on-GPT needs a BIOS boot partition. Multiple disks need an explicitly checked bootloader destination.
7. Choose your actual timezone, username, hostname, and password. Matching the Windows username is unnecessary. For Hebron use `Asia/Hebron`; nearby timezones can have different daylight-saving rules.
8. Review the changes, install, restart, and remove the USB when prompted. Check that both Ubuntu and Windows boot.

### 2.4 Optional: follow the lecturer's separate / and /home layout

**Purpose:** reproduce the two-partition example on PDF page 14, illustrated on pages 17–19. Choose this **instead of** assigning all Linux space to `/` in section 2.3. A working single-root installation does not need to be repartitioned for ROS.

In the installer's **Something else** screen, use only the free space reserved for Linux:

| Setting                        | First new partition                            | Second new partition                           |
| ------------------------------ | ---------------------------------------------- | ---------------------------------------------- |
| Size in the lecturer's example | `50000 MB`                                     | Remaining allocated Linux space                |
| Type                           | Primary, where appropriate for the disk layout | Primary, where appropriate for the disk layout |
| Location                       | Beginning of this space                        | Beginning of the remaining space               |
| Use as                         | Ext4 journaling file system                    | Ext4 journaling file system                    |
| Mount point                    | `/`                                            | `/home`                                        |

Select the free-space row, click **+**, enter the first partition's settings, and click **OK**. Repeat on the remaining free space for `/home`. Check the existing EFI partition and bootloader destination as described in section 2.3, then review the changes before **Install Now**.

The written `50000 MB` recommendation and screenshot sizes differ; follow your planned allocation, not a screenshot's disk numbers. `50000 MB` is the lecturer's example, not a fixed ROS requirement. Installed packages under `/opt` and `/usr` consume root space, while a workspace under `~/catkin_ws` consumes home space. Increase `/` if your planned system software needs more room. On an MBR disk, the primary-partition limit still applies; do not force two additional primary partitions if the table cannot accommodate them.

**Purpose:** inspect the resulting mounts after installation.

```bash
findmnt -T /
findmnt -T "$HOME"
df -h / "$HOME"
```

With separate partitions, the root and home paths should resolve to different filesystem sources. With a single `/` partition, both resolving to the same filesystem is expected.

## 2E. Full installation on an external HDD or SSD

**Result:** Ubuntu, ROS, your workspace, and the boot files live on the external drive. Plug it in and choose it in the laptop's boot menu; unplug it after shutdown and Windows can boot from its internal disk. This is a **full installed system**, not “Try Ubuntu,” a persistent live USB, or a Windows program.

**Scope:** ordinary 64-bit Intel/AMD PCs with 64-bit UEFI. Section 2E.10 adds optional Legacy BIOS support. ARM/Snapdragon laptops, Apple Silicon, 32-bit UEFI devices, and vendor-specific platforms need different images/boot procedures. No single Ubuntu 20.04 drive is guaranteed to work on every laptop.

### 2E.1 Prepare the two separate drives

- **Installer USB:** 8 GB or larger, prepared with the Ubuntu 20.04.6 Desktop amd64 ISO using section 2.1. Creating this media erases that USB.
- **Destination external HDD/SSD:** the drive that will contain the installed Ubuntu system. Plan for the lab's **100 GB or more** allocation plus your data. An SSD connected directly through USB 3.x is preferable for builds; a hard disk also works but can be slower. A normal low-cost flash stick is less suitable for sustained OS writes.
- Back up the destination's existing files. The fresh-disk layout below erases that destination. If you must retain its data, use verified unallocated space and preserve its existing partition table; do not use **Create Partition Table**.
- Use a reliable data cable, direct USB port, and adequate power. Some HDD enclosures need their own power adapter. Avoid an unreliable hub during installation.
- Keep the Windows BitLocker recovery key available before firmware changes. An external-only install does **not** require decrypting the internal Windows disk. Disable Windows Fast Startup only if you plan to mount/write its NTFS volumes from Linux; otherwise leave those volumes alone.

Do not use the destination external disk as the active installer medium in this procedure. If you only have one removable drive, obtain a second installer USB or use a separately planned network/VM installation method.

### 2E.2 Choose firmware mode and protect the internal drive

For modern laptops use **UEFI + GPT** on the external destination. The internal disk's MBR/GPT layout does not require the external disk to match it. What matters for independent boot is the target laptop's firmware support. The earlier “match Windows' boot mode” advice is primarily for one GRUB menu managing an internal dual boot.

**Preferred route A: make the internal disk unavailable during installation.** With the laptop shut down, disconnect the internal drive only if the manufacturer provides a safe user-serviceable procedure, or use a firmware option that actually disables that storage device. Do not open a laptop unnecessarily or switch RAID/VMD/AHCI modes as a substitute. Changing boot priority alone does not hide the internal disk from the installer. Verify in the live session that the disk is absent.

**Alternative route B: leave the internal drive connected, but install without a bootloader and configure the external bootloader explicitly afterward.** Section 2E.5 explains this route. It avoids relying on the old installer's bootloader selector. You must still select the destination partitions correctly; it is not a substitute for checking the disk identity.

Why the extra care: Ubiquity has a documented history of writing to the first EFI System Partition even when another location was selected. Do not assume the **Device for boot loader installation** dropdown protects the internal ESP in UEFI mode. [Ubuntu installer bug record](https://lists.ubuntu.com/archives/foundations-bugs/2023-March/491671.html).

Boot the installer USB's **UEFI** entry and choose **Try Ubuntu**. For this UEFI path, the following must report UEFI:

```bash
if [ -d /sys/firmware/efi ]; then
    echo "UEFI - continue with the external UEFI instructions"
else
    echo "Legacy BIOS - reboot the installer in UEFI mode for this path"
fi
```

Secure Boot can remain enabled with Ubuntu's signed boot chain. A firmware that rejects old installation media may need newer supported signed media or a deliberate Secure Boot policy change; never clear platform keys to bypass an error. Section 2E.11 covers failures and device-specific limitations.

### 2E.3 Identify the external destination, not just its drive letter

**Purpose:** distinguish the installer USB, external destination, and any internal disks by model, serial number, capacity, transport, and filesystem.

```bash
lsblk -o NAME,PATH,SIZE,MODEL,SERIAL,TRAN,FSTYPE,LABEL,UUID,MOUNTPOINT
ls -l /dev/disk/by-id/
```

Record the destination's **whole-disk path**, its model/serial, and later its partition UUIDs. A USB enclosure may report `usb`, but do not rely on that field alone. Names such as `/dev/sda` and `/dev/sdb` can swap after reboot or when using another port. The removable flag can be `0` for a perfectly valid external drive.

If uncertain, compare the listings before and after connecting the **not-mounted destination** while still in the live session. Never unplug a drive that is mounted or being written. In every subsequent command, replace `REPLACE_WITH_EXTERNAL_...` using the current verified listing; these placeholders deliberately are not usable device names.

### 2E.4 Partition only the external destination

**Purpose:** open the partition editor in the live session.

```bash
sudo gparted
```

Select the external destination by its checked model and capacity. For a fresh drive whose contents may be erased, choose **Device → Create Partition Table → GPT**. This destroys that drive's existing partition layout. Do not do this on the installer USB or internal Windows disk.

Create the following on the external drive, with normal MiB alignment:

| Partition                           | Suggested size                 | Format / flag                 | Installer use                                |
| ----------------------------------- | ------------------------------ | ----------------------------- | -------------------------------------------- |
| External EFI System Partition (ESP) | 512 MiB                        | FAT32; `esp` and `boot` flags | EFI System Partition; mounted at `/boot/efi` |
| Optional BIOS boot partition        | 2 MiB                          | Unformatted; `bios_grub` flag | No mount point; only for section 2E.10       |
| Ubuntu root                         | Remaining intended Linux space | ext4                          | `/`                                          |

Label the ESP `UBUNTU_EFI` and root `UBUNTU_EXT` if useful. Labels aid identification; they are not a replacement for checking model/serial/UUID. A single root partition keeps free space flexible and includes `/home`. To follow the lab's separate-home layout, split the Linux space as in section 2.4; both partitions must remain on the external drive.

A dedicated swap partition is not required for this basic installation. Keep any swap file on the external root; do not select internal Windows/Linux swap. This guide does not configure hibernation. An optional NTFS/exFAT shared-data partition can be created from space reserved in advance, but Ubuntu's root and Catkin workspace should remain on ext4.

Apply the checked changes, close GParted, and record the new partitions:

```bash
lsblk -f
sudo blkid
```

**External-drive exception to section 2.3:** use the **external drive's own ESP**. Do not reuse the internal Windows ESP. [Ubuntu ESP requirements](https://help.ubuntu.com/community/UEFI).

### 2E.5 Install Ubuntu onto those external partitions

Choose **one** launch route:

**Route A, internal disk unavailable:** start **Install Ubuntu** from the live desktop normally.

**Route B, internal disk still visible:** close any running installer. Confirm that the Ubuntu 20.04 Ubiquity help lists the no-bootloader option:

```bash
ubiquity --help
```

**Purpose:** start the installer with automatic bootloader installation disabled.

```bash
sudo ubiquity --no-bootloader
```

This option applies to Ubuntu 20.04's Ubiquity, not every newer Ubuntu installer. It leaves bootloader work for section 2E.6. [Canonical discussion of the Ubiquity option](https://github.com/canonical/ubuntu-desktop-provision/issues/691).

In either route:

1. Select language, keyboard, and **Normal installation**. Updates during installation are optional.
2. Choose **Something else**. Do not choose an automatic “alongside Windows” layout for this external-only setup.
3. Assign the verified **external ext4 partition** to `/`. Format it only if this is the intended new installation. Assign a separate external `/home` only if you created one.
4. Select the **external FAT32 ESP** as the EFI System Partition. The installer may assign `/boot/efi` automatically. Leave every internal partition as **do not use**, with no format checkbox.
5. If the bootloader-device dropdown is present, choose the **external whole disk**, but still use route A or B: the dropdown alone is not sufficient in UEFI mode.
6. Review the final partition changes: every format/create operation must belong to the external destination. Abort if an internal partition is listed for modification.
7. Create your account and complete installation. **Choose Continue Testing / remain in the live session; do not reboot yet.** Route B has no bootloader until the next steps finish. Route A also benefits from explicitly setting up portable boot files.

If the installer exits with an error, resolve it first; a completed file copy does not guarantee a completed installation.

### 2E.6 Mount the installed external system for bootloader setup

These commands run in a **live-session terminal**, after the installer has finished and closed. They also serve as a recovery method if the external installation exists but does not boot. This recipe assumes ext4 root with `/boot` inside it, not encrypted root or a separate `/boot` partition.

**Purpose:** verify mount state. If the installer left the external partitions mounted under `/target` or `/media/...`, unmount those specific external mounts in the Disks application first. Do not unmount the live installer medium or run a blanket “unmount all disks” command.

```bash
lsblk -o NAME,SIZE,MODEL,FSTYPE,UUID,MOUNTPOINT
findmnt
```

**Purpose:** mount the installed root and its own ESP at explicit paths. Substitute your actual external partition paths.

```bash
sudo mkdir -p /mnt/ubuntu-external
sudo mount /dev/REPLACE_WITH_EXTERNAL_ROOT_PARTITION /mnt/ubuntu-external
sudo mkdir -p /mnt/ubuntu-external/boot/efi
sudo mount /dev/REPLACE_WITH_EXTERNAL_ESP_PARTITION /mnt/ubuntu-external/boot/efi
findmnt /mnt/ubuntu-external
findmnt /mnt/ubuntu-external/boot/efi
cat /mnt/ubuntu-external/etc/os-release
```

Expect the new installed Ubuntu 20.04 root and the external FAT32 ESP, both on the intended external physical disk. If either is wrong, stop and unmount/correct it before continuing.

**Purpose:** verify persistent mounts reference external filesystem UUIDs.

```bash
sudo blkid /dev/REPLACE_WITH_EXTERNAL_ROOT_PARTITION
sudo blkid /dev/REPLACE_WITH_EXTERNAL_ESP_PARTITION
cat /mnt/ubuntu-external/etc/fstab
sudo cp -n /mnt/ubuntu-external/etc/fstab /mnt/ubuntu-external/etc/fstab.before-external-boot
sudo nano /mnt/ubuntu-external/etc/fstab
```

Keep the existing valid options. Check that `/` uses the external root UUID and `/boot/efi` uses the external ESP UUID; add/correct the ESP entry if missing/wrong. If you created separate `/home`, check its external UUID too. Do not duplicate mount entries. For the FAT32 ESP, the expected entry has this shape (**file content**, replace the UUID):

```fstab
UUID=REPLACE_WITH_EXTERNAL_ESP_UUID /boot/efi vfat umask=0077 0 1
```

Remove only unintended dependencies on internal disks; a valid `/swapfile` entry is fine. Do not add `nofail` to hide a required ESP/root configuration error.

**Purpose:** expose the live kernel's device/proc/sys interfaces to the installed system and enter it.

```bash
for directory in dev proc sys run; do
    sudo mount --rbind "/$directory" "/mnt/ubuntu-external/$directory"
    sudo mount --make-rslave "/mnt/ubuntu-external/$directory"
done
sudo chroot /mnt/ubuntu-external /bin/bash
```

You are now root **inside the installed external system**. The next blocks have no `sudo` for that reason. Commands such as `apt install` now change the external installation, not the live USB. Connect the live session to the Internet first; stop on DNS/APT errors.

**Purpose:** install/update the UEFI boot utilities and Ubuntu's signed boot chain.

```bash
apt update
apt install grub2-common grub-efi-amd64-bin grub-efi-amd64-signed shim-signed
```

If a package configuration dialog asks for an ESP or install destination, select only the verified external destination. APT maintainer scripts may update the current laptop's firmware boot entry; that is separate from disk contents. The explicit command below uses `--no-nvram`, and portable boot will not rely on that laptop-specific entry.

**Purpose:** install the signed Ubuntu UEFI loader to the mounted **external** ESP without explicitly registering a new firmware entry.

```bash
grub-install --target=x86_64-efi \
  --efi-directory=/boot/efi --boot-directory=/boot \
  --bootloader-id=ubuntu --uefi-secure-boot --no-nvram
update-initramfs -u -k all
update-grub
```

Focal's `--uefi-secure-boot` option requires `grub-efi-amd64-signed`. If it reports missing signed files or fails, stop and resolve the package issue; do not add `--force` or silently substitute an unsigned loader while expecting Secure Boot to work. [Ubuntu 20.04 grub-install options](https://manpages.ubuntu.com/manpages/focal/man8/grub-install.8.html).

### 2E.7 Add the portable UEFI fallback boot path

Continue **inside the chroot**. A firmware “ubuntu” entry belongs to one laptop's NVRAM; it does not travel with the external disk. The standard x86-64 removable-media location is **`EFI/BOOT/BOOTX64.EFI`**. For this signed path, that file must be Ubuntu's **shim**, with the matching signed GRUB beside it. [GRUB removable-media installation](https://www.gnu.org/software/grub/manual/grub/html_node/Installing-GRUB-using-grub_002dinstall.html), [Ubuntu signed boot-chain explanation](https://documentation.ubuntu.com/security/security-features/platform-protections/secure-boot/).

**Purpose:** inspect the files generated in the previous step.

```bash
ls -l /boot/efi/EFI/ubuntu/
```

Before proceeding, confirm `shimx64.efi`, `grubx64.efi`, and `grub.cfg` exist there. Inspect the small `grub.cfg`; for this layout it should locate the external root filesystem by UUID and load `/boot/grub/grub.cfg`:

```bash
cat /boot/efi/EFI/ubuntu/grub.cfg
blkid
```

**Purpose:** preserve an existing fallback directory if present, then install the signed fallback files on this external ESP. This changes that drive's default fallback loader; use it for the dedicated Ubuntu external disk described here, not a shared multiboot ESP without a separate plan.

```bash
if [ -d /boot/efi/EFI/BOOT ]; then
    cp -a /boot/efi/EFI/BOOT "/boot/efi/EFI/BOOT.backup-$(date +%Y%m%d-%H%M%S)"
fi
mkdir -p /boot/efi/EFI/BOOT
cp /boot/efi/EFI/ubuntu/shimx64.efi /boot/efi/EFI/BOOT/BOOTX64.EFI
cp /boot/efi/EFI/ubuntu/grubx64.efi /boot/efi/EFI/BOOT/grubx64.efi
cp /boot/efi/EFI/ubuntu/grub.cfg /boot/efi/EFI/BOOT/grub.cfg
if [ -f /boot/efi/EFI/ubuntu/mmx64.efi ]; then
    cp /boot/efi/EFI/ubuntu/mmx64.efi /boot/efi/EFI/BOOT/mmx64.efi
fi
```

Keep `EFI/ubuntu` as well: the signed loader's configuration lookup may use that vendor directory. Do not rename/remove it after creating `EFI/BOOT`.

**Purpose:** verify the fallback copies and leave the installed-system shell.

```bash
cmp /boot/efi/EFI/ubuntu/shimx64.efi /boot/efi/EFI/BOOT/BOOTX64.EFI
cmp /boot/efi/EFI/ubuntu/grubx64.efi /boot/efi/EFI/BOOT/grubx64.efi
test -s /boot/grub/grub.cfg && echo "Main GRUB configuration exists"
sync
exit
```

`cmp` produces no output when files match. These checks confirm file placement, not firmware compatibility; actual boot tests follow.

Back in the **live-session terminal**, close any file-manager windows or terminals browsing the installed mounts, then unmount only this mounted external tree:

```bash
sudo umount -R /mnt/ubuntu-external
sync
sudo poweroff
```

If unmount reports busy, close the process using the tree and retry; do not force-unplug the drive. After full shutdown, remove the installer USB, leave the external system drive connected, and restore the internal drive connection/firmware setting if you disabled it.

### 2E.8 First boot and proof of independence

Use the laptop's **one-time boot menu** and select the external drive's **UEFI USB** entry. It may display the enclosure name rather than “Ubuntu.” Do not select a similarly named internal-disk `ubuntu` entry merely because it is present. You can keep **Windows Boot Manager** first in the permanent boot order.

**Purpose:** check that root, home, EFI files, and swap all belong to the intended external installation. Run in the newly booted Ubuntu system.

```bash
findmnt -T /
findmnt -T "$HOME"
findmnt /boot/efi
lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN,FSTYPE,UUID,MOUNTPOINT
cat /etc/fstab
swapon --show
```

Trace the root and ESP source partitions back to the external physical disk. A `/swapfile` on the external root is expected if swap was configured. No required `/home`, `/boot/efi`, or swap entry should depend on the first laptop's internal disk.

**Purpose:** inspect Secure Boot state, if the utility is not already installed.

```bash
sudo apt install mokutil
mokutil --sb-state
```

Perform these acceptance checks before calling the disk portable:

1. Boot it on the installation laptop using the external USB entry and confirm the mounts above.
2. Shut down Ubuntu fully, disconnect the external drive, and confirm internal Windows still boots. If Windows does not start automatically, select **Windows Boot Manager** and restore its boot priority; do not delete EFI files as a first response.
3. Shut Windows down, connect the external drive to a **second compatible laptop**, select its UEFI USB entry, and repeat the mount checks. This tests that you are not relying on the first laptop's NVRAM entry or internal ESP.
4. Check Wi-Fi, keyboard/touchpad, audio, GPU rendering, and power management on each laptop. Only then proceed with heavy ROS simulations.

**Continue with sections 3–7** for Ubuntu updates, suitable drivers, ROS, workspace creation, and Turtlesim. Those software commands are the same on an external installation. Do not repeat the Windows partition-shrinking steps. You install ROS once on the external system; its packages and workspace then travel with the drive.

### 2E.9 Using the same drive on different laptops

| Difference between laptops                         | What to do                                                   |
| -------------------------------------------------- | ------------------------------------------------------------ |
| Intel versus AMD x86-64 CPU                        | The amd64 Ubuntu image supports both in principle; actual chipset/firmware support still needs testing. |
| ARM/Snapdragon or Apple Silicon                    | This amd64 image will not boot natively; use a platform-specific installation. |
| UEFI versus Legacy BIOS                            | The main path is UEFI. Use section 2E.10 only if you need Legacy support too. |
| NVIDIA versus Intel/AMD graphics                   | Inspect the GPU on each laptop. Do not pin the external system to one laptop's GPU using a hardcoded Xorg configuration or run `nvidia-xconfig` routinely. A single NVIDIA driver branch may not support every old/new NVIDIA GPU. |
| Secure Boot and NVIDIA/DKMS                        | A module-signing key trusted on one laptop may need enrollment on another. Follow Ubuntu's MOK enrollment prompts on each affected laptop; do not assume firmware trust travels with the disk. |
| Wi-Fi/Bluetooth/audio hardware                     | Reuse the common firmware/kernel packages, then diagnose the actual device. Do not install random third-party driver scripts based only on the laptop brand. |
| Very recent hardware                               | Focal's HWE kernel may still be too old. If a needed device is unsupported, a newer host with a Focal VM/container is an alternative, not a problem solved by another partition layout. |
| Device names, USB serial ports, network interfaces | Use UUIDs for mounts, `/dev/serial/by-id` for suitable serial devices, and inspect the current network names. Do not hardcode `/dev/sdb` or one laptop's IP into startup files. |
| Suspend and ACPI settings                          | A sleep fix for one laptop can cause failures on another. Leave those tweaks out of a shared portable image unless tested on all target machines. |

Each laptop needs its own successful boot/hardware test. The external disk shares the same user accounts, passwords, SSH keys, saved Wi-Fi credentials, and files wherever it is used. For friends who want independent installations, prepare their own drives instead of distributing a copy of your personal system with credentials included.

### 2E.10 Optional: also boot an older Legacy BIOS laptop

Skip if all target laptops support 64-bit UEFI. This adds a second boot method to the **same GPT external disk**; it does not convert Windows or create a second Ubuntu installation. It requires the **unformatted `bios_grub` partition** reserved in section 2E.4. Some older firmware cannot USB-boot a GPT disk even with BIOS GRUB; test the actual laptop. [GRUB BIOS partition requirements](https://www.gnu.org/software/grub/manual/grub/html_node/BIOS-installation.html).

Boot the installed external Ubuntu in UEFI mode first, verify its mount sources, and confirm the BIOS boot partition with:

```bash
lsblk -o NAME,SIZE,MODEL,UUID,MOUNTPOINT
sudo parted /dev/REPLACE_WITH_EXTERNAL_WHOLE_DISK print
```

Do not proceed unless the whole disk is positively identified and its partition table includes a `bios_grub` partition. If you did not reserve one, do not improvise a destructive resize just to copy this command.

**Purpose:** install BIOS GRUB binaries alongside the UEFI files, then write the BIOS bootloader to the external **whole disk**, not a numbered partition.

```bash
sudo apt install grub-pc-bin
sudo grub-install --target=i386-pc --boot-directory=/boot --recheck \
  /dev/REPLACE_WITH_EXTERNAL_WHOLE_DISK
sudo update-grub
```

`i386-pc` is GRUB's BIOS platform name; it does not turn the installed amd64 OS into a 32-bit OS. Install `grub-pc-bin` here, not a blind replacement of all UEFI GRUB packages with `grub-pc`. The existing signed UEFI files should remain. Test both UEFI and Legacy USB boot afterward; Legacy mode has no UEFI Secure Boot validation.

For a truly BIOS-only machine that refuses GPT USB media, use a separately planned **MBR external disk** installation booted in Legacy mode, with GRUB on that external whole disk and the normal space before its first aligned partition. That is a different layout; do not convert a working GPT external drive in place as a troubleshooting shortcut.

### 2E.11 External-drive troubleshooting and recovery

| Symptom                                        | Check / action                                               |
| ---------------------------------------------- | ------------------------------------------------------------ |
| External disk absent from firmware boot menu   | Check USB boot is permitted, try a direct port/cable, check enclosure power, and select the correct UEFI/Legacy menu. USB-C storage visibility inside Windows does not prove pre-boot support. Some docks/Thunderbolt enclosures need firmware-specific support. |
| Disk appears, but says no bootable device      | Confirm an external FAT32 ESP and `EFI/BOOT/BOOTX64.EFI` exist. Boot the live USB, identify the external partitions again, and repeat sections 2E.6–2E.7. |
| Works only on the original laptop              | Check that `/boot/efi` is on the external disk, that fallback files exist, and that `/etc/fstab` has no required internal UUIDs. Then test using a USB entry on another laptop. |
| Secure Boot violation / revoked image          | Update the installed `shim-signed`, signed GRUB, and Ubuntu kernel using trusted Focal repositories; refresh the fallback copies afterward. Firmware revocations or a policy that excludes third-party certificates may reject older loaders. Do not replace a signed loader with an unsigned one and expect Secure Boot acceptance. |
| GRUB appears but Ubuntu is not found           | Inspect external root UUID, `EFI/ubuntu/grub.cfg`, `/boot/grub/grub.cfg`, and installed kernel/initramfs. Repair using the live chroot route; an EFI loader existing alone is insufficient. |
| Emergency mode or long wait for a disk UUID    | Compare `blkid` with `/etc/fstab`. Correct stale internal/old-disk references, including separate home or swap. Do not remove the current root/ESP entries blindly. |
| Black screen on one laptop                     | Try the installer's safe-graphics option for diagnosis. On the installed system, a temporary GRUB `nomodeset` boot may help reach a desktop, but limits graphics and is not a permanent ROS simulation solution. Inspect actual GPUs/drivers using section 4. |
| I/O errors, USB resets, or unexplained freezes | Check the cable, power, port, enclosure, and drive health before changing ROS packages. Preserve important files if storage errors recur. |
| Windows boots instead                          | Use the one-time external USB boot entry. Windows being first in boot order is compatible with this setup. |

**Purpose:** inspect USB/storage errors after booting Ubuntu.

```bash
lsusb -t
sudo journalctl -k -b | grep -Ei 'usb|uas|reset|I/O error|disconnect'
```

A recurring USB reset is not automatically a driver problem; begin with the physical connection. Do not apply global USB/UAS blacklists without identifying the affected enclosure.

**Recovery when Ubuntu accidentally used the internal ESP:** preserve the internal EFI files, boot the live USB in UEFI mode, and use sections 2E.6–2E.7 to install the boot files onto the external ESP and correct its `fstab`. If the external disk has no ESP, allocate one from verified free space first; do not format its root or another disk's ESP. Test external-only boot before considering any cleanup. Internal Windows' `EFI/Microsoft` directory must remain intact; deleting it does not repair Ubuntu portability.

### 2E.12 Updates, performance, and safe removal

**Bootloader maintenance:** manual fallback copies may not be refreshed by every package update. After a shim/GRUB update, verify that `/boot/efi` is still the external ESP, refresh the signed vendor loader, and repeat the fallback copying/checks from section 2E.7. When running from installed Ubuntu rather than a chroot, use an explicit root shell for those no-sudo commands:

```bash
findmnt /boot/efi
sudo -i
```

After confirming the correct mounted ESP, run the `grub-install` command from section 2E.6, `update-grub`, and the copy/check blocks from section 2E.7. `exit` returns to your normal user. Retest the external USB boot path after significant bootloader changes. If you added BIOS support, also rerun its external-disk `grub-install` command after relevant GRUB updates, verifying the whole-disk identity again.

**Portability:** keep the normal Ubuntu generic/HWE kernel rather than one tied to a different vendor board. Use one source of kernel updates; do not install a different laptop's vendor kernel just because you moved the disk. Large builds and Gazebo assets benefit from an SSD and USB 3.x. Use `catkin_make -j2 -l2` when a smaller laptop runs out of RAM, as described in section 10.4.

**Safe disconnect:** save files, stop ROS nodes and builds, and shut the installed external system down completely:

```bash
sudo poweroff
```

Wait for full shutdown and disk activity to stop before unplugging. `sync` alone is not permission to disconnect a running root filesystem. Do not unplug during sleep, resume, an update, or hibernation. Fully shut down before moving the drive to another laptop; do not resume a hibernated installation on different hardware.

When Windows offers to format an unrecognized Linux partition, **cancel**. Windows normally does not browse ext4 natively; the absence of files in Explorer does not mean the partition is empty. Back up your source code and data separately because the portable drive is still a single point of failure.

## 3. Update Ubuntu and install basic tools

All remaining main-path commands run in the **installed Ubuntu terminal**, opened with `Ctrl+Alt+T`, as your normal user. Use Bash for the commands below.

### 3.1 Confirm the OS and architecture

**Purpose:** ensure you are using the intended Ubuntu release before adding repositories.

```bash
cat /etc/os-release
lsb_release -sc
dpkg --print-architecture
uname -r
```

Expect Ubuntu `20.04` and `focal`; a normal Intel/AMD 64-bit PC reports `amd64`. Stop if the release differs. Do not add Focal repositories to Ubuntu 22.04/24.04 to force Noetic installation.

### 3.2 Keep Ubuntu 20.04 while allowing package updates

**Purpose:** suppress prompts to upgrade to another Ubuntu release.

```bash
sudo cp -n /etc/update-manager/release-upgrades /etc/update-manager/release-upgrades.before-noetic
sudo sed -i 's/^Prompt=.*/Prompt=never/' /etc/update-manager/release-upgrades
grep '^Prompt=' /etc/update-manager/release-upgrades
```

Expect `Prompt=never`. This does not block ordinary package updates or prevent someone manually upgrading the OS. Do not run `do-release-upgrade` for this setup.

### 3.3 Refresh package lists, update packages, and enable Universe

**Purpose:** obtain current package lists before installing anything, avoiding the chats' “curl has no installation candidate” ordering problem.

```bash
sudo apt update
sudo apt upgrade
sudo apt install software-properties-common ca-certificates curl wget gnupg lsb-release
sudo add-apt-repository universe
sudo apt update
```

If `apt update` fails because an old ROS source already exists, use section 10.1 before continuing. Review APT prompts instead of blindly accepting removals.

**Purpose:** inspect Ubuntu Pro coverage on this installation, if the client is installed.

```bash
pro status
```

If desired, configure Pro through Ubuntu's account flow described in the [Ubuntu 20.04 support page](https://ubuntu.com/20-04). EOL does not mean you should automatically replace every Ubuntu mirror with `old-releases`.

### 3.4 Install the development and diagnostic basics

**Purpose:** install C/C++ compilers, Make, CMake, debugger, Git, Python 3 tooling, and resource-monitoring utilities.

```bash
sudo apt install build-essential cmake gdb git python3 python3-pip python3-dev python3-venv htop
```

**Optional:** install the terminal layout tool mentioned in the chats.

```bash
sudo apt install terminator
```

Do not replace Ubuntu's system Python or use `sudo pip` to upgrade ROS dependencies. Use an isolated virtual environment for unrelated Python projects.

## 4. Configure hardware before testing graphical ROS tools

### 4.1 Identify hardware and available proprietary drivers

**Purpose:** inspect PCI/USB devices and loaded drivers.

```bash
lspci -k
lsusb
sudo apt install ubuntu-drivers-common
ubuntu-drivers devices
```

`ubuntu-drivers` handles supported additional driver packages; it does not install a missing driver for every possible Wi-Fi, audio, or chipset device. Most Linux drivers come from the kernel and firmware packages. Test the actual devices as well as checking the listings.

### 4.2 NVIDIA only: install the appropriate driver

Skip this on Intel/AMD-only machines or if an appropriate NVIDIA driver already works. Choose the **recommended/tested** supported driver in **Software & Updates → Additional Drivers**, or use Ubuntu 20.04's automatic selection:

```bash
sudo ubuntu-drivers autoinstall
```

**Device note:** the chats show an RTX 4050 working with `535`, after automatic selection of a `570-server-open` package failed. That does not make `535` the correct driver for every GPU, or prove all server/open drivers are broken. For manual installation, first use `ubuntu-drivers devices` and APT policy to select a compatible package. [Ubuntu NVIDIA guidance](https://help.ubuntu.com/community/BinaryDriverHowto/Nvidia).

**Conditional example only:** if `nvidia-driver-535` is offered and appropriate for the GPU:

```bash
apt-cache policy nvidia-driver-535
sudo apt install nvidia-driver-535
```

After successful installation, save work and reboot. Complete any requested MOK enrollment if Secure Boot is enabled.

```bash
sudo reboot
```

**Purpose:** verify the NVIDIA driver after reboot.

```bash
nvidia-smi
```

This verifies driver communication, not every graphics application. The CUDA version shown is the driver's supported CUDA level; it does not mean the CUDA toolkit is installed. A separate CUDA toolkit is not required for ordinary ROS/RViz use.

### 4.3 Optional: Ubuntu 20.04 HWE kernel and firmware

**Purpose:** obtain the Focal hardware-enablement kernel and standard firmware when needed for hardware support.

```bash
apt-cache policy linux-generic-hwe-20.04
sudo apt install --install-recommends linux-generic-hwe-20.04
sudo apt install linux-firmware
sudo reboot
```

Use the normal package update path if HWE is already installed. Do not replace a board/vendor-specific kernel without checking its support instructions. **Do not use `linux-generic-hwe-24.04` from chat7 on Ubuntu 20.04.** Some recent devices remain unsupported by Focal's kernel; a newer host OS with a Focal VM/container may be more suitable. [Ubuntu HWE package guidance](https://discourse.ubuntu.com/t/ask-us-anything-about-ubuntu-kernels/27664).

## 5. Install ROS Noetic

### 5.1 Configure the ROS repository and signing key

**Purpose:** install the official ROS 1 repository configuration package, which manages the key and source together. This replaces the old `apt-key`/keyserver steps in the chats. [ROS key migration guidance](https://discourse.ros.org/t/ros-signing-key-migration-guide/43937), [official repository package](https://github.com/ros-infrastructure/ros-apt-source).

For a fresh setup, download the verified Focal release asset below. Version `1.3.0` was available when this guide was reviewed. If unavailable later, obtain the **`ros-apt-source` Focal `all.deb`** asset from the [official releases](https://github.com/ros-infrastructure/ros-apt-source/releases); do not substitute `ros2-apt-source`.

```bash
mkdir -p ~/Downloads/ros-setup
cd ~/Downloads/ros-setup
curl -fL --retry 3 -o ros-apt-source.deb \
  https://github.com/ros-infrastructure/ros-apt-source/releases/download/1.3.0/ros-apt-source_1.3.0.focal_all.deb
```

**Purpose:** inspect the downloaded package; it should identify itself as `ros-apt-source`.

```bash
dpkg-deb -f ./ros-apt-source.deb Package Version Architecture
```

**Purpose:** install the repository configuration, then refresh the ROS package index.

```bash
sudo apt install ./ros-apt-source.deb
sudo apt update
apt-cache policy ros-noetic-desktop-full
```

APT should report a candidate for `ros-noetic-desktop-full`. If you previously followed the chats, first resolve duplicate/old source definitions using section 10.1. Never bypass repository signature checking.

### 5.2 Install the full desktop ROS distribution

**Purpose:** install ROS core, RViz, Gazebo integration, and the desktop/simulation tools used in the chats.

```bash
sudo apt install ros-noetic-desktop-full
```

Wait for completion. For a deliberately headless installation, `ros-noetic-ros-base` is an alternative, but it does not provide the graphical tools assumed below.

### 5.3 Load ROS into Bash

**Purpose:** activate Noetic now and add it to future Bash terminals without repeatedly appending the same line.

```bash
source /opt/ros/noetic/setup.bash
grep -qxF 'source /opt/ros/noetic/setup.bash' ~/.bashrc || \
  echo 'source /opt/ros/noetic/setup.bash' >> ~/.bashrc
```

**Purpose:** verify that ROS commands and Python 3 configuration are visible.

```bash
rosversion -d
echo "$ROS_DISTRO"
echo "$ROS_PYTHON_VERSION"
```

Expect `noetic`, `noetic`, and `3`. For Zsh, use the corresponding `setup.zsh` and `.zshrc`; the rest of this guide assumes Bash.

### 5.4 Install build and dependency tools

**Purpose:** install the tools used to resolve package dependencies and work with Catkin source projects.

```bash
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator \
  python3-wstool python3-catkin-tools build-essential
```

### 5.5 Initialize rosdep and include Noetic's EOL metadata

**Purpose:** create the machine-wide rosdep source list once.

```bash
if [ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]; then
    sudo rosdep init
fi
```

**Purpose:** update your user's dependency database, including end-of-life distributions.

```bash
rosdep update --include-eol-distros
```

Run the update **without sudo**. An existing default source list does not need deletion simply because `rosdep init` says it exists. The EOL flag is required for Noetic metadata now; it does not cure network failures or every missing dependency rule. [ROS rosdep EOL behavior](https://discourse.ros.org/t/rosdep-and-eol-distros/7640).

## 6. Create and build the Catkin workspace

### 6.1 Create the workspace and perform the first build

**Purpose:** create the folder for your own ROS packages and generate its build environment.

```bash
source /opt/ros/noetic/setup.bash
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
```

Run `catkin_make` from `~/catkin_ws`, not `src`, and never with `sudo`. `catkin_init_workspace` is unnecessary here because the first build initializes the source space.

### 6.2 Activate the workspace

**Purpose:** make your own packages visible now and in future terminals. Run only after a successful build.

```bash
source ~/catkin_ws/devel/setup.bash
grep -qxF 'source ~/catkin_ws/devel/setup.bash' ~/.bashrc || \
  echo 'source ~/catkin_ws/devel/setup.bash' >> ~/.bashrc
```

Keep the core `/opt/ros/noetic` source line before the workspace source line in `.bashrc`.

**Purpose:** verify the workspace overlay.

```bash
echo "$ROS_PACKAGE_PATH"
```

The output should include your own `catkin_ws/src` and `/opt/ros/noetic/share`. This printed path is not a command. You can have multiple workspaces, but source the intended one; overlays can be chained deliberately. Do not mix `catkin_make` and `catkin build` in the same build/devel directories.

### 6.3 Optional: create the first lab package

**Purpose:** create the same basic package used in the chats.

```bash
cd ~/catkin_ws/src
catkin_create_pkg test_package_1 std_msgs rospy roscpp
cd ~/catkin_ws
catkin_make
source devel/setup.bash
rospack depends1 test_package_1
```

If it already exists, skip creation and inspect it; do not delete your work. `roscpp`, `rospy`, and `std_msgs` in the output are package names, not programs to execute. Dependency lookup errors should be investigated using section 10.3 even if a list prints underneath them.

## 7. Verify ROS and the computer

### 7.1 Start the ROS master — terminal 1

**Purpose:** start the master, parameter server, and ROS logging.

```bash
roscore
```

Leave it running while using terminals 2 and 3. `Ctrl+C` stops it.

### 7.2 Start the graphical test — terminal 2

**Purpose:** open the Turtlesim simulator.

```bash
rosrun turtlesim turtlesim_node
```

### 7.3 Control the turtle — terminal 3

**Purpose:** test node-to-node communication using keyboard commands.

```bash
rosrun turtlesim turtle_teleop_key
```

Focus terminal 3 and press the arrow keys. The turtle should move. This tests a basic ROS workflow, not all hardware or simulation features.

### 7.4 Inspect running nodes — terminal 4

**Purpose:** confirm that the nodes and their topics are registered.

```bash
rosnode list
rostopic list
rosnode info /turtlesim
```

**Purpose:** test RViz rendering, with the master still running.

```bash
rviz
```

Close RViz when done. A missing fixed-frame warning in an empty session is not an installation failure.

**Purpose:** test Gazebo's ROS integration in a separate terminal.

```bash
roslaunch gazebo_ros empty_world.launch
```

Close the simulator with `Ctrl+C` in its launching terminal. Initial model/network requests may take time. Stop the test nodes and then `roscore` when finished.

### 7.5 Check resources and hardware function

**Purpose:** report memory, CPU, disk space, failed services, and network state.

```bash
free -h
lscpu
df -h /
systemctl --failed
nmcli device status
```

**Purpose:** interactively view CPU and memory usage; press `q` to exit.

```bash
htop
```

Also test Wi-Fi, Bluetooth, audio, external displays if needed, and suspend/resume after saving work. A loaded driver or free-memory listing is not a complete hardware health test.

## 8. Optional development software and serial access

### 8.1 Install Visual Studio Code

**Purpose:** install the editor through Snap.

```bash
sudo snap install --classic code
```

If already installed, keep it or update it:

```bash
sudo snap refresh code
```

**Purpose:** add C/C++, Python, and CMake editing support.

```bash
code --install-extension ms-vscode.cpptools
code --install-extension ms-python.python
code --install-extension twxs.cmake
```

The chats used `ms-iot.vscode-ros`, whose [repository is archived](https://github.com/ms-iot/vscode-ros). It is optional and may have compatibility/availability limits; ROS works without it. If your course specifically uses that extension:

```bash
code --install-extension ms-iot.vscode-ros
```

### 8.2 Allow serial-port access for development boards

**Purpose:** grant your user access to typical USB serial devices.

```bash
sudo usermod -aG dialout "$USER"
```

Log out completely and log back in, or reboot. Then check membership and attached serial devices:

```bash
id -nG
ls -l /dev/serial/by-id/
```

If no device appears, connect it and inspect `lsusb` and `sudo dmesg -w` (stop with `Ctrl+C`). Device paths differ: `/dev/ttyUSB0` and `/dev/ttyACM0` are examples. Prefer a stable `/dev/serial/by-id/...` link where available. Some boards need specific udev rules; `dialout` is not universal USB access. Do not solve this by using `chmod 777` on devices.

### 8.3 Configure Git and an SSH key

**Purpose:** set the author identity for your commits. Replace both values.

```bash
git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"
```

**Purpose:** check whether an SSH public key already exists before generating one.

```bash
ls -la ~/.ssh
```

**Purpose:** generate a key only if needed; do not overwrite an existing key. Set a passphrase when prompted if desired.

```bash
ssh-keygen -t ed25519 -C "your_email@example.com"
cat ~/.ssh/id_ed25519.pub
```

Add the **public** `.pub` output to your Git host's SSH key settings. Never share `id_ed25519`, the private key. An SSH key with a passphrase may still prompt unless managed by an agent.

### 8.4 Optional Google Chrome — amd64 only

**Purpose:** download and install Google's Linux desktop package.

```bash
mkdir -p ~/Downloads/chrome-setup
cd ~/Downloads/chrome-setup
wget -O google-chrome-stable_current_amd64.deb \
  https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
```

Use this on `amd64`, not ARM. Firefox is sufficient for reading the guide; Chrome is not a ROS prerequisite. Browser support can change: [Google's requirements](https://support.google.com/chrome/answer/95346).

## 9. Optional dual-boot preferences

### 9.1 Set the actual timezone and network time

**Purpose:** inspect time settings and available timezone names.

```bash
timedatectl
timedatectl list-timezones
```

**Purpose:** set Hebron time, if that is your location; otherwise substitute the correct listed timezone.

```bash
sudo timedatectl set-timezone Asia/Hebron
sudo timedatectl set-ntp true
```

### 9.2 Optional workaround for the Windows/Ubuntu clock offset

**Purpose:** use local time in the hardware clock to match a Windows installation that uses local RTC time.

```bash
timedatectl set-local-rtc 1 --adjust-system-clock
timedatectl
```

Local RTC has daylight-saving/timezone limitations; keeping both operating systems on UTC is another approach. Apply only if needed and check the displayed time in both systems. Undo this Ubuntu change with:

```bash
timedatectl set-local-rtc 0 --adjust-system-clock
```

### 9.3 Boot Windows by default

**Purpose:** make a shared UEFI laptop start Windows normally. In firmware settings, move **Windows Boot Manager** above **ubuntu**. Use the manufacturer's one-time boot menu to choose Ubuntu when needed. This firmware option does not apply identically to Legacy BIOS systems.

If you prefer GRUB's menu, configure its default entry using the actual menu title or ID on this computer; do not assume Windows is entry number `2`.

**Purpose:** inspect generated menu entries and edit the source configuration.

```bash
grep -E "^[[:space:]]*(menuentry|submenu) " /boot/grub/grub.cfg
sudo cp -n /etc/default/grub /etc/default/grub.before-customization
sudo nano /etc/default/grub
```

Set `GRUB_DEFAULT` to the exact Windows entry title in quotes, and optionally set `GRUB_TIMEOUT=10`. Do not edit `/boot/grub/grub.cfg` directly. In nano: `Ctrl+O`, Enter to save, then `Ctrl+X` to exit.

**Purpose:** regenerate GRUB's menu after editing.

```bash
sudo update-grub
```

Grub Customizer and rEFInd were mentioned as alternatives, not demonstrated necessities. Keep a working bootloader for the basic installation; a graphical boot-manager replacement needs its own UEFI/Secure Boot review.

## 10. Troubleshoot installation and ROS dependency errors

### 10.1 APT: missing key, duplicate ROS sources, or curl unavailable

**Purpose:** inspect ROS source definitions before changing them.

```bash
grep -RnsE 'packages\.ros\.org|ros-apt-source|ros-archive-keyring' \
  /etc/apt/sources.list /etc/apt/sources.list.d/
```

For the old `ros-latest.list` created by the chats, inspect it first:

```bash
cat /etc/apt/sources.list.d/ros-latest.list
```

**Purpose:** disable only that old ROS-only file while preserving a backup. Run only if it exists and contains the obsolete entry; choose another backup name if the destination already exists.

```bash
sudo mv -n /etc/apt/sources.list.d/ros-latest.list \
  /etc/apt/sources.list.d/ros-latest.list.disabled
sudo apt update
sudo apt install ca-certificates curl
```

Then follow section 5.1. If another file contains the same old source, back it up and disable only the conflicting entry. Do not disable the new package-managed source. A successful setup has one consistent ROS 1 source definition and no `Signed-By` conflict. Do not blindly delete keys or use `trusted=yes`/`--allow-unauthenticated`.

For `Unable to locate package`, verify `focal`, Internet access, repository configuration, and a successful `sudo apt update`. For a failing Ubuntu mirror, select a working Ubuntu mirror in Software & Updates; keep the release set to Focal.

### 10.2 APT: lock, interrupted installation, or unmet dependencies

**Purpose:** identify an active package-manager process.

```bash
ps -ef | grep -E '[a]pt|[d]pkg|[u]nattended-upgrade'
```

Wait for the installer/updater to finish. Do not delete lock files or reboot in the middle of a healthy installation.

**Purpose:** finish package configuration and repair dependencies after an interrupted installation, once no other package manager is running.

```bash
sudo dpkg --configure -a
sudo apt --fix-broken install
sudo apt update
```

Review proposed changes. “You have held broken packages” does not prove packages were explicitly held. Check with:

```bash
apt-mark showhold
```

If a selected NVIDIA package still has unsatisfied dependencies, inspect its candidate/version and select a compatible offered driver. `--fix-broken` does not clear a live package lock or manufacture missing repository packages.

### 10.3 rosdep/rospack errors

**Purpose:** verify the environment, refresh EOL metadata, and refresh package discovery.

```bash
source /opt/ros/noetic/setup.bash
source ~/catkin_ws/devel/setup.bash
echo "$ROS_DISTRO $ROS_PYTHON_VERSION"
rosdep update --include-eol-distros
rospack profile
rosdep check --from-paths ~/catkin_ws/src --ignore-src --rosdistro noetic
```

If you accidentally used `sudo rosdep update` and now receive cache permission errors:

```bash
sudo rosdep fix-permissions
rosdep update --include-eol-distros
```

**Repair the invalid commit URL from chat6 only if you previously applied it.** The hash `d560ac3635f28eedf2b0d8efbc4ac5fe9ce1a8d1` returned 404 in the actual chat. Do not reuse it.

```bash
cat /etc/ros/rosdep/sources.list.d/20-default.list
sudo cp -n /etc/ros/rosdep/sources.list.d/20-default.list \
  /etc/ros/rosdep/sources.list.d/20-default.list.before-repair
sudo sed -i \
  's|ros/rosdistro/d560ac3635f28eedf2b0d8efbc4ac5fe9ce1a8d1|ros/rosdistro/master|g' \
  /etc/ros/rosdep/sources.list.d/20-default.list
rosdep update --include-eol-distros
```

A message saying the cache was updated is not success if the same run reports failed sources. Resolve all download/404 errors first.

**Purpose:** distinguish an unknown dependency rule from an absent installed package.

```bash
rosdep resolve python3-catkin-pkg --rosdistro noetic
rosdep resolve python3-empy --rosdistro noetic
apt-cache policy python3-catkin-pkg python3-empy python3-rospkg
```

Legacy `python-*` names in `rospack` warnings may involve conditional dependency metadata or unavailable rules; they do not automatically mean Python 2 must be installed. The final chat proposed local Python 3 mappings, but there is no subsequent output proving they fixed the problem. They are therefore an **optional workaround**, not a required fresh-install step; see Appendix C.

### 10.4 Shell and workspace mistakes

**Purpose:** inspect and edit the Bash startup file if commands are missing or duplicate source lines accumulated.

```bash
ls -la ~/.bashrc
echo "$HOME/.bashrc"
tail -n 25 ~/.bashrc
cp -n ~/.bashrc ~/.bashrc.before-ros-cleanup
nano ~/.bashrc
```

Keep one core ROS source line, then one valid workspace source line. Remove only confirmed duplicate or invalid lines. If the workspace does not exist, remove/comment its source line until you build it.

**Purpose:** apply the corrected file.

```bash
source ~/.bashrc
```

Do not paste terminal prompts, printed paths, error messages, or package-name output as commands. `udo` is a typo for `sudo`, not a package you need. `>> ~/.bashrc` appends; `> ~/.bashrc` overwrites the whole file.

If `catkin_make` runs out of RAM, retry with fewer jobs from the workspace root:

```bash
cd ~/catkin_ws
catkin_make -j2 -l2
```

## 11. Optional mouse, touchpad, audio, and sleep fixes

### 11.1 Bluetooth mouse

**Purpose:** install Bluetooth utilities, unblock the adapter, and start its service.

```bash
sudo apt install bluez rfkill
rfkill list
sudo rfkill unblock bluetooth
sudo systemctl enable --now bluetooth
bluetoothctl
```

Put the mouse into pairing mode. **Inside `bluetoothctl`**, enter:

```text
power on
agent on
default-agent
scan on
```

Replace the address below with the mouse's discovered Bluetooth address, then enter these at the same Bluetooth prompt:

```text
pair AA:BB:CC:DD:EE:FF
trust AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF
scan off
exit
```

Trust permits reconnection but cannot guarantee it if the adapter, firmware, or mouse fails. If no controller is available, inspect `lsusb`, `lspci -k`, `rfkill list`, and kernel logs. A 2.4 GHz receiver mouse usually needs its USB receiver; Wi-Fi installation commands are not a generic mouse driver solution.

### 11.2 Disable a faulty touchpad — only if needed

Connect and test an alternative pointing device first. Run these as your desktop user, without sudo.

**Purpose:** disable touchpad events for the current GNOME user.

```bash
gsettings set org.gnome.desktop.peripherals.touchpad send-events disabled
```

**Purpose:** undo the change.

```bash
gsettings set org.gnome.desktop.peripherals.touchpad send-events enabled
```

**Optional X11-only diagnostic:** list devices and temporarily disable the actual touchpad, replacing the quoted name.

```bash
echo "$XDG_SESSION_TYPE"
xinput list
xinput disable "REPLACE_WITH_EXACT_TOUCHPAD_NAME"
```

Undo with `xinput enable "REPLACE_WITH_EXACT_TOUCHPAD_NAME"`. Device IDs may change after reboot. `xinput` is not a general Wayland solution.

The chats also suggested blacklisting `psmouse`. That can disable other PS/2 pointing devices, does not target every I2C touchpad, and is not literally a power-off command. Prefer a touchpad-specific firmware setting for problems before login; do not apply a blanket kernel-module blacklist as routine setup.

### 11.3 Pinch-to-zoom

**Purpose:** identify the graphical session before changing gesture settings.

```bash
echo "$XDG_SESSION_TYPE"
```

Gestures depend on the touchpad, desktop version, application, and X11/Wayland session. Ubuntu 20.04 commonly uses Xorg; if offered at login, **Ubuntu on Wayland** is the explicit alternative. Do not assume the plain “Ubuntu” entry always means Wayland. NVIDIA support may constrain the choice.

In Firefox, inspect `about:config` for `apz.gtk.touchpad_pinch.enabled` if supported by that version. Chrome's Wayland/Ozone options vary by version. These are browser settings, not terminal commands, and no single command guarantees Windows-equivalent gestures. `Ctrl` + `+`/`-` is a simple application zoom alternative.

### 11.4 Audio on Ubuntu 20.04

**Purpose:** identify audio devices and the sound server.

```bash
aplay -l
pactl info
lspci -k | grep -A3 -i audio
```

**Purpose:** install the mixer tools and update available audio firmware/configuration.

```bash
sudo apt install alsa-utils pavucontrol
apt-cache policy firmware-sof-signed alsa-ucm-conf linux-firmware
sudo apt install firmware-sof-signed alsa-ucm-conf linux-firmware
```

If a package has no candidate, stop and check the Focal repositories; do not add another Ubuntu release. Firmware does not fix every amplifier/codec issue.

**Purpose:** choose the correct output/profile and unmute the correct hardware channels.

```bash
pavucontrol
```

Close it when done, then:

```bash
alsamixer
```

In `alsamixer`, use `F6` to choose the intended card and `M` to toggle mute. Start at moderate volume. Test speakers and headphones separately.

Ubuntu 20.04 normally uses PulseAudio. The EasyEffects/PipeWire command in chat7 is not a standard Focal step. Optional PulseAudio effects, if available for your setup:

```bash
apt-cache policy pulseeffects
sudo apt install pulseeffects
```

Effects are not driver repairs and cannot guarantee the same sound as Windows' vendor processing.

### 11.5 Laptop freezes after suspend or closing the lid

**Purpose:** identify supported sleep modes and inspect the previous boot's logs.

```bash
cat /sys/power/mem_sleep
journalctl --list-boots
journalctl -b -1 -u systemd-suspend.service
journalctl -b -1 -k | tail -n 120
```

Previous-boot logs may be unavailable if not retained. Update the appropriate drivers/kernel first and test one change at a time. Disabling sleep is not a repair for a failed resume.

**Conditional test:** only if `deep` appears in `/sys/power/mem_sleep`, temporarily select it, save work, and test suspend.

```bash
echo deep | sudo tee /sys/power/mem_sleep
systemctl suspend
```

Wake with the power button if necessary. The mode selection resets at reboot. If this helps, preserve existing GRUB arguments and append `mem_sleep_default=deep` inside the quotes of `GRUB_CMDLINE_LINUX_DEFAULT`:

```bash
sudo cp -n /etc/default/grub /etc/default/grub.before-sleep-change
sudo nano /etc/default/grub
```

Example file content, **not a shell command**:

```text
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash mem_sleep_default=deep"
```

Apply and reboot:

```bash
sudo update-grub
sudo reboot
```

To undo, remove only `mem_sleep_default=deep`, run `sudo update-grub`, and reboot. If `deep` is absent, this parameter cannot create firmware support for it.

**NVIDIA-specific diagnosis:** inspect the installed driver's services and parameters before changing them.

```bash
systemctl status nvidia-suspend.service nvidia-resume.service nvidia-hibernate.service
grep -E 'PreserveVideoMemoryAllocations|TemporaryFilePath' /proc/driver/nvidia/params
df -h /var/tmp
```

The chats' video-memory-preservation option must be paired with the correct suspend/resume integration and sufficient backing storage. It is not a universal fix. Use the power-management README for the **installed driver version**, under [NVIDIA's driver documentation](https://download.nvidia.com/XFree86/Linux-x86_64/), before enabling these options. Appendix D records the conditional commands.

## 12. Maintenance after a successful setup

**External-drive installations:** also follow section 2E.12 after shim/GRUB updates, and retest the portable USB boot path. Always shut Ubuntu down fully before disconnecting its system drive.

**Purpose:** keep packages updated within the configured Ubuntu release.

```bash
sudo apt update
sudo apt upgrade
rosdep update --include-eol-distros
```

**Purpose:** preview unused packages before cleanup.

```bash
sudo apt autoremove --dry-run
```

Only if the list is appropriate:

```bash
sudo apt autoremove
```

Keep a known-working fallback kernel until the new one has been tested. Back up `~/catkin_ws/src` and project configuration; generated `build`/`devel` folders can be rebuilt. Share this Markdown file by USB or a repository/Gist; friends should read commands before running them rather than pipe a shared page directly into a shell.

## Appendix A. Package installation and useful ROS commands

These are optional after setup. Runtime commands need `roscore` and the relevant nodes running. Stop continuous commands with `Ctrl+C`.

### A.1 Discover packages

```bash
# List installed/discoverable ROS packages and their locations.
rospack list

# List only names.
rospack list-names

# Find an installed package.
rospack find turtlesim

# Search installable Noetic packages.
apt-cache search ros-noetic-

# Install an example extra package.
sudo apt install ros-noetic-teleop-twist-keyboard

# Inspect direct and recursive dependencies of the lab package.
rospack depends1 test_package_1
rospack depends test_package_1

# Navigate to a package and inspect its manifest.
roscd test_package_1
cat package.xml

# List package files, or copy a manifest into the current directory.
rosls turtlesim
roscp turtlesim package.xml ./turtlesim-package.xml
```

APT package names commonly replace underscores with hyphens, e.g. `rqt_graph` → `ros-noetic-rqt-graph`. `ros2 pkg ...` from chat4 is for ROS 2, not Noetic.

### A.2 Build an existing source project

**Purpose:** download the project's documented ROS 1/Noetic branch. Replace the URL and branch; do not assume every repository has a branch literally called `noetic`.

```bash
cd ~/catkin_ws/src
git clone --branch REPLACE_WITH_NOETIC_COMPATIBLE_BRANCH REPLACE_WITH_REPOSITORY_URL
```

**Purpose:** install declared dependencies, then build and activate the workspace.

```bash
cd ~/catkin_ws
rosdep install --from-paths src --ignore-src --rosdistro noetic
catkin_make
source devel/setup.bash
```

Proceed to the build only after resolving dependency errors. The `-r` used in the chats continues past failures; it does not mean dependencies were successfully installed.

**Purpose:** make an existing Python ROS node executable. Use its real path.

```bash
chmod +x ~/catkin_ws/src/REPLACE_WITH_PACKAGE/scripts/REPLACE_WITH_NODE.py
```

A Python node needs a Python 3 shebang and declared dependencies. A C++ node must be registered in its package's CMake configuration and compiled. The lengthy controller examples in chat6 are application exercises, not installation steps; some need corrections before use.

### A.3 Inspect nodes, topics, and messages in Turtlesim

```bash
# Inspect node connectivity.
rosnode list
rosnode info /turtlesim
rosnode ping /turtlesim

# Inspect topics, types, message structure, and actual data.
rostopic list
rostopic info /turtle1/cmd_vel
rostopic type /turtle1/cmd_vel
rosmsg show geometry_msgs/Twist
rostopic echo /turtle1/pose
```

Run these continuous/graphical commands separately:

```bash
# Measure publication frequency.
rostopic hz /turtle1/pose
```

```bash
# View the node/topic graph.
rosrun rqt_graph rqt_graph
```

```bash
# Plot pose fields.
rosrun rqt_plot rqt_plot /turtle1/pose/x /turtle1/pose/y /turtle1/pose/theta
```

**Purpose:** publish one simulated velocity command. Use only the Turtlesim topic shown here, not a physical robot's velocity topic.

```bash
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- \
  '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
```

**Purpose:** continuously move the simulated turtle; `10 Hz` provides margin over its command timeout. Stop with `Ctrl+C`.

```bash
rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist -- \
  '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
```

**Purpose:** send an explicit stop command.

```bash
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- \
  '[0.0, 0.0, 0.0]' '[0.0, 0.0, 0.0]'
```

**Purpose:** run a separate simulator without a node-name or topic-name collision.

```bash
rosrun turtlesim turtlesim_node __ns:=second_sim __name:=turtlesim
```

### A.4 Services and parameters

```bash
# List and inspect services.
rosservice list
rosservice type /clear
rossrv show turtlesim/Spawn

# Clear drawn trails and spawn another turtle.
rosservice call /clear
rosservice call /spawn "{x: 2.0, y: 2.0, theta: 0.2, name: 'my_turtle'}"

# Inspect and change background parameters, then refresh the display.
rosparam list
rosparam get /turtlesim/background_g
rosparam set /turtlesim/background_r 150
rosservice call /clear

# Save parameters and load them into a separate namespace.
cd ~/catkin_ws
rosparam dump param.yaml
rosparam load param.yaml copy_turtle
rosparam get /copy_turtle/turtlesim/background_r
```

Use the same filename for dump/load; the chats mention a `param.yaml`/`params.yaml` typo. Loading under `copy_turtle` stores parameters there; it does not create a running turtle node.

### A.5 Bags, TF, and serial integration

```bash
# Record only the test topics; stop recording with Ctrl+C.
rosbag record -O turtle-test.bag /turtle1/pose /turtle1/cmd_vel
```

```bash
# Inspect and replay the recorded data in an isolated simulation session.
rosbag info turtle-test.bag
rosbag play turtle-test.bag
```

Playback republishes commands too; do not replay control bags onto a live robot unintentionally. Unlike recording every topic with `-a`, selecting topics limits disk consumption.

```bash
# Install the optional TF inspection tool and Graphviz renderer.
sudo apt install ros-noetic-tf2-tools graphviz

# Generate a frame diagram when a robot is publishing TF.
rosrun tf2_tools view_frames.py
```

```bash
# Install a serial bridge only for firmware that actually implements rosserial.
sudo apt install ros-noetic-rosserial-python

# Substitute the actual port and the baud rate configured in the firmware.
rosrun rosserial_python serial_node.py _port:=/dev/ttyUSB0 _baud:=115200
```

**Purpose:** stop a selected ROS node cleanly, or clean up stale node registrations after a crash.

```bash
rosnode kill /turtlesim
rosnode cleanup
```

For stuck Gazebo processes, first use `Ctrl+C` in the launching terminal. If needed, inspect `pgrep -a gzserver` and `pgrep -a gzclient`, then terminate only the identified processes. The blanket `killall -9` from the chats is a last resort, not normal shutdown.

## Appendix B. Alternatives to native dual boot

These are separate installation paths, not additional steps to run on the native Ubuntu system. A VM or container can keep Focal user space on newer hardware; graphics, USB, networking, and resource requirements need separate configuration. Installing a Focal container does not make the host's unsupported hardware drivers work.

### B.1 WSL2 on Windows

**Purpose:** inspect available distributions, install Ubuntu 20.04 if listed, and verify WSL version. Run in Windows PowerShell; installation may require Administrator rights and a restart.

```powershell
wsl --list --online
wsl --set-default-version 2
wsl --install -d Ubuntu-20.04
wsl --list --verbose
```

If the installed distribution is version 1, convert it:

```powershell
wsl --set-version Ubuntu-20.04 2
```

Then run the Ubuntu/ROS software steps inside that distribution. Skip partitioning, GRUB, native Ubuntu GPU-driver installation, lid management, and native kernel installation. Keep the Catkin workspace under Linux `~/`, not `/mnt/c`. WSL graphics use the Windows/WSL stack. [Microsoft WSL installation](https://learn.microsoft.com/en-us/windows/wsl/install).

**Purpose:** install USB forwarding support for a device needed inside WSL. [Microsoft USB instructions](https://learn.microsoft.com/en-us/windows/wsl/connect-usb).

```powershell
winget install --interactive --exact dorssel.usbipd-win
usbipd list
```

Bind the actual bus ID as Administrator, then attach with a WSL session running:

```powershell
usbipd bind --busid REPLACE_WITH_BUS_ID
usbipd attach --wsl --busid REPLACE_WITH_BUS_ID
```

The device is unavailable to Windows while attached. Detach when finished:

```powershell
usbipd detach --busid REPLACE_WITH_BUS_ID
```

### B.2 Docker — only on a host with Docker already configured

**Purpose:** start a disposable Noetic shell for basic CLI work.

```bash
docker run --rm -it ros:noetic-ros-base bash
```

This is not a desktop-full installation. Changes disappear when this container exits unless saved in an image or mounted storage; RViz/Gazebo, USB devices, and ROS networking need additional setup. Do not add permissive X11 access or `--privileged` just to make a basic shell work.

## Appendix C. Conditional legacy rosdep mapping workaround

Use only on **Focal + Noetic + Python 3**, after restoring valid source URLs and checking that the exact legacy keys still fail. This adapts the unverified final suggestion in chat6; it is not evidence that every warning in that chat had the same cause. Check your package manifests before treating a true Python 2 dependency as Python 3.

**Purpose:** verify that the replacement packages are available.

```bash
apt-cache policy python3-catkin-pkg python3-empy python3-rospkg \
  python3-catkin-pkg-modules python3-rosdep-modules
```

Proceed only if the relevant candidates exist. If either destination file below already exists, inspect and back it up before modifying it.

**Purpose:** define local mappings restricted to Ubuntu Focal.

```bash
sudo tee /etc/ros/rosdep/focal-python.yaml >/dev/null <<'EOF'
python-catkin-pkg:
  ubuntu:
    focal: [python3-catkin-pkg]
python-empy:
  ubuntu:
    focal: [python3-empy]
python-rospkg:
  ubuntu:
    focal: [python3-rospkg]
python-catkin-pkg-modules:
  ubuntu:
    focal: [python3-catkin-pkg-modules]
python-rosdep-modules:
  ubuntu:
    focal: [python3-rosdep-modules]
EOF
```

**Purpose:** register the mapping before the default list, refresh the cache, and check resolution.

```bash
echo 'yaml file:///etc/ros/rosdep/focal-python.yaml' | \
  sudo tee /etc/ros/rosdep/sources.list.d/10-focal-python.list
rosdep update --include-eol-distros
rosdep resolve python-catkin-pkg --rosdistro noetic
rospack depends1 test_package_1
```

These mappings only teach rosdep what to install; they do not install the packages themselves. Re-run the workspace dependency installation from Appendix A.2 if needed. If the result is still wrong, diagnose it rather than adding increasingly broad aliases.

**Purpose:** disable this added override while retaining its contents, if it is no longer needed.

```bash
sudo mv -n /etc/ros/rosdep/sources.list.d/10-focal-python.list \
  /etc/ros/rosdep/sources.list.d/10-focal-python.list.disabled
rosdep update --include-eol-distros
```

## Appendix D. Advanced hardware options mentioned in the chats

These are conditional experiments, not proven fixes on every laptop. Record current values and make one change at a time.

### D.1 NVIDIA video-memory preservation

Only for a driver whose documentation requires the `/proc/driver/nvidia/suspend` integration: verify the services exist, that `/var/tmp` has backing storage larger than the total GPU memory plus overhead, and that no conflicting options already exist.

```bash
grep -RnsE 'PreserveVideoMemoryAllocations|TemporaryFilePath' /etc/modprobe.d/
systemctl is-enabled nvidia-suspend.service nvidia-hibernate.service nvidia-resume.service
```

**Purpose:** edit the NVIDIA power-management options; preserve/back up any existing file.

```bash
sudo nano /etc/modprobe.d/nvidia-power-management.conf
```

File content to add only when appropriate:

```text
options nvidia NVreg_PreserveVideoMemoryAllocations=1 NVreg_TemporaryFilePath=/var/tmp
```

**Purpose:** enable the matching service integration and rebuild the initramfs.

```bash
sudo systemctl enable nvidia-suspend.service nvidia-hibernate.service nvidia-resume.service
sudo update-initramfs -u
sudo reboot
```

If unsuccessful, restore the file's prior contents and service states, rebuild the initramfs, and reboot. Enabling the hibernate service does not configure system hibernation itself. Reference: [NVIDIA power-management documentation](https://download.nvidia.com/XFree86/Linux-x86_64/595.104.02/README/powermanagement.html); use the corresponding documentation for your installed driver, not settings from a different driver generation.

### D.2 ACPI wake sources

**Purpose:** inspect wake sources if logs suggest immediate unwanted wakeups.

```bash
cat /proc/acpi/wakeup
```

**Conditional example:** only if `XHC` is the identified problematic entry, this toggles it. It can disable USB wake, so retain another way to wake the laptop.

```bash
echo XHC | sudo tee /proc/acpi/wakeup
cat /proc/acpi/wakeup
```

The same write toggles it back; read the state first. Do not toggle every enabled entry. This is not a general cure for freezes.

### D.3 Disable lid-triggered suspend temporarily

This changes behavior rather than repairing suspend; skip it if you want normal lid sleep. Desktop power settings may override logind through inhibitors.

**Purpose:** edit logind's lid behavior if a temporary workaround is wanted.

```bash
sudo cp -n /etc/systemd/logind.conf /etc/systemd/logind.conf.before-lid-change
sudo nano /etc/systemd/logind.conf
```

Set under the existing `[Login]` section:

```ini
HandleLidSwitch=ignore
```

Save work and reboot to apply. Restarting `systemd-logind` in an active desktop session, as suggested in the chats, can disrupt sessions. Revert the setting to restore prior behavior. An ignored lid can leave a laptop running and heating inside a bag.

## Appendix E. Moving /home after installation — optional, offline procedure

You do not need a separate `/home` for ROS. This advanced procedure is for an ordinary unencrypted ext4 root with `/home` currently a normal directory on that root. LVM, encryption, Btrfs subvolumes, existing separate `/home`, or other mounts inside `/home` need a different plan. Back up data independently first.

**Purpose:** perform the copy while the installed system is offline, avoiding the active-session copy/rename problem in chat8.

1. Boot **Try Ubuntu** from a live USB. In GParted, create a new empty ext4 partition from suitable space. Do not shrink a mounted filesystem. Identify the installed root and new home partitions using `lsblk -f`.
2. Replace the two device placeholders below. Mount only the positively identified partitions. The new partition must be empty and large enough.

```bash
lsblk -f
sudo mkdir -p /mnt/installed /mnt/newhome
sudo mount /dev/REPLACE_WITH_ROOT_PARTITION /mnt/installed
sudo mount /dev/REPLACE_WITH_NEW_HOME_PARTITION /mnt/newhome
findmnt /mnt/installed
findmnt /mnt/newhome
```

**Purpose:** copy all home data with ownership, permissions, links, ACLs, and extended attributes.

```bash
sudo rsync -aHAXx --numeric-ids --info=progress2 /mnt/installed/home/ /mnt/newhome/
```

**Purpose:** verify content and metadata with a checksum dry run; inspect any differences before proceeding.

```bash
sudo rsync -aHAXxnc --numeric-ids --itemize-changes /mnt/installed/home/ /mnt/newhome/
sudo blkid /dev/REPLACE_WITH_NEW_HOME_PARTITION
```

**Purpose:** back up and edit the installed system's mount configuration, not the live USB's configuration.

```bash
sudo cp -n /mnt/installed/etc/fstab /mnt/installed/etc/fstab.before-home-move
sudo nano /mnt/installed/etc/fstab
```

Add a line using the new ext4 partition's actual UUID:

```fstab
UUID=REPLACE_WITH_ACTUAL_UUID /home ext4 defaults 0 2
```

**Purpose:** retain the original home as a fallback and prepare the new mount point. First verify `/mnt/installed/home.old` does not already exist; stop if it does.

```bash
sudo test ! -e /mnt/installed/home.old && \
  sudo mv /mnt/installed/home /mnt/installed/home.old
sudo mkdir -p /mnt/installed/home
sudo chmod 755 /mnt/installed/home
sudo umount /mnt/newhome
sudo mount /dev/REPLACE_WITH_NEW_HOME_PARTITION /mnt/installed/home
findmnt /mnt/installed/home
ls -la /mnt/installed/home
```

Verify the expected user directories. Then unmount and reboot into the installed system:

```bash
sudo umount /mnt/installed/home
sudo umount /mnt/installed
sudo reboot
```

**Purpose:** confirm the new home mount after login.

```bash
findmnt /home
df -h /home
```

Check files, ownership, application settings, and ROS workspace builds. Keep `/home.old` until the migration is verified and an independent backup exists; the automatic `rm -rf /home.old` cleanup from chat8 is intentionally not part of this walkthrough.

**Rollback if login fails:** boot the live USB, mount the installed root, restore its saved `fstab`, unmount any new home mount, and restore `home.old` to `home` after confirming the current root-side `home` directory is empty. Preserve both copies until recovery is complete.