Homework 2
Logistics
- Create a git repository using the link provided in Canvas.
- Your homework will be submitted via this git repository.
- I will grade whatever is on the
main
branch when I clone the repository. - Guidelines provides important information about what
you should turn-in, including information about your
README.md
file. - All work on both parts of this problem should be done in a single
git
repository containing a single ROS package calledhomework2
- In general, you should refer to
ROS
entities using relative names from within your nodes (i.e., no leading "/")
Robots
The turtlebots are in A275. See Turtlebot3 Setup for more information.
Part I: Turtle Trajectories
Overview
The goal of this assignment is to make a turtlebot follow a figure-8 trajectory.
- The turtlebot will begin in the "paused" state and believe that it's configuration is \((x, y, \theta) = (0,0,0)\), relative to it's odometry (
odom
) frame. - Upon calling the
/resume
service, the turtlebot will enter the "moving" state and begin following the trajectory - Upon calling the
/pause
service, the turtlebot will pause its motion, without resetting its position
The Kinematics
The turtlebot3 is a differential-drive robot that can be modeled as kinematic car (also known as a unicycle model). The control inputs for the robot are linear velocity \(v\) and the angular velocity \(\omega\), and result in motion according to the kinematic equations
\begin{align} \dot{x} &= v \cos \theta \\ \dot{y} &= v \sin \theta \\ \dot{\theta} &= \omega. \end{align}- The x axis points in the forward direction of the robot and the angle \(\theta\) is relative to the world x-axis.
Your goal is to make the turtlebot follow a figure-eight trajectory:
\begin{align} x_d(t) &= \frac{W}{2} \sin \frac{2 \pi t}{T} \\ y_d(t) &= \frac{H}{2} \sin \frac{4 \pi t}{T} \end{align}where \(t\) is time, \(T\) is the time to finish one figure-eight, \(W\) is the width of the figure-eight , and \(H\) is the height of the figure-eight.
What to implement
This homework is presented in the form of a specification, rather than a step-by-step guide. The Hints section provides useful information for how to implement everything.
Trajectory Node
Create a node called trajectory
that
- Publishes a
cmd_vel (geometry_msgs/Twist)
message at a fixed frequency, causing the turtlebot to follow the desired open-loop trajectory- It is up to you to find an appropriate frequency, but 10 Hz is too slow and 1 kHz is faster than needed.
- Broadcasts:
- A static transform from
world
toodom
. - The
world
frame andodom
frame are co-located. When theturtlebot
is at (0, 0, 0) in the odom frame it is at \(0, 0, \theta_0\) in the world frame, where \(\theta_0\) is the initial angle of the robot on its figure-eight trajectory - Thus, when the turtlebot starts, it can initiate its odometry to
(0,0,0)
but always be facing the correct direction in the odometry frame
- A static transform from
- Services:
pause (type up to you)
Stop the turtle's motion, in a way that the trajectory can be resumedresume (type up to you)
Resume the turtle's motion along the trajectory
- Parameters:
width
: The width of the figure eightheight
: The height of the figure eightperiod
: The amount of time it takes to complete the figure eight~pub_freq
: The frequency at which to publish the messages. This is a private parameter.- Pick parameters that fit your location and the turtlebot's capabilities.
Simulated Odom Node
The purpose of this node is to allow you to use the turtlesim node instead of a real robot while refining your trajectory
Create a node called simodom
that
- Subscribes to a
turtlesim/Pose
message - Publishes the corresponding
nav_msgs/Odometry
message- The header frame is odom
- The child frame is base_footprint
- You can leave covariance information as zeros
- Broadcasts the corresponding transform between
odom
andbase_footprint
- When the robot first is turned on, it assumes that it is located at (0,0,0) in the
/odom
frame. - The
/base_footprint
frame is the top-level frame of the turtlebot3 (e.g., all the turtlebot's frames are children of/base_footprint
. Therefore, the transform between/odom
and/base_footprint
corresponds to where the robot thinks it is, relative to where it was when it first was turned on.
Python Package
A python package called homework2
and located under src/homework2
- The ROS package is
$ws/src/homework2
and the python package is$ws/src/homework2/src/homework2
, where$ws
is your workspace - This package should contain code that performs calculations related to the trajectory and control but does
not interact with the hardware
- Generally, code that performs calculations is much easier to test and can be used in a wide variety of situations so it makes sense to split it from the node code.
- This code should not use any
rospy
functions at all
- At a minimum, the package should provide functions to
- Compute the trajectory and its derivatives at a given time
- Compute the proper control inputs at a given time
- The
trajectory
node shall import thehomework2
package to do its calculations and use the results to control the robot
Tests
Unit tests for your python package using rossunit
- You should be able to run the tests from
catkin_make run_tests
- Test each of the following computations at \(t = 0\) and at least one other time \(t\): \(x_d(t)\), \(y_d(t)\), \(\dot{x}_d(t)\), \(\dot{y}_d(t)\), \(\ddot{x}_d(t)\), \(\ddot{y}_d(t)\), \(v(t)\), \(\omega(t)\).
- For this assignment, it is only necessary to test the functions/methods in the python package, not your nodes.
Configuration
- A
config
directory containing- A
trajectory.yaml
file containing the trajectory parameters (the publishing frequency is specified directly in the launchfile) - A
turtle_view.rviz
file containing yourrviz
setup.
- A
- A launchfile in the
launch/
directory calledfigure_eight.launch
that- Loads the
width
,height
,period
parameters fromtrajectory.yaml
- Starts the
trajectory
node, including setting thepub_freq
parameter Launches
rviz
- When launching
rviz
it loadsturtle_view.rviz
to get therviz
settings - The fixed frame should be
/world
- The odometry should be displayed (using an
rviz
odometry visualizer) - All the
tf
frames should be hidden except for/world
. However, thetf
tree visualizer should be added to the scene (i.e., the left panel of rviz) - The robot model should be shown
- Provides an option called
mode
- When
mode
issim
(the default) nodes are launched to command the trajectory in turtlesim, but also to have the robot model inrviz
moving as if the turtle were the real robot. - You will need to publish
joint_states
for the wheels for the robot to show up inrviz
. These joint states can just be 0, you do not need to simulate the wheels actually turning. - When
mode
isreal
, the robot with namerobot_name
runs the trajectory in real life.turtlesim_node
should not be started
- When
- When launching
- Loads the
Documentation
- Documentation showing how you computed the control inputs from the trajectory is required. You should link/refer to the documentation in your README.md.
A few valid methods of showing the computations are:
- Python code using
sympy
to perform the calculations - A Mathematica Notebook
- A picture or scan of handwritten calculations
- A pdf of calculations written in
latex
- Directly embedded in your README.
- Python code using
- The functions and classes you write for the python package should be commented with docstrings as per the Guidelines
- The nodes you write should be commented with docstrings as per the Guidelines
- Write a
README.md
as per the Guidelines- Assume the user already knows how to use ROS and our turtlebots so no need to explain the basic setup.
- Videos:
- A screencast of the robot in
rviz
- A video of the robot completing a figure eight in turtlesim.
- A video of the robot completing a figure eight in real life.
- Videos can be a link to a sharing site in your README or as a
gif
file.
- A screencast of the robot in
Hints
- Even if you have a robot, it makes sense to test this in the turtlesim if you like
- Use a timer running at a specified frequency to publish the velocity commands to the turtle.
- Since the trajectory is continuous but you are sending discrete commands, you need the timer to publish fast enough relative to the turtle's speed to follow the trajectory effectively.
- ROS offers some standard services in the std_srvs package, which can be useful for your pause/resume services.
- The Turtlebot3 bringup guide provides a guide for how to start running the actual robot
- You will need to manually run a launchfile on the
turtlebot3
, but you should includeturtlebot3_bringup/turtlebot3_remote.launch
in your own launchfile so it does not need to be called separately (when running on the real turtlebot). - It is possible to make everything work from a single launchfile, but you will have to write it yourself and it is not necessary for this assignment.
- You will need to manually run a launchfile on the
- You can also look at the contents of the
turtlebot3
launchfiles to get a feel for what's going on.- In particular, how is the
/robot_description
parameter loaded?
- In particular, how is the
- A useful structure is to have two classes, although you need not follow this structure:
FigureEight
is responsible for all calculations related toFigureEight
trajectories:- It should be able to provide the trajectory coordinates and derivatives at any given time for a set of parameters.
Kinematics
is responsible for the calculations related to the kinematics of the turltebot- This class can use
FigureEight
to compute the appropriate control inputs to follow the trajectory - In general, multiple trajectory shapes could be supported by substituting
FigureEight
with another class for a different trajectory- You need not support multiple trajectory shapes, but this may be a helpful way of thinking about how to organize the code.
- To accomplish this task, you will take advantage of the differential flattness of the system.
- The overall goal is to solve for the control inputs \(v\) and \(\omega\) that will result in the desired trajectory
- Because the system is differentially flat, we can find these expressions without integrating the differential equations describing the kinematics of the robot
- See my Practical Differential Flatness Guide
- Use the
-d
argument torviz
to load a.rviz
configuration file that you create to set up the fixed frame properly and make. - Remember to use
$(find)$
when referencing files stored in packages.
Practical Differential Flatness Guide
- Solve the kinematic equations for \(\theta = f(\dot{x}, \dot{y})\), (thus \(\theta\) is some function \(f\) that depends only on \(\dot{x}\) and \(\dot{y}\).
- Hint: one way to solve this is to look at \(\frac{\dot{y}}{\dot{x}}\)
- Find \(\dot{\theta} = f'(\dot{x}, \dot{y})\)
- Notice that this derivative depends only on \(\dot{x}\), \(\ddot{x}\), \(\dot{y}\), and \(\ddot{y}\).
- Notice that \(\omega = \dot{\theta}\), thus given a reference trajectory and its derivatives you can directly find the steering angle to stay on a given trajectory.
- In other words, one of the controls (steering angle) is now expressed directly as a function of the trajectory.
- Next solve for \(v = g(\dot{x}, \dot{y}, \ddot{x}, \ddot{y})\)
- In other words, solve for the velocity control in terms of the trajectory and its derivatives
- What is \(\dot{x}^2 + \dot{y}^2\) (you can compute this from the kinematic equations)?
- You have now solved for the controls \(w\) and \(v\) directly in terms of the vehicle's (\(x\), \(y\)) position and derivatives.
- The robot follows a trajectory when \(x(t) = x_d(t)\) and \(y(t) = y_d(t)\). You can achieve this behavior by
- Taking the necessary derivatives of \(x_d(t)\) and $y_d(t)
- Substituting these derivatives into your expressions for \(v\) and \(\omega\)
- At least in theory, the robot will follow the trajectory. In practice there will be small errors from wheel slipping, dynamics, and control timing.
Part II: Xacro ARM
In this section you will be creating a Xacro URDF for the robot arm shown below and visualizing it in rviz, and then making it's end-effector follow a trajectory.
Specifications
These specifications are not necessarily ordered by what you should do first. Pay close attention to the desired orientation of the arm and its reference frames.
The Xacro File
- Create a xacro urdf file called
twoarm.urdf.xacro
for the robot arm.- It should read and use lengths \(L_1\) and \(L_2\) and radii \(R_1\) and \(R_2\) from a file
config/arm.yaml
.- Be sure to choose lengths such that \(L_1 \neq L_2\).
- See Xacro Yaml Support
- When \(\theta_1 = 0\) and \(\theta_2 = 0\), the robot should be completely horizontal, with the coordinates of the end-effector (i.e., the tip of link 2) relative to the fixed frame being \((L_1 + L_2, 0, 0)\).
- Each link should be a different color when displayed in
rviz
. - Each link should be visualized as a cylinder, whose height is the length of that link.
- The collision volume for each link is the same as the visualization and should be specified in the xacro file
- Each link should have the proper inertia, assuming a mass of 1kg.
- When \(L\) or \(R\) changes, the inertia should change accordingly
- There are no joint limits (each joint is continuous).
- There should be a frame at the location of the robot's end-effector.
- The arm should be in the \(xy\) plane of your
<Fixed Frame>
.- In other words the robot is rotating in the plane of the "ground" since the z axis points up in rviz.
- In
rviz
the x axis is red, y axis is green, and z axis is blue
- Each joint frame should have the \(z\) axis pointing up (i.e., the \(z\) axis is the axis of rotation) and the \(x\) axis pointing along the length of the arm.
- It should read and use lengths \(L_1\) and \(L_2\) and radii \(R_1\) and \(R_2\) from a file
- A launchfile called
arm_basics.launch
that starts- A
robot_state_publisher
- The
joint_state_publisher_gui
(optionally, if the argumentuse_jsp
is set totrue
, by default that argument isfalse
) - Rviz, which loads a configuration file that sets up the fixed frame and the rest of the visualization properly
- Remember to use
$(find)$
when referencing files stored in packages. - Document how to use your launchfile in the
README.md
- A
A node called
\begin{align} x(t) &= 0.9\cos\frac{2\pi t}{T} \sqrt{(L_1+L_2)^2 - h^2} \\ y(t) &= \frac{2}{3}(L_1 + L_2) \end{align}arm_traj
that causes the end-effector to track the following trajectory:- Let \(h=\frac{2}{3}(L_1 + L_2)\) so that the trajectory is always achievable.
- The trajectory should result in the end-effector moving back and forth in a straight line with a constant \(y\) value
- The node should read the parameters \(L_1\), \(L_2\), and \(T\) from the parameter server (but not \(h\), since it is derived).
- Unlike with the turtle trajectory, here you can specify joint angles directly by publishing
sensor_msg/JointState
messages onjoint_state
.- Therefore, you only need the inverse kinematics: no derivatives.
- You can derive the inverse kinematics with some basic geometry, look them up (e.g., in Lynch, Park, Modern Robotics Chapter 6, cite your source),
or use
tf2
to compute them for you. - The locations on the trajectory should be published at a fixed frequency that results in the motion of the arm appearing smooth
- Each joint state message has a header of type
std_msgs.msgs.Header
- set the stamp member to the current time before you send the message
- seq is set automatically by the publisher
- frame_id is used by
/tf
so it can also be left blank for JointState messages
- A node called
arm_marker
that draws markers inrviz
at the end-effector position.- One cycle should take \(T\) seconds: you should read \(T\) from the parameter server so you use the same value as in your trajectory node
- The markers should disappear after \(\frac{T}{5}\) seconds
- The markers should be published at a fixed rate
- You likely will need to publish these markers at a significantly slower rate than the trajectory updates because they can be slow to process.
- You can use any shape you want
- The color and shape should change if the
x
coordinate is positive or negative - Determine the position of the end-effector using a
tf
listener, and trigger the change based on the result
- A launchfile called
arm_mark.launch
that lets you run all the nodes you've created and visualize the results. It should:- Use an
<include>
tag to loadarm_basics.launch
launchfile, launching rviz and therobot_state_publisher
and, either thejoint_state_publisher
(ifuse_jsp
is true) or your trajectory generation node (ifuse_jsp
is false). - The launchfile should load \(L_1\), \(L_2\) and \(T\) into the parameter server from a yaml file called
arm.yaml
- Hint: you may wish to write this launchfile incrementally, adding new
<node>
tasks for each node you create.
- Use an
- An entry in your
README.md
about what each launchfile and node is called and what it does. Comments in the docstring of each node should describe the ROS api for each node (publishers, subscribers, services, etc.).