Skip to content

Commit ce3655a

Browse files
author
Emiliano Borghi
committed
Add square test
1 parent ad1c848 commit ce3655a

File tree

1 file changed

+274
-0
lines changed

1 file changed

+274
-0
lines changed

ca_node/scripts/move_square.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#!/usr/bin/python
2+
import math
3+
import rospy as ros
4+
import sys
5+
import time
6+
7+
from geometry_msgs.msg import Twist, Pose
8+
from nav_msgs.msg import Odometry
9+
from tf.transformations import euler_from_quaternion, quaternion_from_euler
10+
11+
12+
__author__ = "Gabriel Urbain"
13+
__copyright__ = "Copyright 2018, IDLab, UGent"
14+
15+
__license__ = "MIT"
16+
__version__ = "1.0"
17+
__maintainer__ = "Gabriel Urbain"
18+
__email__ = "[email protected]"
19+
__status__ = "Education"
20+
__date__ = "October 15th, 2018"
21+
22+
23+
class SquareMove(object):
24+
"""
25+
This class is an abstract class to control a square trajectory on the turtleBot.
26+
It mainly declare and subscribe to ROS topics in an elegant way.
27+
"""
28+
29+
def __init__(self):
30+
31+
# Declare ROS subscribers and publishers
32+
self.node_name = "square_move"
33+
self.odom_sub_name = "/create1/odom"
34+
self.vel_pub_name = "/create1/cmd_vel"
35+
self.vel_pub = None
36+
self.odometry_sub = None
37+
38+
# ROS params
39+
self.pub_rate = 0.1
40+
self.queue_size = 2
41+
42+
# Variables containing the sensor information that can be used in the main program
43+
self.odom_pose = None
44+
45+
def start_ros(self):
46+
47+
# Create a ROS node with a name for our program
48+
ros.init_node(self.node_name, log_level=ros.INFO)
49+
50+
# Define a callback to stop the robot when we interrupt the program (CTRL-C)
51+
ros.on_shutdown(self.stop_robot)
52+
53+
# Create the Subscribers and Publishers
54+
self.odometry_sub = ros.Subscriber(self.odom_sub_name, Odometry, callback=self.__odom_ros_sub, queue_size=self.queue_size)
55+
self.vel_pub = ros.Publisher(self.vel_pub_name, Twist, queue_size=self.queue_size)
56+
57+
def stop_robot(self):
58+
59+
# Get the initial time
60+
self.t_init = time.time()
61+
62+
# We publish for a second to be sure the robot receive the message
63+
while time.time() - self.t_init < 1 and not ros.is_shutdown():
64+
65+
self.vel_ros_pub(Twist())
66+
time.sleep(self.pub_rate)
67+
68+
sys.exit("The process has been interrupted by the user!")
69+
70+
def move(self):
71+
""" To be surcharged in the inheriting class"""
72+
73+
while not ros.is_shutdown():
74+
time.sleep(1)
75+
76+
def __odom_ros_sub(self, msg):
77+
78+
self.odom_pose = msg.pose.pose
79+
80+
def vel_ros_pub(self, msg):
81+
82+
self.vel_pub.publish(msg)
83+
84+
85+
class SquareMoveVel(SquareMove):
86+
"""
87+
This class implements a open-loop square trajectory based on velocity control. HOWTO:
88+
- Start the roscore (on the computer or the robot, depending on your configuration)
89+
$ roscore
90+
- Start the sensors on the turtlebot:
91+
$ roslaunch turtlebot3_bringup turtlebot3_robot.launch
92+
- Start this node on your computer:
93+
$ python move_square vel
94+
"""
95+
96+
def __init__(self, dir):
97+
98+
super(SquareMoveVel, self).__init__()
99+
self.dir = -1.0 if (dir == "right") else 1.0
100+
101+
def go_forward(self, duration, speed):
102+
103+
# Get the initial time
104+
self.t_init = time.time()
105+
106+
# Set the velocity forward and wait (do it in a while loop to keep publishing the velocity)
107+
while time.time() - self.t_init < duration and not ros.is_shutdown():
108+
109+
msg = Twist()
110+
msg.linear.x = speed
111+
msg.angular.z = 0
112+
self.vel_ros_pub(msg)
113+
time.sleep(self.pub_rate)
114+
115+
def turn(self, duration, ang_speed):
116+
117+
# Get the initial time
118+
self.t_init = time.time()
119+
120+
# Set the velocity forward and wait 2 sec (do it in a while loop to keep publishing the velocity)
121+
while time.time() - self.t_init < duration and not ros.is_shutdown():
122+
123+
msg = Twist()
124+
msg.linear.x = 0
125+
msg.angular.z = ang_speed
126+
self.vel_ros_pub(msg)
127+
time.sleep(self.pub_rate)
128+
129+
def move(self):
130+
131+
self.go_forward(2, 0.33)
132+
self.turn(self.dir*3.5, 0.33)
133+
self.go_forward(2, 0.33)
134+
self.turn(self.dir*3.5, 0.33)
135+
self.go_forward(2, 0.33)
136+
self.turn(self.dir*3.5, 0.33)
137+
self.go_forward(2, 0.33)
138+
self.stop_robot()
139+
140+
141+
class SquareMoveOdom(SquareMove):
142+
"""
143+
This class implements a semi closed-loop square trajectory based on relative position control,
144+
where only odometry is used. HOWTO:
145+
- Start the roscore (on the computer or the robot, depending on your configuration)
146+
$ roscore
147+
- Start the sensors on the turtlebot:
148+
$ roslaunch turtlebot3_bringup turtlebot3_robot.launch
149+
- Start this node on your computer:
150+
$ python move_square odom
151+
"""
152+
153+
def __init__(self):
154+
155+
156+
super(SquareMoveOdom, self).__init__()
157+
158+
self.pub_rate = 0.1
159+
160+
def get_z_rotation(self, orientation):
161+
162+
(roll, pitch, yaw) = euler_from_quaternion([orientation.x, orientation.y, orientation.z, orientation.w])
163+
print (roll, pitch, yaw)
164+
return yaw
165+
166+
def move_of(self, d, speed=0.5):
167+
168+
x_init = self.odom_pose.position.x
169+
y_init = self.odom_pose.position.y
170+
171+
# Set the velocity forward until distance is reached
172+
while math.sqrt((self.odom_pose.position.x - x_init)**2 + \
173+
(self.odom_pose.position.y - y_init)**2) < d and not ros.is_shutdown():
174+
175+
sys.stdout.write("\r [MOVE] The robot has moved of {:.2f}".format(math.sqrt((self.odom_pose.position.x - x_init)**2 + \
176+
(self.odom_pose.position.y - y_init)**2)) + "m over " + str(d) + "m")
177+
sys.stdout.flush()
178+
179+
msg = Twist()
180+
msg.linear.x = speed
181+
msg.angular.z = 0
182+
self.vel_ros_pub(msg)
183+
time.sleep(self.pub_rate)
184+
185+
sys.stdout.write("\n")
186+
187+
def turn_of(self, a, ang_speed=0.4):
188+
189+
# Convert the orientation quaternion message to Euler angles
190+
a_init = self.get_z_rotation(self.odom_pose.orientation)
191+
print (a_init)
192+
193+
# Set the angular velocity forward until angle is reached
194+
while (self.get_z_rotation(self.odom_pose.orientation) - a_init) < a and not ros.is_shutdown():
195+
196+
# sys.stdout.write("\r [TURN] The robot has turned of {:.2f}".format(self.get_z_rotation(self.odom_pose.orientation) - \
197+
# a_init) + "rad over {:.2f}".format(a) + "rad")
198+
# sys.stdout.flush()
199+
# print (self.get_z_rotation(self.odom_pose.orientation) - a_init)
200+
201+
msg = Twist()
202+
msg.angular.z = ang_speed
203+
msg.linear.x = 0
204+
self.vel_ros_pub(msg)
205+
time.sleep(self.pub_rate)
206+
207+
sys.stdout.write("\n")
208+
209+
def move(self):
210+
211+
# Wait that our python program has received its first messages
212+
while self.odom_pose is None and not ros.is_shutdown():
213+
time.sleep(0.1)
214+
215+
# Implement main instructions
216+
self.move_of(0.33)
217+
self.turn_of(math.pi/4)
218+
self.move_of(0.33)
219+
self.turn_of(math.pi/4)
220+
self.move_of(0.33)
221+
self.turn_of(math.pi/4)
222+
self.move_of(0.33)
223+
self.stop_robot()
224+
225+
226+
class SquareMoveOdomIMU(SquareMoveOdom):
227+
"""
228+
This class implements a semi closed-loop square trajectory based on relative position control,
229+
where only odometry is used. HOWTO:
230+
- Start the roscore (on the computer or the robot, depending on your configuration)
231+
$ roscore
232+
- Start the sensors on the turtlebot:
233+
$ roslaunch turtlebot3_bringup turtlebot3_robot.launch
234+
- Start a node for sensors fusion the turtleot (IMU + Odometry are now merged in a new odometry message):
235+
$ roslaunch robot_pose_ekf robot_pose_ekf.launch imu_used:=true odom_used:=true vo_used:=false
236+
- Start this node on your computer:
237+
$ python move_square odom
238+
"""
239+
240+
def __init__(self):
241+
242+
243+
# The functions are the same as in the previous example except for the odometry name that is now filtered
244+
# in a, Extended Kalman Filter (EKF) with the IMU values thanks to the node robot_pose_ekf
245+
super(SquareMoveOdomIMU, self).__init__()
246+
self.odom_sub_name = "/create1/odom_combined"
247+
248+
249+
if __name__ == '__main__':
250+
251+
# Choose the example you need to run in the command line
252+
if len(sys.argv) > 1:
253+
254+
if sys.argv[1] == "vel":
255+
256+
dir = "right" if (len(sys.argv) > 2 and sys.argv[2] == "right") else "left"
257+
258+
r = SquareMoveVel(dir)
259+
260+
elif sys.argv[1] == "odom":
261+
r = SquareMoveOdom()
262+
263+
elif sys.argv[1] == "odom_imu":
264+
r = SquareMoveOdomIMU()
265+
266+
else:
267+
sys.exit(-1)
268+
269+
else:
270+
sys.exit(-1)
271+
272+
# Listen and Publish to ROS + execute moving instruction
273+
r.start_ros()
274+
r.move()

0 commit comments

Comments
 (0)