Browse Source

Update the running instructions: first pass

pull/1/head
hello-chintan 2 years ago
parent
commit
c1fee37bac
13 changed files with 468 additions and 350 deletions
  1. +3
    -3
      README.md
  2. +63
    -52
      example_1.md
  3. +46
    -27
      example_2.md
  4. +46
    -32
      example_3.md
  5. +52
    -36
      example_4.md
  6. +210
    -130
      follow_joint_trajectory.md
  7. +5
    -0
      gazebo_basics.md
  8. +11
    -37
      getting_started.md
  9. +7
    -22
      internal_state_of_stretch.md
  10. +9
    -5
      moveit_basics.md
  11. +5
    -0
      navigation_stack.md
  12. +6
    -4
      rviz_basics.md
  13. +5
    -1
      teleoperating_stretch.md

+ 3
- 3
README.md View File

@ -11,15 +11,15 @@ This branch provides instructions on the installation and use of code on the Str
6. [Navigation Stack](navigation_stack.md)
7. [MoveIt! Basics](moveit_basics.md)
8. [Follow Joint Trajectory Commands](follow_joint_trajectory.md)
9. [FUNMAP](https://github.com/hello-robot/stretch_ros/tree/master/stretch_funmap)
<!-- 9. [FUNMAP](https://github.com/hello-robot/stretch_ros/tree/master/stretch_funmap)
10. Microphone Array
11. ROS testing
12. Other Nav Stack Features
13. Perception
14. 4 Modes of Stretch
14. 4 Modes of Stretch -->
## Other ROS Examples
## Other ROS 2 Examples
To help you get started on your software development, here are examples of nodes to have the stretch perform simple tasks.
1. [Teleoperate Stretch with a Node](example_1.md) - Use a python script that sends velocity commands.

+ 63
- 52
example_1.md View File

@ -1,56 +1,66 @@
## Example 1
<p align="center">
<img src="images/move_stretch.gif"/>
</p>
The goal of this example is to give you an enhanced understanding of how to control the mobile base by sending `Twist` messages to a Stretch robot.
Begin by running `roscore` in a terminal. Then set the ros parameter to *navigation* mode by running the following commands in a new terminal.
```bash
rosparam set /stretch_driver/mode "navigation"
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
To drive the robot forward with the move node, type the following in a new terminal.
To drive the robot in circles with the move node, type the following in a new terminal.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 move.py
ros2 run stetch_ros_tutorials move
```
To stop the node from sending twist messages, type **Ctrl** + **c**.
### The Code
Below is the code which will send *Twist* messages to drive the robot forward.
Below is the code which will send *Twist* messages to drive the robot in circles.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
import rospy
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
class Move:
class Move(Node):
def __init__(self):
self.pub = rospy.Publisher('/stretch/cmd_vel', Twist, queue_size=1) #/stretch_diff_drive_controller/cmd_vel for gazebo
super().__init__('stretch_base_move')
self.publisher_ = self.create_publisher(Twist, '/stretch/cmd_vel', 10)
self.get_logger().info("Starting to move in circle...")
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.move_around)
def move_forward(self):
def move_around(self):
command = Twist()
command.linear.x = 0.1
command.linear.x = 0.0
command.linear.y = 0.0
command.linear.z = 0.0
command.angular.x = 0.0
command.angular.y = 0.0
command.angular.z = 0.0
self.pub.publish(command)
command.angular.z = 0.5
if __name__ == '__main__':
rospy.init_node('move')
self.publisher_.publish(command)
def main(args=None):
rclpy.init(args=args)
base_motion = Move()
rate = rospy.Rate(10)
while not rospy.is_shutdown():
base_motion.move_forward()
rate.sleep()
rclpy.spin(base_motion)
base_motion.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
```
### The Code Explained
@ -58,25 +68,32 @@ if __name__ == '__main__':
Now let's break the code down.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
```
Every Python ROS [Node](http://wiki.ros.org/Nodes) will have this declaration at the top. The first line makes sure your script is executed as a Python script.
```python
import rospy
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
```
You need to import rospy if you are writing a ROS Node. The geometry_msgs.msg import is so that we can send velocity commands to the robot.
You need to import rclpy if you are writing a ROS 2 Node. The geometry_msgs.msg import is so that we can send velocity commands to the robot.
```python
class Move:
class Move(Node):
def __init__(self):
self.pub = rospy.Publisher('/stretch/cmd_vel', Twist, queue_size=1)#/stretch_diff_drive_controller/cmd_vel for gazebo
super().__init__('stretch_base_move')
self.publisher_ = self.create_publisher(Twist, '/stretch/cmd_vel', 10)
```
This section of code defines the talker's interface to the rest of ROS. pub = rospy.Publisher("/stretch/cmd_vel", Twist, queue_size=1) declares that your node is publishing to the /stretch/cmd_vel topic using the message type Twist. The queue_size argument limits the amount of queued messages if any subscriber is not receiving them fast enough.
This section of code defines the talker's interface to the rest of ROS. self.publisher_ = self.create_publisher(Twist, '/stretch/cmd_vel', 10) declares that your node is publishing to the /stretch/cmd_vel topic using the message type Twist. The queue_size argument limits the amount of queued messages if any subscriber is not receiving them fast enough.
```Python
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.move_around)
```
We create a timer with a period of 0.5 seconds. This timer ensures that the function move_around is called every 0.5 seconds. This ensures a constant rate of 2Hz for the execution loop.
```Python
command = Twist()
@ -85,45 +102,39 @@ Make a Twist message. We're going to set all of the elements, since we
can't depend on them defaulting to safe values.
```python
command.linear.x = 0.1
command.linear.y = 0.0
command.linear.z = 0.0
command.linear.x = 0.0
command.linear.y = 0.0
command.linear.z = 0.0
```
A Twist has three linear velocities (in meters per second), along each of the axes. For Stretch, it will only pay attention to the x velocity, since it can't directly move in the y direction or the z direction.
A Twist has three linear velocities (in meters per second), along each of the axes. For Stretch, it will only pay attention to the x velocity, since it can't directly move in the y direction or the z direction. We set the linear velocities to 0.
```python
command.angular.x = 0.0
command.angular.y = 0.0
command.angular.z = 0.0
command.angular.x = 0.0
command.angular.y = 0.0
command.angular.z = 0.5
```
A *Twist* also has three rotational velocities (in radians per second).
The Stretch will only respond to rotations around the z (vertical) axis.
The Stretch will only respond to rotations around the z (vertical) axis. We set this to a non-zero value.
```python
self.pub.publish(command)
self.publisher_.publish(command)
```
Publish the Twist commands in the previously defined topic name */stretch/cmd_vel*.
```Python
rospy.init_node('move')
base_motion = Move()
rate = rospy.Rate(10)
```
The next line, rospy.init_node(NAME, ...), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
The `rospy.Rate()` function creates a Rate object rate. With the help of its method sleep(), it offers a convenient way for looping at the desired rate. With its argument of 10, we should expect to go through the loop 10 times per second (as long as our processing time does not exceed 1/10th of a second!)
```python
while not rospy.is_shutdown():
base_motion.move_forward()
rate.sleep()
def main(args=None):
rclpy.init(args=args)
base_motion = Move()
rclpy.spin(base_motion)
base_motion.destroy_node()
rclpy.shutdown()
```
This loop is a fairly standard rospy construct: checking the rospy.is_shutdown() flag and then doing work. You have to check is_shutdown() to check if your program should exit (e.g. if there is a Ctrl-C or otherwise). The loop calls rate.sleep(), which sleeps just long enough to maintain the desired rate through the loop.
The next line, rclpy.init(args=args), is very important as it tells ROS to initialize the node. Until rclpy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name 'stretch_base_move'. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/". We then create an instance called base_motion of the class Move(). This is then spun using the spin function in rclpy to call the callback functions, in our case the timer that ensures the move_around function is called at a steady rate of 2Hz.
## Move Stretch in Simulation
<!-- ## Move Stretch in Simulation
<p align="center">
<img src="images/move.gif"/>
</p>
@ -139,7 +150,7 @@ After saving the edited node, bringup [Stretch in the empty world simulation](ga
```
cd catkin_ws/src/stretch_ros_turotials/src/
python3 move.py
```
``` -->
To stop the node from sending twist messages, type **Ctrl** + **c**.

+ 46
- 27
example_2.md View File

@ -2,6 +2,7 @@
The aim of this example is to provide instruction on how to filter scan messages.
<!-- TODO: Update the links -->
For robots with laser scanners, ROS provides a special Message type in the [sensor_msgs](http://wiki.ros.org/sensor_msgs) package called [LaserScan](http://docs.ros.org/en/api/sensor_msgs/html/msg/LaserScan.html) to hold information about a given scan. Let's take a look at the message specification itself:
```
@ -43,24 +44,23 @@ Knowing the orientation of the LiDAR allows us to filter the scan values for a d
First, open a terminal and run the stretch driver launch file.
```bash
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
Then in a new terminal run the rplidar launch file from `stretch_core`.
```bash
roslaunch stretch_core rplidar.launch
ros2 launch stretch_core rplidar.launch.py
```
To filter the lidar scans for ranges that are directly in front of Stretch (width of 1 meter) run the scan filter node by typing the following in a new terminal.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 scan_filter.py
ros2 run stretch_ros_tutorials scan_filter
```
Then run the following command to bring up a simple RViz configuration of the Stretch robot.
```bash
rosrun rviz rviz -d `rospack find stretch_core`/rviz/stretch_simple_test.rviz
ros2 run rviz2 rviz2 -d `ros2 pkg prefix stretch_calibration`/rviz/stretch_simple_test.rviz
```
Change the topic name from the LaserScan display from */scan* to */filter_scan*.
@ -71,31 +71,47 @@ Change the topic name from the LaserScan display from */scan* to */filter_scan*.
### The Code
```python
#!/usr/bin/env python
#!/usr/bin/env python3
import rospy
import rclpy
from rclpy.node import Node
from numpy import linspace, inf
from math import sin
from sensor_msgs.msg import LaserScan
class Scanfilter:
class ScanFilter(Node):
def __init__(self):
self.width = 1.0
super().__init__('stretch_scan_filter')
self.pub = self.create_publisher(LaserScan, '/filtered_scan', 10)
self.sub = self.create_subscription(LaserScan, '/scan', self.scan_filter_callback, 10)
self.width = 1
self.extent = self.width / 2.0
self.sub = rospy.Subscriber('/scan', LaserScan, self.callback)
self.pub = rospy.Publisher('filtered_scan', LaserScan, queue_size=10)
def callback(self,msg):
self.get_logger().info("Publishing the filtered_scan topic. Use RViz to visualize.")
def scan_filter_callback(self,msg):
angles = linspace(msg.angle_min, msg.angle_max, len(msg.ranges))
points = [r * sin(theta) if (theta < -2.5 or theta > 2.5) else inf for r,theta in zip(msg.ranges, angles)]
new_ranges = [r if abs(y) < self.extent else inf for r,y in zip(msg.ranges, points)]
msg.ranges = new_ranges
self.pub.publish(msg)
def main(args=None):
rclpy.init(args=args)
scan_filter = ScanFilter()
rclpy.spin(scan_filter)
scan_filter.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
rospy.init_node('scan_filter')
Scanfilter()
rospy.spin()
main()
```
### The Code Explained
@ -103,18 +119,19 @@ if __name__ == '__main__':
Now let's break the code down.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
```
Every Python ROS [Node](http://wiki.ros.org/Nodes) will have this declaration at the top. The first line makes sure your script is executed as a Python script.
```python
import rospy
import rclpy
from rclpy.node import Node
from numpy import linspace, inf
from math import sin
from sensor_msgs.msg import LaserScan
```
You need to import rospy if you are writing a ROS Node. There are functions from numpy and math that are required within this code, thus why linspace, inf, and sin are imported. The sensor_msgs.msg import is so that we can subscribe and publish LaserScan messages.
You need to import rclpy if you are writing a ROS Node. There are functions from numpy and math that are required within this code, that's why linspace, inf, and sin are imported. The sensor_msgs.msg import is so that we can subscribe and publish LaserScan messages.
```python
self.width = 1
@ -123,14 +140,14 @@ self.extent = self.width / 2.0
We're going to assume that the robot is pointing up the x-axis, so that any points with y coordinates further than half of the defined width (1 meter) from the axis are not considered.
```python
self.sub = rospy.Subscriber('/scan', LaserScan, self.callback)
self.sub = self.create_subscription(LaserScan, '/scan', self.scan_filter_callback, 10)
```
Set up a subscriber. We're going to subscribe to the topic "scan", looking for LaserScan messages. When a message comes in, ROS is going to pass it to the function "callback" automatically.
Set up a subscriber. We're going to subscribe to the topic "/scan", looking for LaserScan messages. When a message comes in, ROS is going to pass it to the function "callback" automatically.
```python
self.pub = rospy.Publisher('filtered_scan', LaserScan, queue_size=10)
self.pub = self.create_publisher(LaserScan, '/filtered_scan', 10)
```
`pub = rospy.Publisher("filtered_scan", LaserScan, queue_size=10)` declares that your node is publishing to the *filtered_scan* topic using the message type LaserScan. This lets the master tell any nodes listening on *filtered_scan* that we are going to publish data on that topic.
This declares that your node is publishing to the *filtered_scan* topic using the message type LaserScan. This lets any nodes listening on *filtered_scan* that we are going to publish data on that topic.
```python
angles = linspace(msg.angle_min, msg.angle_max, len(msg.ranges))
@ -155,15 +172,17 @@ self.pub.publish(msg)
Substitute in the new ranges in the original message, and republish it.
```python
rospy.init_node('scan_filter')
Scanfilter()
def main(args=None):
rclpy.init(args=args)
scan_filter = ScanFilter()
```
The next line, rospy.init_node(NAME, ...), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
The next line, rclpy.init_node initializes the node. In this case, your node will take on the name 'stretch_scan_filter'. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
Setup Scanfilter class with `Scanfilter()`
Setup Scanfilter class with `scan_filter = Scanfilter()`
```python
rospy.spin()
rclpy.spin(scan_filter)
```
Give control to ROS. This will allow the callback to be called whenever new
messages come in. If we don't put this line in, then the node will not work,

+ 46
- 32
example_3.md View File

@ -2,20 +2,16 @@
The aim of example 3 is to combine the two previous examples and have Stretch utilize its laser scan data to avoid collision with objects as it drives forward.
Begin by running `roscore` in a terminal. Then set the ros parameter to *navigation* mode by running the following commands in a new terminal.
```bash
rosparam set /stretch_driver/mode "navigation"
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
Then in a new terminal type the following to activate the LiDAR sensor.
```bash
roslaunch stretch_core rplidar.launch
ros2 launch stretch_core rplidar.launch.py
```
To activate the avoider node, type the following in a new terminal.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 avoider.py
ros2 run stretch_ros_tutorials avoider
```
To stop the node from sending twist messages, type **Ctrl** + **c** in the terminal running the avoider node.
@ -26,21 +22,22 @@ To stop the node from sending twist messages, type **Ctrl** + **c** in the termi
### The Code
```python
#!/usr/bin/env python
#!/usr/bin/env python3
import rospy
import rclpy
from rclpy.node import Node
from numpy import linspace, inf, tanh
from math import sin
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
class Avoider:
def __init__(self):
self.pub = rospy.Publisher('/stretch/cmd_vel', Twist, queue_size=1) #/stretch_diff_drive_controller/cmd_vel for gazebo
self.sub = rospy.Subscriber('/scan', LaserScan, self.set_speed)
class Avoider(Node):
def __init__(self):
super().__init__('stretch_avoider')
self.width = 1
self.extent = self.width / 2.0
self.distance = 0.5
self.twist = Twist()
@ -51,19 +48,32 @@ class Avoider:
self.twist.angular.y = 0.0
self.twist.angular.z = 0.0
def set_speed(self,msg):
self.publisher_ = self.create_publisher(Twist, '/stretch/cmd_vel', 1)
self.subscriber_ = self.create_subscription(LaserScan, '/scan', self.set_speed, 10)
def set_speed(self, msg):
angles = linspace(msg.angle_min, msg.angle_max, len(msg.ranges))
points = [r * sin(theta) if (theta < -2.5 or theta > 2.5) else inf for r,theta in zip(msg.ranges, angles)]
new_ranges = [r if abs(y) < self.extent else inf for r,y in zip(msg.ranges, points)]
error = min(new_ranges) - self.distance
self.twist.linear.x = tanh(error) if (error > 0.05 or error < -0.05) else 0
self.pub.publish(self.twist)
self.publisher_.publish(self.twist)
def main(args=None):
rclpy.init(args=args)
avoider = Avoider()
rclpy.spin(avoider)
avoider.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
rospy.init_node('avoider')
Avoider()
rospy.spin()
main()
```
### The Code Explained
@ -71,31 +81,32 @@ if __name__ == '__main__':
Now let's break the code down.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
```
Every Python ROS [Node](http://wiki.ros.org/Nodes) will have this declaration at the top. The first line makes sure your script is executed as a Python script.
```python
import rospy
import rclpy
from rclpy.node import Node
from numpy import linspace, inf, tanh
from math import sin
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
```
You need to import rospy if you are writing a ROS Node. There are functions from numpy and math that are required within this code, thus linspace, inf, tanh, and sin are imported. The sensor_msgs.msg import is so that we can subscribe to LaserScan messages. The geometry_msgs.msg import is so that we can send velocity commands to the robot.
You need to import rclpy if you are writing a ROS Node. There are functions from numpy and math that are required within this code, thus linspace, inf, tanh, and sin are imported. The sensor_msgs.msg import is so that we can subscribe to LaserScan messages. The geometry_msgs.msg import is so that we can send velocity commands to the robot.
```python
self.pub = rospy.Publisher('/stretch/cmd_vel', Twist, queue_size=1)#/stretch_diff_drive_controller/cmd_vel for gazebo
self.publisher_ = self.create_publisher(Twist, '/stretch/cmd_vel', 1)
```
This section of code defines the talker's interface to the rest of ROS. pub = rospy.Publisher("/stretch/cmd_vel", Twist, queue_size=1) declares that your node is publishing to the /stretch/cmd_vel topic using the message type Twist.
This declares that your node is publishing to the /stretch/cmd_vel topic using the message type Twist.
```python
self.sub = rospy.Subscriber('/scan', LaserScan, self.set_speed)
self.subscriber_ = self.create_subscription(LaserScan, '/scan', self.set_speed, 10)
```
Set up a subscriber. We're going to subscribe to the topic "scan", looking for LaserScan messages. When a message comes in, ROS is going to pass it to the function "set_speed" automatically.
Set up a subscriber. We're going to subscribe to the topic "/scan", looking for LaserScan messages. When a message comes in, ROS is going to pass it to the function "set_speed" automatically.
```python
@ -138,19 +149,22 @@ Set the speed according to a tanh function. This method gives a nice smooth mapp
```python
self.pub.publish(self.twist)
self.publisher_.publish(self.twist)
```
Publish the Twist message.
```python
rospy.init_node('avoider')
Avoider()
rospy.spin()
def main(args=None):
rclpy.init(args=args)
avoider = Avoider()
rclpy.spin(avoider)
avoider.destroy_node()
rclpy.shutdown()
```
The next line, rospy.init_node(NAME, ...), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
The next line, rclpy.init() method initializes the node. In this case, your node will take on the name 'stretch_avoider'. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
Setup Avoider class with `Avioder()`
Setup Avoider class with `avoider = Avioder()`
Give control to ROS with `rospy.spin()`. This will allow the callback to be called whenever new messages come in. If we don't put this line in, then the node will not work, and ROS will not process any messages.
Give control to ROS with `rclpy.spin()`. This will allow the callback to be called whenever new messages come in. If we don't put this line in, then the node will not work, and ROS will not process any messages.
**Next Example:** [Example 4](example_4.md)

+ 52
- 36
example_4.md View File

@ -2,16 +2,16 @@
![image](images/balloon.png)
Let's bringup stretch in the willowgarage world from the [gazebo basics tutorial](gazebo_basics.md) and RViz by using the following command.
Let's bringup stretch in RViz by using the following command.
```bash
roslaunch stretch_gazebo gazebo.launch world:=worlds/willowgarage.world rviz:=true
ros2 launch stretch_core stretch_driver.launch.py
ros2 run rviz2 rviz2 -d `ros2 pkg prefix stretch_calibrtion`/rviz/stretch_simple_test.rviz
```
the `rviz` flag will open an RViz window to visualize a variety of ROS topics. In a new terminal run the following commands to create a marker.
In a new terminal run the following commands to create a marker.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 marker.py
ros2 run stretch_ros_tutorials marker
```
The gif below demonstrates how to add a new *Marker* display type, and change the topic name from `visualization_marker` to `balloon`. A red sphere Marker should appear above the Stretch robot.
@ -20,43 +20,57 @@ The gif below demonstrates how to add a new *Marker* display type, and change th
### The Code
```python
#!/usr/bin/env python
#!/usr/bin/env python3
import rospy
import rclpy
from rclpy.node import Node
from visualization_msgs.msg import Marker
class Balloon():
class Balloon(Node):
def __init__(self):
self.publisher = rospy.Publisher('balloon', Marker, queue_size=10)
super().__init__('stretch_marker')
self.publisher_ = self.create_publisher(Marker, 'balloon', 10)
self.marker = Marker()
self.marker.header.frame_id = '/base_link'
self.marker.header.stamp = rospy.Time()
self.marker.header.stamp = self.get_clock().now().to_msg()
self.marker.type = self.marker.SPHERE
self.marker.id = 0
self.marker.action = self.marker.ADD
self.marker.scale.x = 0.5
self.marker.scale.y = 0.5
self.marker.scale.z = 0.5
self.marker.color.r = 1.0
self.marker.color.g = 0.0
self.marker.color.b = 0.0
self.marker.color.a = 1.0
self.marker.pose.position.x = 0.0
self.marker.pose.position.y = 0.0
self.marker.pose.position.z = 2.0
self.get_logger().info("Publishing the balloon topic. Use RViz to visualize.")
def publish_marker(self):
self.publisher.publish(self.marker)
self.publisher_.publish(self.marker)
def main(args=None):
rclpy.init(args=args)
balloon = Balloon()
while rclpy.ok():
balloon.publish_marker()
balloon.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
rospy.init_node('marker', argv=sys.argv)
ballon = Balloon()
rate = rospy.Rate(10)
while not rospy.is_shutdown():
ballon.publish_marker()
rate.sleep()
main()
```
@ -64,30 +78,33 @@ if __name__ == '__main__':
Now let's break the code down.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
```
Every Python ROS [Node](http://wiki.ros.org/Nodes) will have this declaration at the top. The first line makes sure your script is executed as a Python script.
```python
import rospy
import rclpy
from rclpy.node import Node
from visualization_msgs.msg import Marker
```
You need to import rospy if you are writing a ROS Node. Import the `Marker` type from the visualization_msgs.msg package. This import is required to publish a Marker, which will be visualized in RViz.
You need to import rclpy if you are writing a ROS 2 Node. Import the `Marker` type from the visualization_msgs.msg package. This import is required to publish a Marker, which will be visualized in RViz.
```python
self.pub = rospy.Publisher('balloon', Marker, queue_size=10)
self.publisher_ = self.create_publisher(Marker, 'balloon', 10)
```
This section of code defines the talker's interface to the rest of ROS. pub = rospy.Publisher("balloon", Twist, queue_size=1) declares that your node is publishing to the */ballon* topic using the message type *Twist*.
This declares that your node is publishing to the */ballon* topic using the message type *Twist*.
```python
self.marker = Marker()
self.marker.header.frame_id = '/base_link'
self.marker.header.stamp = rospy.Time()
self.marker.header.stamp = self.get_clock().now().to_msg()
self.marker.type = self.marker.SPHERE
```
<!-- TODO: Update links -->
Create a maker. Markers of all shapes share a common type. Set the frame ID and type. The frame ID is the frame in which the position of the marker is specified. The type is the shape of the marker. Further details on marker shapes can be found here: [RViz Markers](http://wiki.ros.org/rviz/DisplayTypes/Marker)
```python
@ -130,27 +147,26 @@ Specify the pose of the marker. Since spheres are rotationally invariant, we're
```python
def publish_marker(self):
self.publisher.publish(self.marker)
self.publisher_.publish(self.marker)
```
Publish the Marker data structure to be visualized in RViz.
```python
rospy.init_node('marker', argv=sys.argv)
ballon = Balloon()
rate = rospy.Rate(10)
def main(args=None):
rclpy.init(args=args)
balloon = Balloon()
```
The next line, rospy.init_node(NAME, ...), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
Setup Balloon class with `Balloon()`
The next line, rospy.init. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
Give control to ROS with `rospy.spin()`. This will allow the callback to be called whenever new messages come in. If we don't put this line in, then the node will not work, and ROS will not process any messages.
Setup Balloon class with `balloon = Balloon()`
```python
while not rospy.is_shutdown():
ballon.publish_marker()
rate.sleep()
while rclpy.ok():
balloon.publish_marker()
balloon.destroy_node()
rclpy.shutdown()
```
This loop is a fairly standard rospy construct: checking the rospy.is_shutdown() flag and then doing work. You have to check is_shutdown() to check if your program should exit (e.g. if there is a Ctrl-C or otherwise). The loop calls rate.sleep(), which sleeps just long enough to maintain the desired rate through the loop.
This loop is a fairly standard rclpy construct: checking the rclpy.ok() flag and then doing work. You have to run this check to see if your program should exit (e.g. if there is a Ctrl-C or otherwise).

+ 210
- 130
follow_joint_trajectory.md View File

@ -1,24 +1,22 @@
## FollowJointTrajectory Commands
Stretch ROS driver offers a [`FollowJointTrajectory`](http://docs.ros.org/en/api/control_msgs/html/action/FollowJointTrajectory.html) action service for its arm. Within this tutorial we will have a simple FollowJointTrajectory command sent to a Stretch robot to execute.
Stretch driver offers a [`FollowJointTrajectory`](http://docs.ros.org/en/api/control_msgs/html/action/FollowJointTrajectory.html) action service for its arm. Within this tutorial we will have a simple FollowJointTrajectory command sent to a Stretch robot to execute.
## Stow Command Example
<p align="center">
<img src="images/stow_command.gif"/>
</p>
Begin by running `roscore` in a terminal. Then set the ros parameter to *position* mode by running the following commands in a new terminal.
Begin by launching stretch_driver in a terminal.
```bash
rosparam set /stretch_driver/mode "position"
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
In a new terminal type the following commands.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 stow_command.py
ros2 run stretch_ros_tutorials stow_command
```
This will send a FollowJointTrajectory command to stow Stretch's arm.
@ -27,45 +25,77 @@ This will send a FollowJointTrajectory command to stow Stretch's arm.
```python
#!/usr/bin/env python3
import rospy
from control_msgs.msg import FollowJointTrajectoryGoal
import rclpy
from rclpy.node import Node
from rclpy.duration import Duration
from rclpy.action import ActionClient
import sys
from control_msgs.action import FollowJointTrajectory
from trajectory_msgs.msg import JointTrajectoryPoint
import hello_helpers.hello_misc as hm
import time
class StowCommand(hm.HelloNode):
from sensor_msgs.msg import JointState
class StowCommand(Node):
def __init__(self):
hm.HelloNode.__init__(self)
super().__init__('stretch_stow_command')
self.joint_state = JointState()
self.trajectory_client = ActionClient(self, FollowJointTrajectory, '/stretch_controller/follow_joint_trajectory')
server_reached = self.trajectory_client.wait_for_server(timeout_sec=60.0)
if not server_reached:
self.get_logger().error('Unable to connect to arm action server. Timeout exceeded.')
sys.exit()
self.subscription = self.create_subscription(JointState, '/stretch/joint_states', self.joint_states_callback, 1)
self.subscription
def joint_states_callback(self, joint_state):
self.joint_state = joint_state
def issue_stow_command(self):
stow_point = JointTrajectoryPoint()
stow_point.time_from_start = rospy.Duration(0.000)
stow_point.positions = [0.2, 0.0, 3.4]
joint_state = self.joint_state
if (joint_state is not None):
self.get_logger().info('stowing...')
trajectory_goal = FollowJointTrajectoryGoal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [stow_point]
trajectory_goal.trajectory.header.stamp = rospy.Time(0.0)
trajectory_goal.trajectory.header.frame_id = 'base_link'
stow_point1 = JointTrajectoryPoint()
stow_point2 = JointTrajectoryPoint()
duration1 = Duration(seconds=0.0)
duration2 = Duration(seconds=4.0)
stow_point1.time_from_start = duration1.to_msg()
stow_point2.time_from_start = duration2.to_msg()
joint_value1 = joint_state.position[1] # joint_lift is at index 1
joint_value2 = joint_state.position[0] # wrist_extension is at index 0
joint_value3 = joint_state.position[8] # joint_wrist_yaw is at index 8
stow_point1.positions = [joint_value1, joint_value2, joint_value3]
stow_point2.positions = [0.2, 0.0, 3.14]
trajectory_goal = FollowJointTrajectory.Goal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [stow_point1, stow_point2]
trajectory_goal.trajectory.header.stamp = self.get_clock().now().to_msg()
trajectory_goal.trajectory.header.frame_id = 'base_link'
self.trajectory_client.send_goal_async(trajectory_goal)
self.get_logger().info('Sent stow goal = {0}'.format(trajectory_goal))
self.trajectory_client.send_goal(trajectory_goal)
rospy.loginfo('Sent stow goal = {0}'.format(trajectory_goal))
self.trajectory_client.wait_for_result()
def main(self):
hm.HelloNode.main(self, 'stow_command', 'stow_command', wait_for_first_pointcloud=False)
rospy.loginfo('stowing...')
self.issue_stow_command()
time.sleep(2)
def main(args=None):
rclpy.init(args=args)
stow_command = StowCommand()
rclpy.spin_once(stow_command)
stow_command.issue_stow_command()
rclpy.spin(stow_command)
stow_command.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
try:
node = StowCommand()
node.main()
except KeyboardInterrupt:
rospy.loginfo('interrupt received, so shutting down')
main()
```
### The Code Explained
@ -79,65 +109,81 @@ Every Python ROS [Node](http://wiki.ros.org/Nodes) will have this declaration at
```python
import rospy
from control_msgs.msg import FollowJointTrajectoryGoal
import rclpy
from rclpy.node import Node
from rclpy.duration import Duration
from rclpy.action import ActionClient
import sys
from control_msgs.action import FollowJointTrajectory
from trajectory_msgs.msg import JointTrajectoryPoint
import hello_helpers.hello_misc as hm
import time
from sensor_msgs.msg import JointState
```
You need to import rospy if you are writing a ROS Node. Import the FollowJointTrajectoryGoal from the [control_msgs.msg](http://wiki.ros.org/control_msgs) package to control the Stretch robot. Import JointTrajectoryPoint from the [trajectory_msgs](http://wiki.ros.org/trajectory_msgs) package to define robot trajectories. The [hello_helpers](https://github.com/hello-robot/stretch_ros/tree/master/hello_helpers) package consists of a module the provides various Python scripts used across [stretch_ros](https://github.com/hello-robot/stretch_ros). In this instance we are importing the hello_misc script.
<!-- TODO: Update links below -->
You need to import rclpy if you are writing a ROS 2 Node. Import the FollowJointTrajectory from the [control_msgs.msg](http://wiki.ros.org/control_msgs) package to control the Stretch robot. Import JointTrajectoryPoint from the [trajectory_msgs](http://wiki.ros.org/trajectory_msgs) package to define robot trajectories.
```python
class StowCommand(hm.HelloNode):
class StowCommand(Node):
def __init__(self):
hm.HelloNode.__init__(self)
super().__init__('stretch_stow_command')
```
The `StowCommand ` class inherits the `HelloNode` class from `hm` and is initialized.
The `StowCommand ` class inherits from the `Node` class from and is initialized.
```python
def issue_stow_command(self):
stow_point = JointTrajectoryPoint()
stow_point.time_from_start = rospy.Duration(0.000)
stow_point.positions = [0.2, 0.0, 3.4]
```
The `issue_stow_command()` is the name of the function that will stow Stretch's arm. Within the function, we set *stow_point* as a `JointTrajectoryPoint`and provide desired positions (in meters). These are the positions of the lift, wrist extension, and yaw of the wrist, respectively. These are defined in the next set of the code.
The `issue_stow_command()` method will stow Stretch's arm. Within the function, we set *stow_point* as a `JointTrajectoryPoint`and provide desired positions (in meters). These are the positions of the lift, wrist extension, and yaw of the wrist, respectively. These are defined in the next set of the code.
```python
trajectory_goal = FollowJointTrajectoryGoal()
stow_point1 = JointTrajectoryPoint()
stow_point2 = JointTrajectoryPoint()
duration1 = Duration(seconds=0.0)
duration2 = Duration(seconds=4.0)
stow_point1.time_from_start = duration1.to_msg()
stow_point2.time_from_start = duration2.to_msg()
joint_value1 = joint_state.position[1] # joint_lift is at index 1
joint_value2 = joint_state.position[0] # wrist_extension is at index 0
joint_value3 = joint_state.position[8] # joint_wrist_yaw is at index 8
stow_point1.positions = [joint_value1, joint_value2, joint_value3]
stow_point2.positions = [0.2, 0.0, 3.14]
trajectory_goal = FollowJointTrajectory.Goal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [stow_point]
trajectory_goal.trajectory.header.stamp = rospy.Time(0.0)
trajectory_goal.trajectory.points = [stow_point1, stow_point2]
trajectory_goal.trajectory.header.stamp = self.get_clock().now().to_msg()
trajectory_goal.trajectory.header.frame_id = 'base_link'
```
Set *trajectory_goal* as a `FollowJointTrajectoryGoal` and define the joint names as a list. Then `trajectory_goal.trajectory.points` is defined by the positions set in *stow_point*. Specify the coordinate frame that we want (base_link) and set the time to be now.
Set *trajectory_goal* as a `FollowJointTrajectory.Goal()` and define the joint names as a list. Then `trajectory_goal.trajectory.points` is defined by the positions set in *stow_point*. Specify the coordinate frame that we want (base_link) and set the time to be now.
```python
self.trajectory_client.send_goal(trajectory_goal)
rospy.loginfo('Sent stow goal = {0}'.format(trajectory_goal))
self.trajectory_client.wait_for_result()
self.trajectory_client.send_goal_async(trajectory_goal)
self.get_logger().info('Sent stow goal = {0}'.format(trajectory_goal))
```
Make the action call and send the goal. The last line of code waits for the result before it exits the python script.
Make the action call and send the goal.
```python
def main(self):
hm.HelloNode.main(self, 'stow_command', 'stow_command', wait_for_first_pointcloud=False)
rospy.loginfo('stowing...')
self.issue_stow_command()
time.sleep(2)
def main(args=None):
rclpy.init(args=args)
stow_command = StowCommand()
rclpy.spin_once(stow_command)
stow_command.issue_stow_command()
rclpy.spin(stow_command)
stow_command.destroy_node()
rclpy.shutdown()
```
Create a funcion, `main()`, to do all of the setup the `hm.HelloNode` class and issue the stow command.
Create a funcion, `main()`, to do all of the setup in the class and issue the stow command. Initialize the `StowCommand()` class and set it to *node* and run the `main()` function.
```python
if __name__ == '__main__':
try:
node = StowCommand()
node.main()
except KeyboardInterrupt:
rospy.loginfo('interrupt received, so shutting down')
main()
```
Initialize the `StowCommand()` class and set it to *node* and run the `main()` function.
To make the script executable call the main() function like above.
## Multipoint Command Example
@ -146,18 +192,16 @@ Initialize the `StowCommand()` class and set it to *node* and run the `main()` f
<img src="images/multipoint.gif"/>
</p>
Begin by running `roscore` in a terminal. Then set the ros parameter to *position* mode by running the following commands in a new terminal.
If you have killed the above instance of stretch_driver relaunch it again through the terminal.
```bash
rosparam set /stretch_driver/mode "position"
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
In a new terminal type the following commands.
```bash
cd catkin_ws/src/stretch_ros_turotials/src/
python3 multipoint_command.py
ros2 run stretch_ros_tutorials multipoint_command
```
This will send a list of JointTrajectoryPoint's to move Stretch's arm.
@ -166,61 +210,103 @@ This will send a list of JointTrajectoryPoint's to move Stretch's arm.
```python
#!/usr/bin/env python3
import rospy
import time
from control_msgs.msg import FollowJointTrajectoryGoal
import sys
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from rclpy.duration import Duration
from control_msgs.action import FollowJointTrajectory
from trajectory_msgs.msg import JointTrajectoryPoint
import hello_helpers.hello_misc as hm
class MultiPointCommand(hm.HelloNode):
from sensor_msgs.msg import JointState
class MultiPointCommand(Node):
def __init__(self):
hm.HelloNode.__init__(self)
super().__init__('stretch_multipoint_command')
self.trajectory_client = ActionClient(self, FollowJointTrajectory, '/stretch_controller/follow_joint_trajectory')
server_reached = self.trajectory_client.wait_for_server(timeout_sec=60.0)
if not server_reached:
self.get_logger().error('Unable to connect to arm action server. Timeout exceeded.')
sys.exit()
self.subscription = self.create_subscription(JointState, '/stretch/joint_states', self.joint_states_callback, 1)
self.subscription
self.get_logger().info('issuing multipoint command...')
def joint_states_callback(self, joint_state):
self.joint_state = joint_state
def issue_multipoint_command(self):
point0 = JointTrajectoryPoint()
point0.positions = [0.2, 0.0, 3.4]
point0.velocities = [0.2, 0.2, 2.5]
point0.accelerations = [1.0, 1.0, 3.5]
joint_state = self.joint_state
duration0 = Duration(seconds=0.0)
duration1 = Duration(seconds=2.0)
duration2 = Duration(seconds=4.0)
duration3 = Duration(seconds=6.0)
duration4 = Duration(seconds=8.0)
duration5 = Duration(seconds=10.0)
joint_value1 = joint_state.position[1] # joint_lift is at index 1
joint_value2 = joint_state.position[0] # wrist_extension is at index 0
joint_value3 = joint_state.position[8] # joint_wrist_yaw is at index 8
point0 = JointTrajectoryPoint()
point1 = JointTrajectoryPoint()
point1.positions = [0.3, 0.1, 2.0]
point0.positions = [joint_value1, joint_value2, joint_value3]
point2 = JointTrajectoryPoint()
point2.positions = [0.5, 0.2, -1.0]
point0.velocities = [0.2, 0.2, 2.5]
point3 = JointTrajectoryPoint()
point3.positions = [0.6, 0.3, 0.0]
point0.accelerations = [1.0, 1.0, 3.5]
point4 = JointTrajectoryPoint()
point4.positions = [0.8, 0.2, 1.0]
point0.time_from_start = duration0.to_msg()
point5 = JointTrajectoryPoint()
point5.positions = [0.5, 0.1, 0.0]
point1 = JointTrajectoryPoint()
point1.positions = [0.3, 0.1, 2.0]
point1.time_from_start = duration1.to_msg()
trajectory_goal = FollowJointTrajectoryGoal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [point0, point1, point2, point3, point4, point5]
trajectory_goal.trajectory.header.stamp = rospy.Time(0.0)
trajectory_goal.trajectory.header.frame_id = 'base_link'
point2 = JointTrajectoryPoint()
point2.positions = [0.5, 0.2, -1.0]
point2.time_from_start = duration2.to_msg()
self.trajectory_client.send_goal(trajectory_goal)
rospy.loginfo('Sent stow goal = {0}'.format(trajectory_goal))
self.trajectory_client.wait_for_result()
point3 = JointTrajectoryPoint()
point3.positions = [0.6, 0.3, 0.0]
point3.time_from_start = duration3.to_msg()
def main(self):
hm.HelloNode.main(self, 'multipoint_command', 'multipoint_command', wait_for_first_pointcloud=False)
rospy.loginfo('issuing multipoint command...')
self.issue_multipoint_command()
time.sleep(2)
point4 = JointTrajectoryPoint()
point4.positions = [0.8, 0.2, 1.0]
point4.time_from_start = duration4.to_msg()
point5 = JointTrajectoryPoint()
point5.positions = [0.5, 0.1, 0.0]
point5.time_from_start = duration5.to_msg()
trajectory_goal = FollowJointTrajectory.Goal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [point0, point1, point2, point3, point4, point5]
trajectory_goal.trajectory.header.stamp = self.get_clock().now().to_msg()
trajectory_goal.trajectory.header.frame_id = 'base_link'
self.trajectory_client.send_goal_async(trajectory_goal)
self.get_logger().info('Sent stow goal = {0}'.format(trajectory_goal))
def main(args=None):
rclpy.init(args=args)
multipoint_command = MultiPointCommand()
rclpy.spin_once(multipoint_command)
multipoint_command.issue_multipoint_command()
rclpy.spin(multipoint_command)
multipoint_command.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
try:
node = MultiPointCommand()
node.main()
except KeyboardInterrupt:
rospy.loginfo('interrupt received, so shutting down')
main()
```
@ -228,31 +314,25 @@ if __name__ == '__main__':
Seeing that there are similarities between the multipoint and stow command nodes, we will only breakdown the different components of the multipoint_command node.
```python
point0 = JointTrajectoryPoint()
point0.positions = [0.2, 0.0, 3.4]
point1 = JointTrajectoryPoint()
point1.positions = [0.3, 0.1, 2.0]
point1.time_from_start = duration1.to_msg()
```
Sett *point0* as a `JointTrajectoryPoint`and provide desired positions (in meters). These are the positions of the lift, wrist extension, and yaw of the wrist, respectively.
Set *point1* as a `JointTrajectoryPoint`and provide desired positions (in meters). These are the positions of the lift, wrist extension, and yaw of the wrist, respectively.
**IMPORTANT NOTE**: The lift and wrist extension can only go up to 0.2 m/s. If you do not provide any velocities or accelerations for the lift or wrist extension, then they go to their default values. However, the Velocity and Acceleration of the wrist yaw will stay the same from the previous value unless updated.
```python
point0.velocities = [0.2, 0.2, 2.5]
```
Provide desired velocity of the lift (m/s), wrist extension (m/s), and wrist yaw (rad/s) for *point0*.
```python
point0.accelerations = [1.0, 1.0, 3.5]
```
Provide desired velocity of the lift (m/s^2), wrist extension (m/s^2), and wrist yaw (rad/s^2).
trajectory_goal = FollowJointTrajectory.Goal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
**IMPORTANT NOTE**: The lift and wrist extension can only go up to 0.2 m/s. If you do not provide any velocities or accelerations for the lift or wrist extension, then they go to their default values. However, the Velocity and Acceleration of the wrist yaw will stay the same from the previous value unless updated.
trajectory_goal.trajectory.points = [point0, point1, point2, point3, point4, point5]
trajectory_goal.trajectory.header.stamp = self.get_clock().now().to_msg()
trajectory_goal.trajectory.header.frame_id = 'base_link'
```python
trajectory_goal = FollowJointTrajectoryGoal()
trajectory_goal.trajectory.joint_names = ['joint_lift', 'wrist_extension', 'joint_wrist_yaw']
trajectory_goal.trajectory.points = [point0, point1, point2, point3, point4, point5]
trajectory_goal.trajectory.header.stamp = rospy.Time(0.0)
trajectory_goal.trajectory.header.frame_id = 'base_link'
self.trajectory_client.send_goal_async(trajectory_goal)
self.get_logger().info('Sent stow goal = {0}'.format(trajectory_goal))
```
Set *trajectory_goal* as a `FollowJointTrajectoryGoal` and define the joint names as a list. Then `trajectory_goal.trajectory.points` is defined by a list of the 6 points. Specify the coordinate frame that we want (base_link) and set the time to be now.
Set *trajectory_goal* as a `FollowJointTrajectory.Goal()` and define the joint names as a list. Then `trajectory_goal.trajectory.points` is defined by a list of the 6 points. Specify the coordinate frame that we want (base_link) and set the time to be now.

+ 5
- 0
gazebo_basics.md View File

@ -1,5 +1,10 @@
# Spawning Stretch in Simulation (Gazebo)
### NOTE
Simulation support for Stretch in ROS 2 is under active development. Please reach out to us if you want to work with Stretch in a simulated environment like Gazebo/Ignition in ROS 2.
Refer to the instructions below if you want to test this functionality in ROS 1.
### Empty World Simulation
To spawn the Stretch in gazebo's default empty world run the following command in your terminal.
```bash

+ 11
- 37
getting_started.md View File

@ -1,51 +1,25 @@
# Getting Started
## Ubuntu
## Installing Ubuntu 20.04 with ROS 2 Galactic on Stretch
Hello Robot utilizes Ubuntu, an open source Linux operating system, for the Stretch RE1 platform. If you are unfamiliar with the operating system, we encourage you to review a [tutorial](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) provided by Ubuntu. Additionally, the Linux command line, BASH, is used to execute commands and is needed to run ROS on the Stretch robot. Here is a [tutorial](https://ryanstutorials.net/linuxtutorial/) on getting started with BASH.
## Installing Noetic on Stretch
Instructions on installing Noetic can be found in our open source [installation guide](https://github.com/hello-robot/stretch_ros/blob/dev/noetic/install_noetic.md). Once your system is setup, clone the [stretch_ros_tutorials](https://github.com/hello-sanchez/stretch_ros_tutorials.git) to your workspace. Then build the packages in your workspace.
```
cd ~/catkin_ws/src
git clone https://github.com/hello-sanchez/stretch_ros_tutorials.git
cd ~/catkin_ws
catkin_make
```
## ROS Setup on Local Computer
Hello Robot is currently running Stretch on Ubuntu 20.04 and on ROS Noetic. To begin the setup, begin with [installing Ubnutu desktop](https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview) on your local machine. Then follow the [installation guide for ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu) on your system.
<!-- TODO: Change the installation instructions link below -->
Instructions on installing Ubuntu 20.04 with ROS Noetic and ROS 2 Galactic can be found in our open source [installation guide](https://github.com/hello-robot/stretch_ros/blob/dev/noetic/install_noetic.md). Following these steps should create a separate Ubuntu 20.04 partition with an ament worskspace created in the home directory.
Currently, the Realsense2_description package isn't installed by rosdep and requires a user to manually install the package. Run the following command in your terminal
## ROS 2 Tutorials Setup on Local Computer
Once your system is setup, clone the [stretch_ros_tutorials](https://github.com/hello-sanchez/stretch_ros_tutorials.git) to the src directory of the ament workspace, then build the packages.
```bash
sudo apt-get install ros-noetic-realsense2-camera
```
After your system is setup, clone the [stretch_ros](https://github.com/hello-robot/stretch_ros.git), [stretch_ros_tutorials](https://github.com/hello-sanchez/stretch_ros_tutorials.git), and [realsense_gazebo_plugin packages]( https://github.com/pal-robotics/realsense_gazebo_plugin) to your preferred workspace. Then install dependencies and build the packages.
```bash
cd ~/catkin_ws/src
git clone https://github.com/hello-robot/stretch_ros
git clone https://github.com/pal-robotics/realsense_gazebo_plugin
cd ~/ament_ws/src
<!-- TODO: Change the link below -->
git clone https://github.com/hello-sanchez/stretch_ros_tutorials.git
cd ~/catkin_ws
rosdep install --from-paths src --ignore-src -r -y
catkin_make
cd ~/ament_ws
colcon build
```
Then source your workspace with the following command
```bash
echo "source /home/USER_NAME/catkin_ws/devel/setup.bash"
```
## RoboMaker
![image](images/aws-robomaker.png)
If a you are unable to dual boot and install ubuntu on your local machine, an alternative is to use [AWS RoboMaker](https://aws.amazon.com/robomaker/). AWS RoboMaker extends the ROS framework with cloud services. The service provides a robotics simulation service, allowing for testing of the Stretch RE1 platform. If you are a first-time user of AWS RoboMaker, follow the [guide here](https://github.com/aws-robotics/aws-robomaker-robotics-curriculum/blob/main/getting-started-with-aws-robomaker/_modules/mod-2a-aws.md) to get up and running with the service.
source ~/ament_ws/install/setup.bash"
```
**Next Tutorial:** [Gazebo Basics](gazebo_basics.md)

+ 7
- 22
internal_state_of_stretch.md View File

@ -2,16 +2,15 @@
Begin by starting up the stretch driver launch file by typing the following in a terminal.
```bash
roslaunch stretch_core stretch_driver.launch
ros2 launch stretch_core stretch_driver.launch.py
```
Then utilize the ROS command-line tool, [rostopic](http://wiki.ros.org/rostopic), to display Stretch's internal state information. For instance, to view the current state of the robot's joints, simply type the following in a terminal.
Then utilize the ROS command-line tool, ros2 topic, to display Stretch's internal state information. For instance, to view the current state of the robot's joints, simply type the following in a terminal.
```bash
rostopic echo /joint_states -n1
ros2 topic echo /stretch/joint_states
```
Note that the flag, `-n1`, at the end of the command, defines the count of how many times you wish to publish the current topic information. If you prefer to continuously print the topic for debugging purposes, then remove the flag.
Your terminal will then output the information associated with the `/joint_states` topic. Your `header`, `position`, `velocity`, and `effort` information may vary from what is printed below.
Your terminal will then output the information associated with the `/stretch/joint_states` topic. Your `header`, `position`, `velocity`, and `effort` information may vary from what is printed below.
```
header:
seq: 70999
@ -28,27 +27,12 @@ effort: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
---
```
Let's say you are interested in only seeing the `header` component of the `/joint_states` topic, you can output this within the rostopic command-line tool by typing the following command.
```bash
rostopic echo /joint_states/header -n1
```
Your terminal will then output something similar to this:
```
seq: 97277
stamp:
secs: 1945
nsecs: 562000000
frame_id: ''
```
Additionally, if you were to type `rostopic echo /` in the terminal, then press your *Tab* key on your keyboard, you will see the list of active topics being published.
Additionally, if you type `ros2 topic list` in the terminal, you will see the list of active topics being published.
A powerful tool to visualize the ROS communication is the ROS [rqt_graph package](http://wiki.ros.org/rqt_graph). You can see a graph of topics being communicated between nodes by typing the following.
A powerful tool to visualize the ROS communication is through the rqt_graph package. You can see a graph of topics being communicated between nodes by typing the following.
```
rqt_graph
ros2 run rqt_graph rqt_graph
```
![image](images/rqt_graph.png)

+ 9
- 5
moveit_basics.md View File

@ -14,17 +14,21 @@ roslaunch stretch_moveit_config moveit_rviz.launch
``` -->
## MoveIt! Without Hardware
To begin running MoveIt! on stretch, run the demo launch file. This doesn't require any simulator or robot to run.
To begin running MoveIt! on stretch, checkout to the feature/hybrid_planning branch and run the demo launch file.
```bash
roslaunch stretch_moveit_config demo.launch
cd ~/ament_ws/src/stretch_ros2
git checkout feature/hybrid_planning
cd ~/ament_ws
colcon build
ros2 launch stretch_moveit_config movegroup_moveit2.launch.py
```
This will brining up an RViz instance where you can move the robot around using [interactive markers](http://wiki.ros.org/rviz/Tutorials/Interactive%20Markers%3A%20Getting%20Started) and create plans between poses. You can reference the bottom gif as a guide to plan and execute motion.
![image](images/moveit.gif)
Additionally, the demo allows a user to select from the three groups, *stretch_arm*, *stretch_gripper*, *stretch_head* to move. The option to change groups in the in *Planning Request* section in the *Displays* tree. A few notes to be kept in mind:
Additionally, the demo allows a user to select from the five groups, *stretch_arm*, *stretch_gripper*, *stretch_head*, *mobile_base_arm* and *position* to move. The option to change groups in the in *Planning Request* section in the *Displays* tree. A few notes to be kept in mind:
* Pre-defined start and goal states can be specified in Start State and Goal State drop downs in Planning tab of Motion Planning RViz plugin.
@ -39,7 +43,7 @@ Additionally, the demo allows a user to select from the three groups, *stretch_a
## Running Gazebo with MoveIt! and Stretch
```bash
<!-- ```bash
# Terminal 1:
roslaunch stretch_gazebo gazebo.launch
# Terminal 2:
@ -52,6 +56,6 @@ roslaunch stretch_moveit_config demo_gazebo.launch
This will launch an Rviz instance that visualizes the joints with markers and an empty world in Gazebo with Stretch and load all the controllers. There are pre-defined positions for each joint group for demonstration purposes. There are three joint groups, namely stretch_arm, stretch_gripper and stretch_head that can be controlled individually via Motion Planning Rviz plugin. Start and goal positions for joints can be selected similar to [this moveit tutorial](https://ros-planning.github.io/moveit_tutorials/doc/quickstart_in_rviz/quickstart_in_rviz_tutorial.html#choosing-specific-start-goal-states).
![image](images/gazebo_moveit.gif)
![image](images/gazebo_moveit.gif) -->
**Next Tutorial:** [Follow Joint Trajectory Commands](follow_joint_trajectory.md)

+ 5
- 0
navigation_stack.md View File

@ -1,5 +1,10 @@
## Navigation Stack with Actual robot
### NOTE
Nav 2 support for Stretch in ROS 2 is under active development. Please reach out to us if you want to use Nav 2 with Stretch in ROS 2.
Refer to the instructions below if you want to test this functionality in ROS 1.
stretch_navigation provides the standard ROS navigation stack as two launch files. This package utilizes gmapping, move_base, and AMCL to drive the stretch RE1 around a mapped space. Running this code will require the robot to be untethered.

+ 6
- 4
rviz_basics.md View File

@ -3,22 +3,24 @@
You can utilize RViz to visualize Stretch's sensor information. To begin, run the stretch driver launch file.
```bash
roslaunch stretch_core stretch_driver.roslaunch
ros2 launch stretch_core stretch_driver.launch.py
```
<!-- TODO: Make this rviz config file available to users in the main branch -->
Then run the following command to bring up a simple RViz configuration of the Stretch robot.
```bash
rosrun rviz rviz -d `rospack find stretch_core`/rviz/stretch_simple_test.rviz
ros2 run rviz2 rviz2 -d `ros2 pkg prefix stretch_calibration`/rviz/stretch_simple_test.rviz
```
An RViz window should open, allowing you to see the various DisplayTypes in the display tree on the left side of the window.
![image](images/simple_rviz.png)
If you want the visualize Stretch's [tf transform tree](http://wiki.ros.org/rviz/DisplayTypes/TF), you need to add the display type to the RViz window. First, click on the *Add* button and include the *TF* type to the display. You will then see all of the transform frames of the Stretch robot and the visualization can be toggled off and on by clicking the checkbox next to the tree. Below is a gif for reference.
If you want to visualize Stretch's [tf transform tree](http://wiki.ros.org/rviz/DisplayTypes/TF), you need to add the display type to the RViz window. First, click on the *Add* button and include the *TF* type to the display. You will then see all of the transform frames of the Stretch robot and the visualization can be toggled off and on by clicking the checkbox next to the tree. Below is a gif for reference.
![image](images/rviz_adding_tf.gif)
There are further tutorials for RViz and can be found [here](http://wiki.ros.org/rviz/Tutorials).
TODO: Add the correct link for working with rviz2 in ROS 2
There are further tutorials for RViz that can be found [here](http://wiki.ros.org/rviz/Tutorials).
## Running RViz and Gazebo

+ 5
- 1
teleoperating_stretch.md View File

@ -1,11 +1,15 @@
## Teleoperating Stretch
### NOTE
Teleoperation support for Stretch in ROS 2 is under active development. Please reach out to us if you want to teleoperate Stretch in ROS 2.
Refer to the instructions below if you want to test this functionality in ROS 1.
### Xbox Controller Teleoperating
![image](images/xbox_controller_commands.png)
Stretch comes ready to run out of the box. The Xbox Teleoperation demo will let you quickly test out the robot capabilities by teleoperating it with an Xbox Controller.
Note: Make sure the USB Dongle is plugged into the the USB port of the base trunk.
To start the demo:

Loading…
Cancel
Save