Ubuntu 22.04 · ROS 2 Humble. Do the steps in order. Each step names the terminal to type in.
sudo apt update && sudo apt install -y git
git clone https://github.com/DronaAviation/ROS2_Tutorial26.git ~/ROS2_Tutorialbash ~/ROS2_Tutorial/install.shTakes 15–40 minutes.
bash ~/ROS2_Tutorial/check_setup.shExpected: All checks passed. This laptop is ready. Fix every FAIL before Lab 1.
cd ~/ROS2_Tutorial
code .- Open
LAB_HANDOUT.md, press Ctrl+Shift+V, drag the tab to the left half. - **Ctrl+
** opens a terminal. Click the split icon three times. Rename themT1,T2,T3,T4`.
+---------------------------+---------------------------+
| | T1 | T2 |
| LAB_HANDOUT.md |--------------+------------|
| | T3 | T4 |
+---------------------------+---------------------------+
source ~/ROS2_Tutorial/env.shExpected: ROS 2 humble ready (this laptop only). Tutorial folder: ...
Terminal keys: copy / paste = Ctrl+Shift+C / Ctrl+Shift+V. Ctrl+C stops the program.
Setup line in T1, T2, T3, T4.
1.1 — T1
ros2 run turtlesim turtlesim_nodeExpected: a blue window with a turtle. Don't type in T1 again.
1.2 — T2
ros2 run turtlesim turtle_teleop_keyClick inside T2, press the arrow keys.
✅ Checkpoint: ↑ in T2 moves the turtle.
1.3 — T3
rqt_graphExpected: /teleop_turtle → /turtle1/cmd_vel → /turtlesim. Close the window.
1.4 — T3 (one at a time)
ros2 node listros2 topic listros2 topic info /turtle1/cmd_velros2 interface show geometry_msgs/msg/Twist1.5 — T3
ros2 topic echo /turtle1/cmd_velClick T2, press ↑, ←, ↓, →. Watch T3. Then Ctrl+C in T3.
1.6 — T3
ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0}, angular: {z: 1.8}}"Expected: the turtle drives in circles. Ctrl+C to stop.
Try: bigger z · negative z · x: 0.0.
1.7 — T3
ros2 service call /reset std_srvs/srv/Emptypython3 1_turtlesim/turtle_circle.pyExpected: one full circle, then the script exits.
✅ Checkpoint: your script drew the circle.
1.8 — T4
python3 1_turtlesim/turtle_pose.pyExpected: x=… y=… heading=… twice a second. Drive with T2 and watch it change.
✅ Checkpoint: T4's numbers move when the turtle moves.
Edit only STEPS in 1_turtlesim/turtle_circle.py:
STEPS = [
('side', 2.0, twist(forward=1.0)),
('turn', 1.0, twist(turn=math.pi / 2)),
] * 4T3:
ros2 service call /reset std_srvs/srv/Emptypython3 1_turtlesim/turtle_circle.pyThen try: a triangle · a bigger circle · two laps. Reset before each run.
More: tasks T1–T4 in TASKS.md.
ros2 service call /reset std_srvs/srv/EmptyCtrl+C in T4, T3, T1. q in T2. Then:
ros2 node listExpected: no output. Close all four terminals.
Four new terminals. Setup line in each.
2.1 — T1
ign gazebo -r multicopter_velocity_control.sdfExpected: Gazebo opens with two drones. Ours is X3. The first start can take minutes.
2.2 — T2
ros2 run ros_gz_bridge parameter_bridge "/X3/gazebo/command/twist@geometry_msgs/msg/Twist]ignition.msgs.Twist"Expected: Creating ROS->GZ Bridge. Leave it running.
2.3 — T3
ros2 node listros2 topic listign topic -l2.4 — T3
ros2 topic pub --once /X3/gazebo/command/twist geometry_msgs/msg/Twist "{linear: {z: 0.5}}"After 2–3 seconds:
ros2 topic pub --once /X3/gazebo/command/twist geometry_msgs/msg/Twist "{}"✅ Checkpoint: the drone hovers.
2.5 — T4
ros2 topic echo /X3/gazebo/command/twist2.6 — T3
python3 2_gazebo/sim_drone.pyExpected: climb → circle → hover → descend → hover.
2.7 — T3
diff 1_turtlesim/turtle_circle.py 2_gazebo/sim_drone.pyWrite down which lines are different.
Edit STEPS in 2_gazebo/sim_drone.py. Try: climb higher · two circles · a square.
To put the drone back on the ground: Ctrl+C in T1, run 2.1 again.
Drone on the ground first (Ctrl+C in T1, run 2.1 again). Keep T2 and T4 running.
2.8 — T3
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/X3/gazebo/command/twist2.9 — T3 (click inside T3 first)
t climb · b descend · i / , forward / back · j / l turn · k hover
✅ Checkpoint: take off, fly forward, turn, land. Then Ctrl+C in T3.
2.10 — T2 (Ctrl+C first)
ros2 run ros_gz_bridge parameter_bridge "/X3/gazebo/command/twist@geometry_msgs/msg/Twist]ignition.msgs.Twist" "/model/x3/odometry@nav_msgs/msg/Odometry[ignition.msgs.Odometry"Expected: Creating ROS->GZ Bridge and Creating GZ->ROS Bridge.
2.11 — T4 (Ctrl+C first)
python3 2_gazebo/sim_drone_pose.pyExpected: x=0.00 y=-0.00 height=0.05 m climb rate=+0.00 m/s, twice a second.
2.12 — T3
python3 2_gazebo/sim_drone.py✅ Checkpoint: T4 shows the height rise to about 1.3 m, sag in the circle, and come back to 0.05 m.
More: tasks D1–D4 in TASKS.md.
Ctrl+C in T4, T2, T1. Close the Gazebo window if it stays open.
- Open floor. 2 m clear, people behind the line, fresh battery.
- Keyboard control reads the whole laptop. Space arms/disarms. Start other terminals before it. Stop it (Ctrl+C) before typing anywhere else.
- Never run
pluto_flight.pyorros2 topic pubwhile keyboard control runs. - Emergency stop: keyboard flight → space. Script flight → Ctrl+C.
- Stop
plutonodeonly when landed and disarmed. Close its terminal or press Ctrl+\.
- Battery charged and fitted; propellers OK
- Drone on flat floor, nothing within 2 m
- Laptop on Wi-Fi
Pluto_XXXX, passworddronaaviation - Four terminals, setup line run in each
3.1 — T1
ros2 run plutodrone plutonodeExpected: Pluto Connected.
3.2 — T2
ros2 run plutoserver plutoserverExpected: Roll/Pitch/Yaw, Altitude, Battery. Tilt the drone by hand. Write down the battery voltage.
3.3 — T4
ros2 node list
ros2 interface show custom_msgs/msg/PlutoMsg
ros2 topic echo /drone_command custom_msgs/msg/PlutoMsgFrom here until 3.5, do not type in any terminal.
3.4 — T3
ros2 launch plutoserver drone_comb.launch.py| Key | Action | Key | Action |
|---|---|---|---|
| space | Arm / disarm | ↑ ↓ | Forward / back |
| q | Take off | ← → | Left / right |
| e | Land | a / d | Turn left / right |
| w / s | Up / down | r | Sticks to centre |
Do not press t.
Sequence: space → q → gentle moves → e → space. Watch T4.
3.5 — T3: Ctrl+C. Typing is safe again.
3.6 — T3 (one at a time, wait between)
Take off:
ros2 topic pub --once /drone_command custom_msgs/msg/PlutoMsg "{rc_roll: 1500, rc_pitch: 1500, rc_yaw: 1500, rc_throttle: 1500, rc_aux1: 1500, rc_aux2: 1500, rc_aux3: 1500, rc_aux4: 1500, command_type: 1}"Land:
ros2 topic pub --once /drone_command custom_msgs/msg/PlutoMsg "{rc_roll: 1500, rc_pitch: 1500, rc_yaw: 1500, rc_throttle: 1500, rc_aux1: 1500, rc_aux2: 1500, rc_aux3: 1500, rc_aux4: 1500, command_type: 2}"Disarm:
ros2 topic pub --once /drone_command custom_msgs/msg/PlutoMsg "{rc_roll: 1500, rc_pitch: 1500, rc_yaw: 1500, rc_throttle: 1300, rc_aux1: 1500, rc_aux2: 1500, rc_aux3: 1500, rc_aux4: 1200, command_type: 0}"Copy the commands exactly. Do not leave out any rc_ field.
3.7 — T3 (first run with the propellers removed)
python3 3_pluto/pluto_flight.pyExpected: idle → take off and hover → land → disarm. Watch T4. Then fit the propellers and run it again.
T2: Ctrl+C to stop plutoserver, then:
python3 3_pluto/pluto_telemetry.pyExpected: battery=… V altitude=… roll=… pitch=… yaw=…. Tilt the drone.
Land and disarm. Ctrl+C in T3, T4. Close the T1 and T2 terminals. Power off, unplug the battery.
Setup line in T1, T2, T3.
T1:
ros2 run turtlesim turtlesim_nodeT2:
ros2 run turtlesim turtle_teleop_keyEverything below in T3, one at a time.
Nodes
ros2 node listros2 node info /turtlesimTopics
ros2 topic list -tros2 topic info /turtle1/cmd_velros2 topic echo /turtle1/poseros2 topic hz /turtle1/poseros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 1.0}, angular: {z: 1.0}}"Message types
ros2 interface show geometry_msgs/msg/Twistros2 interface show turtlesim/msg/PoseServices
ros2 service list -tros2 interface show turtlesim/srv/Spawnros2 service call /spawn turtlesim/srv/Spawn "{x: 2.0, y: 2.0, theta: 0.0, name: 'turtle2'}"ros2 topic listros2 service call /clear std_srvs/srv/Emptyros2 service call /reset std_srvs/srv/EmptyActions
ros2 action list -tros2 interface show turtlesim/action/RotateAbsoluteros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.57}" --feedbackParameters
ros2 param listros2 param get /turtlesim background_bros2 param set /turtlesim background_r 255ros2 service call /clear std_srvs/srv/EmptyTidy up: Ctrl+C in T3 and T1. q in T2.
Setup line in T1, T2, T3.
B.1 — T1: make a workspace
mkdir -p ~/ros2_ws/srccd ~/ros2_ws/srcB.2 — T1: create a package, then copy ours
ros2 pkg create --build-type ament_python --license MIT demo_pkg --dependencies rclpy std_msgsls demo_pkgcp -r ~/ROS2_Tutorial/4_workspace/tutorial_pkg ~/ros2_ws/src/Open package.xml and setup.py in ~/ros2_ws/src/tutorial_pkg.
B.3 — T1: build
cd ~/ros2_wscolcon build --symlink-installExpected: Summary: 2 packages finished. Then in every terminal:
source ~/ros2_ws/install/setup.bashB.4 — publisher and subscriber
T1:
ros2 run tutorial_pkg talkerT2:
ros2 run tutorial_pkg listenerT3:
ros2 topic listros2 topic echo /chatterCtrl+C in T1, T2, T3.
B.5 — T1: launch both at once
ros2 launch tutorial_pkg talker_listener.launch.pyCtrl+C to stop.
B.6 — T1: turtlesim and the square, one command
ros2 launch tutorial_pkg turtle_square.launch.pyExercise
- Copy
talker.pytocounter.py; publish anInt32on/count. Add'counter = tutorial_pkg.counter:main'toentry_pointsinsetup.py. colcon build --symlink-install, re-source,ros2 run tutorial_pkg counter,ros2 topic echo /count.- Add it to
talker_listener.launch.pyand launch all three.
Stuck? Check in this order: is the other program running (ros2 node list)? Is the topic name exact (ros2 topic list)? Did this terminal run the setup line? Then raise your hand.