| Gazebo plugin | ROS 2 node | |
|---|---|---|
| what it is | a library inside the simulator | its own program |
| when it runs | every physics step | on its own schedule, when messages arrive |
| what it can reach | the world itself | only messages |
| use it when | you must change physics: motors, forces, sensors | you write logic: perception, planning, control |
| examples | the drive, the motors, the odometry publisher | your own node · teleop_twist_keyboard · Nav2 |
| in this course | you reuse them | you write them |
The model has no motor. You give it one
<include>
<name>husky</name>
<uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/COSTAR_HUSKY_SENSOR_CONFIG_1</uri>
<plugin filename="gz-sim-diff-drive-system" name="gz::sim::systems::DiffDrive">
<left_joint>front_left_wheel_joint</left_joint> <!-- the names from model.sdf -->
<left_joint>rear_left_wheel_joint</left_joint> <!-- listed twice: four-wheel drive -->
<right_joint>front_right_wheel_joint</right_joint>
<right_joint>rear_right_wheel_joint</right_joint>
<wheel_separation>0.5708</wheel_separation> <!-- metres, left wheel to right wheel -->
<wheel_radius>0.1651</wheel_radius> <!-- metres -->
</plugin>
</include>
<include> — you are not editing the model, you are adding to your own worldmodel.sdf. Leave them out and you get 1.0 m and 0.2 m instead: the robot still drives, and its odometry lies to you all semesterTwist$ ros2 interface show geometry_msgs/msg/Twist
Vector3 linear # a straight-line speed, in metres per second
float64 x # forward, positive ahead
float64 y # sideways, positive to the left
float64 z # up, positive upward
Vector3 angular # a turning speed, in radians per second
float64 x # roll — tipping left and right
float64 y # pitch — nose up and down
float64 z # yaw — turning left, positive anticlockwise
TwistStamped is the version that adds thoselinear.x and angular.z. It cannot fly, and it cannot slide sideways github.com/ros2/common_interfaces/blob/jazzy/geometry_msgs/msg/Twist.msg
Twist, and works out how fast each side of the robot must spin github.com/gazebosim/gz-sim/blob/gz-sim8/src/systems/diff_drive/DiffDrive.cc
$ grep -c "<plugin" model.sdf # the X3
0
$ ros2 interface show actuator_msgs/msg/Actuators
std_msgs/Header header # when this command was issued
float64[] position # where each actuator should sit — rad for something that turns,
# m for something that slides. Empty here: propellers do not hold a position
float64[] velocity # how fast each actuator runs — rad/s for a propeller.
# THIS is the one we use: one number per rotor, in the order declared
float64[] normalized # anything that is not a position or a speed, scaled to −1 … 1
# (a servo at half travel, a throttle at 70%). Empty here
$ ros2 topic pub /x3/command/motor_speed actuator_msgs/msg/Actuators \
'{velocity: [600, 600, 600, 600]}'
velocity| Husky's drive | the drone's motors | |
|---|---|---|
| message it accepts | geometry_msgs/msg/Twist |
actuator_msgs/msg/Actuators |
| what the numbers mean | how the robot should move | how fast each propeller spins |
| units | m/s and rad/s | rad/s, one per rotor |
| how many numbers | 6, of which it uses 2 | 4 — one per rotor |
| who does the arithmetic | the plugin, for you | you do |
| how many plugins | one, for the whole robot | one per rotor |
| can you steer with it | yes | no — only up |
| Plugin | How many | Message it gets | What it does |
|---|---|---|---|
| motor model | one per rotor — four | Actuators — one spin speed |
turns that speed into a push upward and a twist about the shaft. Knows nothing about the aircraft |
| velocity control | one | Twist — an intention |
reads how the aircraft is tilted, works out what all four rotors must do about it, and writes those four speeds. Every step |
Twist as the Husky. Without it, you are spinning propellers by hand gazebosim.org/api/sim/8/classgz_1_1sim_1_1systems_1_1MulticopterVelocityControl.html
Across the boundary
# config/husky.yaml — our own file
- ros_topic_name: "/cmd_vel" # the short name we want in ROS 2
gz_topic_name: "/model/husky/cmd_vel" # the long name the simulator uses
ros_type_name: "geometry_msgs/msg/Twist"
gz_type_name: "gz.msgs.Twist"
direction: ROS_TO_GZ # commands go IN
- ros_topic_name: "/odom"
gz_topic_name: "/model/husky/odometry"
ros_type_name: "nav_msgs/msg/Odometry"
gz_type_name: "gz.msgs.Odometry"
direction: GZ_TO_ROS # measurements come OUT
/cmd_vel github.com/gazebosim/ros_gz/blob/jazzy/ros_gz_bridge/README.md
| Robot | ROS 2 topic | Type | Simulator topic | Direction |
|---|---|---|---|---|
| Husky | /cmd_vel |
geometry_msgs/msg/Twist |
/model/husky/cmd_vel |
in |
| Husky | /odom |
nav_msgs/msg/Odometry |
/model/husky/odometry |
out |
| drone | /cmd_vel |
geometry_msgs/msg/Twist |
/X3/gazebo/command/twist |
in |
| drone | /odom |
nav_msgs/msg/Odometry |
/model/x3/odometry |
out |
/X3/…, measurements come from /model/x3/…$ ros2 run ros_gz_bridge parameter_bridge --ros-args -p config_file:=config/husky.yaml
[INFO] Creating ROS->GZ Bridge: [/cmd_vel -> /model/husky/cmd_vel]
[INFO] Creating GZ->ROS Bridge: [/model/husky/odometry -> /odom]
$ ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.5}}'
$ ros2 topic echo /odom --field pose.pose.position
-r 10 repeats ten times a second. Without it you send one message — and one is enough: the drive keeps the last thing it was told, indefinitely$ ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{}' # all zeros
Where the robot thinks it is
Odometry is working out where you are by adding up how you have moved.
A word on names. This is wheel odometry — counted from the wheels. Visual odometry answers the same question from a camera, and lidar odometry from a lidar. Same question, different sensor, quite different maths. We use the wheels today.
nav_msgs/msg/Odometry, Field by Field| Field | Type | What it is |
|---|---|---|
header.stamp |
time | when this was true. In simulation this is simulation time, not the wall clock |
header.frame_id |
text | what the position is measured against — the place the robot started from |
child_frame_id |
text | which part is moving. The speed below is measured in this part's own axes |
pose.pose.position |
x, y, z metres | where it is |
pose.pose.orientation |
quaternion | which way it faces — four numbers rather than three angles, because three angles jam at the poles |
pose.covariance |
36 numbers | how sure it is about the position, as a 6×6 table |
twist.twist.linear |
x, y, z m/s | how fast it is moving right now, forward / sideways / up |
twist.twist.angular |
x, y, z rad/s | how fast it is turning right now |
twist.covariance |
36 numbers | how sure it is about the speed |
github.com/ros2/common_interfaces/blob/jazzy/nav_msgs/msg/Odometry.msg
$ ros2 topic echo /odom --field pose.covariance --once
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. … ]
geometry_msgs/msg/Pose is position and orientation, and nothing else. Use it when all you need to say is where something is| the drive plugin's own | a separate odometry plugin | |
|---|---|---|
| where it comes from | the drive already knows what it told the wheels, so it adds that up | it watches the model move and reports what it sees |
| what it costs you | nothing — it is already there | one more plugin in the world |
| flat or 3-D | flat: z is always 0 |
you choose 2 or 3 |
| when you need it | any wheeled robot with a drive | a drone — and anything whose motor reports nothing |
PostUpdate, so it cannot report anything, and something else has toLooking at what the robot says
docs.ros.org/en/jazzy/Tutorials/Intermediate/RViz/RViz-User-Guide/RViz-User-Guide.html ·
docs.clearpathrobotics.com/docs/ros/tutorials/rviz
| Display | Topic type | You will use it for |
|---|---|---|
| Odometry · Path | Odometry · Path |
where the robot has been — arrows, or a clean line |
| LaserScan | LaserScan |
the lidar, as a ring of points around the robot |
| PointCloud2 | PointCloud2 |
anything in 3-D: the depth camera, a 3-D lidar |
| Image · Camera | Image |
the camera picture, in a panel of its own |
| Map | OccupancyGrid |
the map, once something is building one |
| Marker · MarkerArray | Marker |
anything you want — arrows, lines, text your own node draws |
| RobotModel · TF | — | the robot's own body and its parts. Needs more than we have today |
ros2 topic echo, or plot it docs.ros.org/en/jazzy/Tutorials/Intermediate/RViz/RViz-User-Guide/RViz-User-Guide.html
teleop_twist_keyboardTwo hundred lines that drive anything
def getKey(settings):
tty.setraw(sys.stdin.fileno()) # raw mode: give me keys at once, don't wait for Enter
key = sys.stdin.read(1) # waits here until exactly one character arrives
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings) # put the terminal back
return key
github.com/ros2/teleop_twist_keyboard/blob/jazzy/teleop_twist_keyboard.py
moveBindings = { # key : (x, y, z, th)
'i': ( 1, 0, 0, 0), # forward
'j': ( 0, 0, 0, 1), # turn left
'l': ( 0, 0, 0, -1), # turn right
',': (-1, 0, 0, 0), # back
'J': ( 0, 1, 0, 0), # SHIFT: slide left without turning
't': ( 0, 0, 1, 0), # UP
'b': ( 0, 0,-1, 0), # DOWN
}
speedBindings = {'q': (1.1, 1.1), 'z': (.9, .9)} # everything 10% faster / slower
t and b go up and downt and b lines are about to matter github.com/ros2/teleop_twist_keyboard/blob/jazzy/teleop_twist_keyboard.py
pub = node.create_publisher(TwistMsg, 'cmd_vel', 10) # RELATIVE name — no leading slash
…
twist.linear.x = x * speed # the table entry, scaled
twist.angular.z = th * turn
pub.publish(twist)
…
finally:
pub.publish(TwistMsg()) # all zeros, on the way out
speed starts at 0.5 m/s and turn at 1.0 rad/s; q and z scale them as you goros2 param set will refuse them. Set them when you start the programfinally is the difference between letting go of the keyboard and watching your robot leave github.com/ros2/teleop_twist_keyboard/blob/jazzy/teleop_twist_keyboard.py
Twist means the same thing to every mobile robot in ROS 2cmd_vel is written with no leading slash, so it can be pointed anywhere without touching the fileActuators. Matching types is exactly how far this reaches$ ros2 run teleop_twist_keyboard teleop_twist_keyboard
$ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap cmd_vel:=/my/topic
/cmd_veli drives the Husky forward · t takes the drone up. Same program, same message, different robotYour first graded work
Take a standard robot off the shelf, and drive it from ROS 2. Any model in the catalogue; wheels or rotors, your choice.
<include>, plus whatever plugin that model needs to move/odom and reports where the robot isteleop_twist_keyboard, with no changes to it/odomYou are given a repository with the skeleton in it. Work in that.
| In the README | What goes in it |
|---|---|
| Run | the exact commands, from zero, on a machine that is not yours |
| See | the path: a screenshot from RViz and a plot drawn from /odom |
| Numbers | the distance you asked for, the distance you got, and the difference |
| Compare | driving from the command line against driving from the keyboard — a paragraph, not an essay |
notes.txt |
what broke, what you tried, what is still unclear. It cannot lower your mark |
my_fuel_lab/
├── package.xml setup.py setup.cfg resource/… # the usual Python-package wrapping
├── worlds/<name>.sdf # ground plane, light, <include>, the movement plugin
├── config/bridge.yaml # both directions, only the entries you use
├── <pkg>/odom_listener.py # ~30 lines: subscribe to /odom, print where you are
├── scripts/run.sh # ONE script: simulator + bridge + listener
├── README.md # Run · See · Numbers · Compare
└── notes.txt
run.sh does the same and needs nothing explained firstDeliberately not in the hand-in — Lab 1 must fit in an evening. Pick one for the top mark:
| Step up to | What it gets you |
|---|---|
| a mission node | ~20 lines that drive a fixed sequence, instead of you typing commands. Your first replacement of a stock tool — and it makes two students' runs comparable |
ros2 bag |
record the drive; replay it into RViz with the simulator switched off |
| a launch file | start the simulator, the bridge and your node with one command |
| a saved RViz layout | for when three clicks stop being enough |
| the drone, steered | add the brain plugin and fly it with the same keyboard program |
Nothing here is wasted: every row is a later lab, arriving early
Lecture 3 — Building the Robot
Describing a robot yourself · parts, joints and frames · the robot that finally appears in RViz