Homework 2
Introduction
You should look over all the tasks in the homework. They generally outline what you need to do, but do not necessarily correlate with the easiest order in which to complete them.
- For example, the last task I write is "write a launchfile to", but it can usually be easier to start writing the launchfile as you begin making your nodes so you can use them in the testing process.
- Refer to the Guidelines
Task B (C++ and Rigid Transforms)
Task B.8 (Vector Operations)
- Add the following operations to Vector2D (referring to Operator Overloading Rules)
operator+=
andoperator+
(vector addition)operator-=
andoperator-
(vector subtraction)operator*=
andoperator*
(multiplication by a scalar)- The scalar can be to either the right or the left of the vector
double dot(Vector2D, Vector2D)
(The dot product of two vectors)double magnitude(Vector2D)
(computer the magnitude of the vector)double angle(Vector2D,Vector2D)
(compute the angle between two vectors)
- Write unit tests for these functions to make sure that they work as expected.
Task B.9 (Integrate a Twist)
- Add a function
Transform2D integrate_twist(Twist2D)
toturtlelib
.- It should compute the transformation corresponding to a rigid body following a constant twist (in its original body frame) for one time-unit
- The ability to integrate for exactly one time-unit allows integrating for arbitrary lengths of time by scaling the twist.
- Include 3 unit tests for this function:
- One test should test pure translation
- One test should test pure rotation
- One test should test simultaneous translation and rotation
Task D (Kinematics)
Task D is related to the Kinematics of wheeled mobile robots, and is part of the turtlelib
package.
You will likely not be able to complete these tasks until all of Task B is complete.
Task D.1 (DiffDrive class)
- Create a
DiffDrive
class inturtlelib
, indiff_drive.hpp
anddiff_drive.cpp
. - This class (and any associated non-member functions) models the kinematics of a differential drive robot with a given wheel track and wheel radius.
- It handles all the mathematical calculations for the robot kinematics without being tied to a specific robot or control scheme.
- The class (and any associated non-member functions) should, at a minimum:
- Track the position of a robot's wheels \((\phi_r,\phi_l)\) and its configuration \(q = (x, y, \theta)\).
- (Forward Kinematics) Given new wheel positions \((\phi_r', \phi_l')\), update the configuration assuming that to get to the new position from the previous position:
- Both wheels started rotating simultaneously
- Both wheels stopped rotating simultaneously
- The wheels rotated at constant velocity
- The wheels achieved their velocity instantaneously
- The wheels did not slip
- The wheels rotated the shortest distance possible to get to their final configuration from the starting configuration
- (Inverse Kinematics) Compute the wheel velocities required to make the robot move at a given body twist.
- If the twist cannot be accomplished without the wheels slipping throw an
std::logic_error()
exception.
- If the twist cannot be accomplished without the wheels slipping throw an
- You may add other functionality to this class as needed/appropriate. These needs may arise in other tasks.
- The
DiffDrive
class should use functions/classes declared ingeometry2d.hpp
andse2d.hpp
as appropriate. - The
DiffDrive
class and associated functions should not use any ROS publishers, services, clients, subscribers, parameters, or other ROS packages.
Task D.2 (DiffDrive test)
- Write unit tests for
diff_drive
for the forward and inverse kinematics. Consider at least the following scenarios:- The robot drives forward (both forward and inverse)
- The robot executes a pure rotation (both forward and inverse)
- The robot follows the arc of a circle (both forward and inverse)
- An impossible-to-follow twist is provided.
Task D.3 (Documentation)
- Make sure everything in
diff_drive.hpp
has doxygen-style comments explaining how to use everything. - Be sure to mention
diff_drive
in theREADME.md
- You will likely need to perform some mathematical calculations (involving twists, transforms, and adjoints) to accomplish this task:
- Record your work in
doc/Kinematics.pdf
. It can be done in latex, or scanned hand-written notes, etc.) - Refer to your notes in your code, using comments to explain the origin of the mathematical expressions you use (labeling equations with numbers helps).
- Record your work in
Task E (Robot Control)
Here we develop some nodes that are useful in both simulation and on the real robot.
The nodes will be created in a new package called nuturtle_control
Task E.1 (Turtle interface)
Write a node called turtle_control
that enables control of the turtlebot via geometry_msgs/msg/Twist
messages on the cmd_vel
topic.
- The node relies on parameters defined in
nuturtle_description
diff_params.yaml
, as appropriate- If any of the needed parameters are not defined, it is an error and the node should print an error message and terminate.
- To print messages that are logged by ROS, use
ROS_ERROR_STREAM
,ROS_INFO_STREAM
,ROS_DEBUG_STREAM
, etc. (See Logging).
- It also relies on messages defined in
nuturtlebot_msgs
(https://github.com/ME495-Navigation/nuturtlebot_msgs.git).- Clone this repository into your source space
ws/src
, and checkout thehumble
branch - This code resides in the
ws/src/nuturtlebot_msgs
directory and contains message definitions for low-level turtlebot control - Use vcstool (install via apt as
python3-vcstool
) to export a.repos
file fromws/src
. vcs export > turtle.repos
. Save theturtle.repos
in the base directory of your git repository.- Other people can then download the
turtle.repos
and use it to duplicate the contents of yourws/src
directory.
- Clone this repository into your source space
- The node subscribes to
cmd_vel (geometry_msgs/msg/Twist)
and publisheswheel_cmd (nuturtlebot_msgs/msg/WheelCommands)
that will make the turtlebot3 follow the specified twist - The node subscribes to
sensor_data (nuturtlebot_msgs/msg/SensorData)
and publishesjoint_states (sensor_msgs/msg/JointState)
to provide the angle (in radians) and velocity (in rad/sec) - Use the
DiffDrive
class fromturtlelib
to implement this node
Task E.2 (Odometry Node)
In this task, you will write a node called odometry
that publishes odometry messages and the odometry transform.
- The node reads the following parameters
body_id
: The name of the body frame of the robot (e.g."base_footprint"
). If not specified, print an error and exit.odom_id
: The name of the odometry frame. Defaults toodom
if not specified.wheel_left
: The name of the left wheel joint. If not specified, print an error and exit.wheel_right
: The name of the right wheel joint. If not specified, print an error and exit.
- Every time the node receives a
JointState
message it should update its internal odometry state. - The node publishes a nav_msgs/msg/Odometry message on the
odom
topic:- Be sure to examine the message definition to understand the meaning of its fields
- You do not need to fill out any covariance information (all covariance can be set to zero)
- The pose should be specified in the
odom_id
frame - The velocity will be specified as the body velocity of the robot in the
body_id
frame
- Broadcast the transform between
odom_id
and thebody_id
on/tf
using atf2
broadcaster - The
odometry
node should offer a service calledinitial_pose
.- The service request provides the configuration of the robot.
- The location of the odometry is reset so that the robot thinks it is at the requested configuration
Task E.3 (Circle)
- Write a node called
circle
that publishescmd_vel
commands to cause the robot to drive in a circle of a specified radius at a specified speed. - The node offers a
control
service that takes the following parameters in the request:velocity
: The angular velocity, positive for counter-clockwise, negative is clockwiseradius
: the radius of the arc
- The node offers a
reverse
service (std_srvs/Empty
) that reverses the direction of the robot along the arc. - The node offers a
stop
service (std_srvs/Empty)
that stops the robot - When not
stopped
, Thecmd_vel
commands should be published at a fixed rate, determined by thefrequency
parameter, which defaults to100Hz
.- When
stopped
, Publish a singlecmd_vel
command of zero, then stop publishing
- When
Task E.4 (ROS API Testing)
In this task, we will test the ROS API of the turtle_control
and odometry nodes.
- For this task, we will use the catch_ros2 package, which enables C++ integration testing using the catch framework
- Write a test node called
turtle_control_test
that teststurtle_control
ROS API- Write a test that verifies that
cmd_vel
commands with pure translation result in the appropriatewheel_cmd
being published. - Write a test that verifies that
cmd_vel
commands with pure rotation in the appropriatewheel_cmd
- Write a test that verifies that encoder data on
sensors
is converted tojoint_states
properly - Write a test launchfile file called
test/turtle_control_test.launch.xml
that runsturtle_control_test
andturtle_control
- Write a test that verifies that
- Write a test node called
turtle_odom_test_node
to test theodemetry
ROS API- Write one test case for the
initial_pose
service - Write one test case that uses a
tf2_listener
to verify that a transform fromodom
to abase_footprint
is being published- The transform should be the identity transformation, the joint states will not change throughout the test
- Write a test launch file called
test/turtle_odom_test.launch.xml
that runs ajoint_state_publisher
as well as theturtle_odom_test_node
- Because the
odometry
node was implemented in terms ofDiffDrive
we will not test the the actual odometry calculations here
- Write one test case for the
- Write a test launch file called
test/turtle_circle_test.launch.xml
that verifies the frequency that thecircle
publishescmd_vel
commands.
Task E.5 (Launchfile)
Write a launchfile called start_robot.launch.xml
that allows the user to cmd_vel
commands to the turtlebot (real or simulated, remote or local), receive odometry,
and visualize everything in rviz
. The details are specified below. It is a good strategy to write pieces of this in parallel with your development
of the nodes it needs to launch (some of which are done in later tasks). For example, work on the simulator-specific tasks first, and add real robot functionality
later.
- Write a launchfile that can be called like
ros2 launch nuturtle_control start_robot.launch.xml cmd_src:=X robot:=Y use_rviz:=Z
- The launchfile does different actios depending on its arguments
- The argument
cmd_src
can becircle
to start thecircle
nodeteleop
to start theturtlebot3_teleop teleop_twist_keyboard
none
start nothing, another node not started by this launchfile will publish thecmd_vel
commands
- The argument
robot
can benusim
to startnusim
simulator, theodometry
node, and theturtle_control
node. This is the default. (See Task C)localhost
To run the nodes directly from theturtlebot3
(See Task F for additional nodes that need to run)- With this option, the
odometry
andturtle_control
nodes should run on the turtlebot, as well as thenumsr_turtlebot
node.
- With this option, the
none
: Don't launch any additional nodes
- The argument
use_rviz
, if true, launchesrviz
with a configuration that enables seeing the robot model,tf
frames, and the odometry.use_rviz
must befalse
when used with therobot:=localhost
option
- The launchfile should use a
tf2_ros static_transform_publisher
to publish an identity transform between thenusim/world
and theodom
frame - The
odometry
node should publish a transform betweenodom
andblue/base_footprint
. rviz
should always display theblue
turtlerviz
should also display thered
turtle, ifrobot == nusim
Hint:
- The syntax for checking conditions in
ros2
xml launch files is very particular - Although the documentation states that it works the same as in ROS 1, it actually does not
- The syntax for
$(eval)
, which evaluates a python expression, seems to be as follows:$(eval '<python_expr>')
, where there must not be a space between the end'
and)
.- To access an argument from the launchfile in the python expression use
$(var argname)
- This will literally insert the value, so if the the value is a string it will need
to be surrounded by
'
- Any quotes used by python inside the
eval
expresion must be escaped with a\
- This will literally insert the value, so if the the value is a string it will need
to be surrounded by
- Here is an example:
"$(eval '\'$(var argname)\' == \'myword\')
will check if the launchfile argumentargname
is equal to'myword'
Task E.6 (Remote Launch)
- This task is optional
- Unlike ROS 1, ROS 2 does not yet have a direct ability to launch nodes on remote machines using launchfiles
- Use launch_remote to write a launchfile called
remote_start.launch.xml
.- It takes an argument
host
to specify the remote machine - It then launches all nodes needed on both your computer and on the remote machine
- It takes an argument
- Feel free to submit pull requests to https://github.com/NU-MSR/launch_remote/tree/main
- You may submit pull requests relating to documentation or functionality.
- The best way of doing remote nodes in ROS 2 is unclear
Task C (The Simulator)
Task C.7 (Turtle Drive)
The nusim
node already loads and displays a robot in rviz
. The goal of this task is to allow that robot to move, according to simulated kinematics,
but through the same publisher/subscriber interface that an actual turtlebot uses.
- The
nusim
node subscribes tored/wheel_cmd
(nuturtlebot_msgs/msg/WheelCommands
) to receive motion commands for the turtlebot - Each received
wheel_cmd
command sets the wheel velocities of the robot, until the nextwheel_cmd
command is received.- The
wheel_cmd
messages are integer values between -265 and 265 and are proportional to the maximum rotational velocity of the motor (see Specifications and A.8).
- The
- On each simulation run, the
nusim
should (in addition to what it already does)- Update the wheel positions and publish them on
red/sensor_data
as anuturtlebot_msgs/SensorData
message.- For the time-being, you can ignore all the fields in this message other than the wheel positions and time stamp.
- Use the forward kinematics from the
DiffDrive
class to update the position of the actual (red) robot and publish the appropriate frame (assume no slipping)
- Update the wheel positions and publish them on
- After this functionality is implemented in the simulator, you should be able to drive the robot around using the teleop twist keyboard.
- The Blue and Red robots will be overlayed, because we have not yet added noise or slipping to our simulation model and therefore the odometry is perfect.
Task Group F (The Real Robot)
This task is about implementing some basic turtlebot functionality and getting familiar with the hardware.
Task F.1 (Turtlebot Setup)
- Follow the Turtlebot3 Setup instructions to get your computer setup to use one of the turtlebots.
- Add
export ROS_DOMAIN_ID <YourID>
to the~/.bashrc
on your computer.- The
<YourID>
is a number from 1 to 64 that is your unique id for the class
- The
- Running code on the turtlebot: (for example on
leonardo
)ssh -oSendEnv=ROS_DOMAIN_ID msr@leonardo
andsource
the install space.- You can now run launchfiles on the turtlebot as usual.
- The node that starts the low-level turtlebot code is run with
ros2 run numsr_turtlebot numsr_turtlebot
- You should also be able to
ros2 topic list
from your laptop to see topics- Try
echoing
the sensor data - Not all fields in the sensor data are currently filled in. The ones you need are basically the encoder values.
- Try
Task F.2 (Cross-compilation)
- Compiling code on the turtlebot itself is rather slow, so we will use cross-compilation
- Cross-compilation is when you compile code on one platform that runs on another
- For most of our tasks, we will use a docker container that has all the necessary tools and libraries
for compiling
aarch64
binaries for the turtlebot on the host system.- This will let us compile on our own computers, which can greatly speed up the compilation process
- Follow the Cross compilation instructions to setup docker and the cross-compiling system.
- Cross-compile your code on your machine
- Use
rsync
to place the generated binaries on theturtlebot3
- Use
ssh
to run the required launchfiles on the turtlebot3
Task F.5 (Physical Testing)
- Take a video and screencast of the following experiment and embed it in the github
README.md
- Credit will only be given for videos that play properly when embedded in the github
README.md
- Do not place the videos in your git repository: this will make the repository too large.
- Credit will only be given for videos that play properly when embedded in the github
- Drive the robot in a circle, clockwise and counter clockwise several times, stopping when the turtlebot is its initial configuration.
- When you get close to stopping, you can use the
teleop_keyboard
to try to get the robot as close to its initial configuration as you can. - Record the final location of the turtlebot according to odometry in the
README.md
. - The distance from zero is a measure of the error in the odometry
- When you get close to stopping, you can use the