UP | HOME

Homework 2

Logistics

  • The Guidelines provide important information about coding standards, expectations, and logistical considerations for this assignment.
  • I recommend reading the guidelines both before starting the assignment and prior to your final submission:
  • When a launch file is listed as filename.launch.{py,xml} implement both an XML and python version.
  • All data files that are installed should be installed to the appropriate sub-directory of the package's share directory (e.g., config/myfile.yaml should be installed to $(find-pkg-share mypackage)/config/myfile.yaml)
  • For this assignment, do not read from yaml files directly in your nodes.
    • Instead the appropriate values should be loaded into the node as parameters at launch time
    • It is impermissible to import yaml in your python code, for example.
  • All python launch files should be written in a declarative style.

Overview

We will simulate a robot in ROS and integrate it into various standard ROS systems (e.g., tf, joint_states, etc.). The robot will be used to catch a brick that is dropped from the sky.

The repository is called homework2 and there will be two packages within the repository

  1. brickbot_interfaces - custom interfaces that are created for this project
  2. brickbot an ament_python package containing everything else

Hints

Here are some general hints for this assignment:

  1. Familiarize yourself with the overall problem by reading the homework description.
  2. Work on the urdf.xacro file to build up the robot and the launchfile to visualize the robot simultaneously
    • It is especially useful to be able to view and manipulate the robot when debugging .urdf.xacro files and the launchfile makes this process much simpler.
  3. Slowly introduce node functionality over time.
  4. Write launchfiles early in the process. Use these launchfiles to make testing nodes easier.

Frames

The following coordinate frames will be used:

  1. world. This is the fixed frame, representing the location of entities in the world
    • All other frames are descendants of world
  2. brick. This frame is the location at the center of the brick's bottom surface
  3. base_link. This frame is the base location of the robot
    • The base_link has other child frames, as defined by the robot's urdf file
  4. odom frame: the robot's position based on its odometry is relative to the odom frame.
    • The position of the odom frame relative to the world represents where the robot started

The Robot

Overview

  1. The robot is modeled as a unicycle robot that "self balances" on a caster. It consists of
    • A base_link that is the main location of the robot and is represented by a cube.
    • A platform is a flat square
      • The base of the platform should be parallel to the ground in the zero position, but should be able to rotate so that the brick can be dumped.
      • The platform should be a fixed vertical distance above the base_link, so that it may rotate fully without colliding with the base link.
      • A fixed cylindrical link called hinge should connect the base_link to the platform
      • The platform rotates about it's +y axis via a joint called alpha
    • A stem is a cylindrical link that turns about the vertical z-axis and is below the base_link, connected by a joint named theta
    • A wheel is connected to the stem by a joint named phi and can roll as the robot moves
    • Each link should be a different color.
  2. The robot has a maximum translational speed but infinite rotational speed, making it effectively omni-directional
    • To move in a given direction, the stem points in that direction and then the robot translates (while the wheel rolls).
  3. The .urdf.xacro file for the robot should be in urdf/robot.urdf.xacro
  4. The visual and collision components can be the same. Inertia can be omitted for this homework.
    • Use xacro features to avoid redundancy when defining the visual and collision components.
    • Any xacro macros used should be defined in urdf/macros.xacro and included in the main xacro file.
  5. The xacro file should take in an argument for a config file, which defaults to robot.yaml

The image below is illustrative: however, the platform is a square rather than a cylinder. hw2_robot.png

Configuration

  1. A configuration file called config/robot.yaml should have the following parameters
    • The platform_height of the robot's platform above the ground
    • The platform_length is the length of the platform's side
    • The wheel_radius of the robot's wheel
    • The max_velocity, maximum translational velocity of the robot
    • The default values of these configuration items can be chosen by you.
  2. The .urdf.xacro files should use config/robot.yaml to set its dimensions appropriately
    • Other dimensions that are not specified should be specified as constants within the urdf.xacro file.
  3. Changing config/robot.yaml should be the only change needed to change these dimensions of the robot.
  4. Also create a configuration file called config/big_robot.yaml with different parameter values.

Visualizing the Robot

  1. Write a launchfile called launch/show_robot.launch.{py,xml} that displays the robot (as defined by robot.urdf.xacro) in rviz
    • The launchfile argument use_jsp controls the behavior of the joint_state_publisher
      • If use_jsp is gui then the joint_state_publisher_gui is used to publish joint states (the default)
      • If use_jsp is jsp then the joint_state_publisher is used to publish joint states
      • If use_jsp is none then no nodes that publish joint_states are started by the launchfile
      • Document the arguments so that the documentation shows up when using ros2 launch -s
    • The launchfile argument rviz_config contains the name of the rviz configuration file relative to the project's share directory.
    • The launchfile argument robot_config contains the name of the robot configuration file relative to the config directory. It defaults to robot.yaml
    • The launchfile argument use_rviz determines whether rviz is started
  2. The launchfile starts any other nodes that may be necessary to display the URDF in rviz

Physics

  1. A python module called physics.py should contain ROS-independent code to keep track of the physics of the environment.
    • It must not import any ROS python modules
  2. It should provide classes with the following definition.

    class Brick:
        '''Keep track of the physics of the Brick.'''
    
        def __init__(self, location, gravity, side_length, dt):
            '''
            Initialize the brick.
    
            Args:
            location - The (x,y,z) location of the brick. It is axis aligned.
            gravity - the acceleration due to gravity in m/s^2
            side_length - the side_length of the platform
            dt - timestep in seconds of the physics simulation
            '''
            pass
    
        @property
        def position(self):
            '''
            Get the brick's position.
    
            Return:
                (x,y,z) location of the brick
            '''
            pass
    
        @position.setter
        def position(self, pos):
            '''
            Set the brick's position.
    
            Args:
               pos - the (x,y,z) location of the brick
            '''
            pass
    
        def drop_gravity(self):
            '''
            Update the brick's location by having it fall in gravity for one timestep.
            '''
            pass
    
        def slide_ramp(self, radians):
            '''
            Update the brick's location as it slides on a frictionless ramp for one timestep.
    
            Args:
               radians - the angle of the ramp
            '''
    
    class Robot:
        def __init__(self, config, dt):
            '''
            Initialize the Robot.
    
            Args:
            config - The (x,y,alpha) position of the robot + tilt of the platform.
            dt - timestep in seconds of the physics simulation
    
            '''
            pass
    
        @property
        def configuration(self):
            '''
            Get the robot's configuration.
    
            Return:
                (x,y,alpha) configuration of the robot
            '''
            pass
    
    
        def drive(self, velocity):
            '''
            Update the robot's position after driving for one timestep.
    
            Args:
               velocity - (dx/dt, dy/dt) relative to the robot's body
            '''
            pass
    
        def tilt(self, alpha):
            '''
            Tilt the platform to the specified angle
    
            Args:
               alpha - the angle of the platform relative to the horizontal.
            '''
            pass
    

Simulation Node

Overview

The simulation node acts as fake hardware for this assignment. It manages and has full knowledge of the brick and the robot.

  1. Robot and brick state are updated by the physics module
  2. The simulation node is implemented in simulation.py
  3. Parameters for the simulation node are:
    • width, length The dimensions of the arena
    • x0, y0 - The initial position of the robot
    • gravity - Gravitational acceleration
    • timestep - Simulation timestep
  4. Parameters should be stored in config/simulation.yaml and loaded via launchfile
  5. Many of the parameters in config/robot.yaml are also needed and should be loaded.

Simulation Loop

  • The simulation is driven by a single timer with a period of timestep
  • Each tick of the timer corresponds to a single simulation step
  • All publishing and broadcasting should be done from the timer callback

The Arena

  1. Drawing walls on the arena boundaries using markers:
    • These should be published as a visualization_msgs/msg/MarkerArray on the visualization_marker_array topic in the arena marker namespace
  2. Broadcasting the brick frame at the location of the brick, and a marker to show its location in rviz
    • The brick marker is published on the visualization_marker topic in the arena marker namespace
    • The brick should of the CUBE type.
  3. Offer a place service (of custom type brickbot_interfaces/srv/Place) that moves the brick to a specified position
    • The service has a request type of geometry_msgs/msg/Point with a member called position and an empty response
    • Only the x,y,z coordinates are moved, the brick maintains a fixed orientation relative to the world
  4. Broadcast a static transform from world to odom based on the initial position of the robot.

Brick Dynamics

  1. Offer a service called drop (of type std_srvs/srv/Empty) that causes the brick to fall in gravity according to the specified rules:
    • After the service responds, the brick starts falling in gravity (using the specified gravity acceleration).
    • If the brick hits the platform or the ground it stops falling.
    • If the brick hits the platform it should move with the platform
      • Your code can assume that the platform is at 0 degrees when the brick is dropped and does not need to handle other cases.
  2. The brick slides on the platform based on gravity and the platform angle, with zero friction
    • When the brick is completely off the platform it freezes and floats in space
    • By construction the sides of the platform and of the brick are always aligned.
    • Angle of the brick's bottom relative to the ground should always match that of the platform

Robot Dynamics

  1. The simulation tracks the state of the robot according to a simple kinematic model.
  2. Velocity commands are received by subscribing to cmd_vel (geometry_msgs/msg/Twist)
    • The robot state is updated in the simulation loop
    • Only x and y linear velocities are executed, others are ignored
    • Velocities above max_velocity are ignored
    • The wheel immediately pivots to point in the direction of motion
    • The wheel should rotate to simulate rolling without slipping
  3. The robot publishes a sensor_msgs/msg/JointState message on the joint_states topic
  4. The robot simulation subscribes to tilt of custom type brickbot_interfaces/msg/Tilt, which has a single member angle, of floating point type.
    • The platform's tilt angle should be updated on the next simulation step

Control Node

Overview

The control node is implemented in control.py and it is responsible for higher-level control and odometry

  1. The control node should have any parameters it requires, as defined in config/robot.yaml
  2. The control node is driven by a timer that executes at 100Hz.
    • Everything published/broadcast from this node happens from the timer callback
  3. The control node subscribes to a goal_pose topic of the geometry_msgs/msg/PoseStamped message type.
    • Only goal_poses relative to the world frame are accepted
    • When the node receives this message it starts driving the robot toward the goal pose by publishing cmd_vel messages
    • When the robot is not moving, the node publishes a cmd_vel of 0
  4. The control node subscribes to the joint_states topic and computes wheel odometry
    • It should publish a nav_msgs/msg/Odometry message (covariance can be ignored) on the odom topic
    • It broadcasts a transform from odom to base_link based on the odometry calculation

Launch

  1. Write a launchfile called control.launch.xml that
    • Starts the simulation node
    • Starts the control node
    • Includes show_robot.launch.{py,xml} as appropriate to draw the robot in rviz
      • Python launchfiles can be included in xml launchfiles and vice versa, you can use any combination you would like here.
    • You can now test the goal_pose command and should see the robot move to the goal location
  2. The launchfile has a use_rviz argument to decide whether to start rviz

Catcher Node

Overview

  1. The catcher node is implemented in catcher.py
    • It can use any parameters it needs as specified in config/robot.yaml
    • It can read the gravity parameter from config/simulation.yaml
  2. Write a node called catcher that causes the robot to catch a falling brick
  3. The node knows the brick's location based on the brick tf frame and monitors it.
  4. When the node detects that the brick is falling it:
    1. Computes (based on max_velocity) whether it can reach the brick in time to catch it
      • If the brick cannot be caught, the node publishes a Text marker that lasts for 3 seconds that says "Unreachable"
        • The marker is published on the visualization_marker topic in the catcher marker namespace
      • If the brick can be caught, the robot drives under the brick and catches it.
  5. When a brick is caught, the robot tilts the brick until it is off the platform (and just floating in space!).

Launchfile

  1. Write a launchfile called catcher.launch.xml that runs all the nodes necessary for the robot to catch a brick
    • The launchfile should include control.launch.xml as needed
    • The rviz configuration should show the robot, the arena, and the brick
    • The launchfile has a use_rviz argument to decide whether to launch rviz

Testing

  1. Your code should pass all the standard tests that are run with colcon test
  2. Write at least one pytest unit test for:
    • physics.Robot.drive
    • physics.Brick.drop_gravity
    • physics.Brick.slide_ramp
  3. Write a test launchfile called test_brick_launch.py that verifies that the control node publishes cmd_vel commands at 100 Hz
  4. Write a test launchfile called test_frames_launch.py that
    • Places the brick directly above the platform
    • Drops the brick
    • Tilts
    • Verifies that a brick, after sliding, stays in the correct location
  5. Your final result should not have any rviz errors or warnings once all nodes in a given launchfile have started running.

README

In the base directory of your repository (e.g. homework2) create a README.md using the following template (be sure to update the ${} with the answers.)

# ME450 Embedded Systems Homework 2
Author: ${Your Name}

${A brief overview of what the package does}

## Quickstart
1. Use `ros2 launch ${pkg} ${launchfile} ${options}` to start the arena and robot simulation
2. Use ${ros2 service call here} to place a brick
3. Use ${ros2 service call here} to drop a brick
4. Here is a video of the robot when the brick is within catching range
   ${embed video here, it must be playable on github. Upload the video as an issue and link to it}

5. Here is a screenshot of the robot when the brick cannot be caught

   ${embed image here, it must be viewable on github.}

6. Here is a screenshot when =big_robot.yaml= is loaded by the show_robot.launch.xml launchfile

   ${embed image here, it must be viewable on github.}

Author: Matthew Elwin.