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
DiffDriveclass inturtlelib, indiff_drive.hppanddiff_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
DiffDriveclass should use functions/classes declared ingeometry2d.hppandse2d.hppas appropriate. - The
DiffDriveclass 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_drivefor 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.hpphas doxygen-style comments explaining how to use everything. - Be sure to mention
diff_drivein 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_descriptiondiff_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 thehumblebranch - This code resides in the
ws/src/nuturtlebot_msgsdirectory and contains message definitions for low-level turtlebot control - Use vcstool (install via apt as
python3-vcstool) to export a.reposfile fromws/src. vcs export > turtle.repos. Save theturtle.reposin the base directory of your git repository.- Other people can then download the
turtle.reposand use it to duplicate the contents of yourws/srcdirectory.
- 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
DiffDriveclass fromturtlelibto 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 toodomif 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
JointStatemessage it should update its internal odometry state. - The node publishes a nav_msgs/msg/Odometry message on the
odomtopic:- 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_idframe - The velocity will be specified as the body velocity of the robot in the
body_idframe
- Broadcast the transform between
odom_idand thebody_idon/tfusing atf2broadcaster - The
odometrynode 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
circlethat publishescmd_velcommands to cause the robot to drive in a circle of a specified radius at a specified speed. - The node offers a
controlservice 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
reverseservice (std_srvs/Empty) that reverses the direction of the robot along the arc. - The node offers a
stopservice (std_srvs/Empty)that stops the robot - When not
stopped, Thecmd_velcommands should be published at a fixed rate, determined by thefrequencyparameter, which defaults to100Hz.- When
stopped, Publish a singlecmd_velcommand 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_testthat teststurtle_controlROS API- Write a test that verifies that
cmd_velcommands with pure translation result in the appropriatewheel_cmdbeing published. - Write a test that verifies that
cmd_velcommands with pure rotation in the appropriatewheel_cmd - Write a test that verifies that encoder data on
sensorsis converted tojoint_statesproperly - Write a test launchfile file called
test/turtle_control_test.launch.xmlthat runsturtle_control_testandturtle_control
- Write a test that verifies that
- Write a test node called
turtle_odom_test_nodeto test theodemetryROS API- Write one test case for the
initial_poseservice - Write one test case that uses a
tf2_listenerto verify that a transform fromodomto abase_footprintis 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.xmlthat runs ajoint_state_publisheras well as theturtle_odom_test_node - Because the
odometrynode was implemented in terms ofDiffDrivewe 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.xmlthat verifies the frequency that thecirclepublishescmd_velcommands.
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_srccan becircleto start thecirclenodeteleopto start theturtlebot3_teleop teleop_twist_keyboardnonestart nothing, another node not started by this launchfile will publish thecmd_velcommands
- The argument
robotcan benusimto startnusimsimulator, theodometrynode, and theturtle_controlnode. This is the default. (See Task C)localhostTo run the nodes directly from theturtlebot3(See Task F for additional nodes that need to run)- With this option, the
odometryandturtle_controlnodes should run on the turtlebot, as well as thenumsr_turtlebotnode.
- With this option, the
none: Don't launch any additional nodes
- The argument
use_rviz, if true, launchesrvizwith a configuration that enables seeing the robot model,tfframes, and the odometry.use_rvizmust befalsewhen used with therobot:=localhostoption
- The launchfile should use a
tf2_ros static_transform_publisherto publish an identity transform between thenusim/worldand theodomframe - The
odometrynode should publish a transform betweenodomandblue/base_footprint. rvizshould always display theblueturtlervizshould also display theredturtle, ifrobot == nusim
Hint:
- The syntax for checking conditions in
ros2xml 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
evalexpresion 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 argumentargnameis 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
hostto 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
nusimnode subscribes tored/wheel_cmd(nuturtlebot_msgs/msg/WheelCommands) to receive motion commands for the turtlebot - Each received
wheel_cmdcommand sets the wheel velocities of the robot, until the nextwheel_cmdcommand is received.- The
wheel_cmdmessages 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
nusimshould (in addition to what it already does)- Update the wheel positions and publish them on
red/sensor_dataas anuturtlebot_msgs/SensorDatamessage.- 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
DiffDriveclass 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~/.bashrcon 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@leonardoandsourcethe 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 listfrom your laptop to see topics- Try
echoingthe 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
aarch64binaries 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
rsyncto place the generated binaries on theturtlebot3 - Use
sshto 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_keyboardto 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