Skip to content

Commit fed4ae4

Browse files
ldaniaIsabelParedes
authored andcommitted
Cicular Movement
1 parent 5148ba5 commit fed4ae4

File tree

3 files changed

+186
-58
lines changed

3 files changed

+186
-58
lines changed

jupyros/ros2/turtle_sim.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import os
2+
import time
3+
import math
4+
import random
5+
6+
import ipycanvas
7+
import ipywidgets
8+
9+
from ament_index_python.packages import get_package_share_directory
10+
11+
12+
class TurtleSim:
13+
def __init__(self, width=1600, height=1600, turtle_size=100, background_color="#4556FF"):
14+
self.turtles = {}
15+
self.turtle_size = turtle_size
16+
self.canvas_middle = {"x": width // 2,
17+
"y": height // 2,
18+
"theta": 0}
19+
20+
# Three layers for the canvas: 0-background, 1-paths, 2-turtles
21+
self.canvas = ipycanvas.MultiCanvas(3,
22+
width=width, height=height,
23+
layout={"width": "100%"})
24+
25+
# Water background
26+
self.canvas[0].fill_style = background_color
27+
self.canvas[0].fill_rect(0, 0, width, height)
28+
29+
# Turtle path width
30+
self.canvas[1].line_width = 8
31+
32+
self.last_move_time = time.time()
33+
self.spawn()
34+
35+
def spawn(self, name=None, pose=None):
36+
37+
if (name is None) or (name in self.turtles.keys()):
38+
name = "turtle" + str(len(self.turtles) + 1)
39+
40+
self.turtles[name] = self.Turtle(name, self.turtle_size)
41+
42+
if pose is None:
43+
# Spawn to middle of canvas
44+
self.turtles[name].pose = self.canvas_middle
45+
else:
46+
self.turtles[name].pose = pose
47+
48+
with ipycanvas.hold_canvas(self.canvas):
49+
self.draw_turtle(name)
50+
51+
print(name + " has spawned.")
52+
53+
def move_turtles(self, new_poses):
54+
elapsed_time = time.time() - self.last_move_time
55+
56+
57+
58+
if elapsed_time > 0.08: # seconds
59+
self.last_move_time = time.time()
60+
61+
with ipycanvas.hold_canvas(self.canvas):
62+
self.canvas[2].clear()
63+
64+
for name in self.turtles.keys():
65+
# Draw line path
66+
self.canvas[1].stroke_style = self.turtles[name].path_color
67+
self.canvas[1].stroke_line(self.turtles[name].pose["x"],
68+
self.turtles[name].pose["y"],
69+
new_poses[name]["x"],
70+
new_poses[name]["y"])
71+
# Update
72+
self.turtles[name].pose["x"] = new_poses[name]["x"]
73+
self.turtles[name].pose["y"] = new_poses[name]["y"]
74+
self.turtles[name].pose["theta"] = new_poses[name]["theta"]
75+
76+
77+
78+
self.draw_turtle(name)
79+
80+
def draw_turtle(self, name="turtle1", n=2):
81+
# Offsets for turtle center and orientation
82+
x_offset = - self.turtle_size / 2
83+
y_offset = - self.turtle_size / 2
84+
theta_offset = self.turtles[name].pose["theta"] - math.radians(90) # to face right side
85+
86+
# Transform canvas
87+
self.canvas[n].save()
88+
self.canvas[n].translate(self.turtles[name].pose["x"], self.turtles[name].pose["y"])
89+
self.canvas[n].rotate(-theta_offset)
90+
91+
self.canvas[n].draw_image(self.turtles[name].canvas,
92+
x_offset, y_offset,
93+
self.turtle_size)
94+
95+
# Revert transformation
96+
self.canvas[n].restore()
97+
98+
class Turtle:
99+
def __init__(self, name, size=100):
100+
self.name = name
101+
self.size = size
102+
self.canvas = None
103+
self.randomize()
104+
self.pose = {"x": 0,
105+
"y": 0,
106+
"theta": 0}
107+
self.path_color = '#B3B8FF' # Light blue
108+
109+
def randomize(self):
110+
img_path = str(get_package_share_directory("turtlesim")) + "/images/"
111+
images = os.listdir(img_path)
112+
turtle_pngs = [img for img in images if ('.png' in img and 'palette' not in img)]
113+
random_png = turtle_pngs[random.randint(0, len(turtle_pngs) - 1)]
114+
turtle_img = ipywidgets.Image.from_file(img_path + random_png)
115+
turtle_canvas = ipycanvas.Canvas(width=self.size, height=self.size)
116+
117+
with ipycanvas.hold_canvas(turtle_canvas):
118+
turtle_canvas.draw_image(turtle_img, 0, 0, self.size)
119+
120+
time.sleep(0.1) # Drawing time
121+
self.canvas = turtle_canvas
122+
123+
return self

notebooks/ROS2_Turtlesim.ipynb

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
"import jupyros.ros2.turtle_sim as turtle\n",
1313
"from turtlesim.srv import Spawn\n",
1414
"from turtlesim.msg import Pose\n",
15-
"from time import time\n",
1615
"import os\n",
1716
"from std_msgs.msg import String\n",
1817
"from geometry_msgs.msg import Twist\n",
@@ -78,7 +77,7 @@
7877
{
7978
"data": {
8079
"application/vnd.jupyter.widget-view+json": {
81-
"model_id": "77f04f8ef9634ee590f4c6d02026d8b8",
80+
"model_id": "f7e9060702e74556b7652d4a783d9768",
8281
"version_major": 2,
8382
"version_minor": 0
8483
},
@@ -107,9 +106,6 @@
107106
"execution_count": 6,
108107
"id": "497db1e0-8c21-4ec0-b620-1607ab34d685",
109108
"metadata": {
110-
"jupyter": {
111-
"source_hidden": true
112-
},
113109
"tags": []
114110
},
115111
"outputs": [
@@ -147,7 +143,7 @@
147143
{
148144
"data": {
149145
"application/vnd.jupyter.widget-view+json": {
150-
"model_id": "2aff5baab18c49bb8af3faed876a372a",
146+
"model_id": "9a96ef62b2b440e0a6edd111a08f2a22",
151147
"version_major": 2,
152148
"version_minor": 0
153149
},
@@ -160,23 +156,27 @@
160156
}
161157
],
162158
"source": [
163-
"topic_name = '/keyboard_stream'\n",
159+
"topic_name = '/Pose'\n",
164160
"def move_turtles(msg):\n",
165-
" scale = 1\n",
161+
" scale = 0.0015\n",
166162
" name = \"turtle1\"\n",
167-
"\n",
168-
" poses[name] = {\"x\": turtlesim.turtles[name].pose[\"x\"] + msg.linear.x*math.cos(turtlesim.turtles[name].pose[\"theta\"])/scale,\n",
169-
" \"y\": turtlesim.turtles[name].pose[\"y\"] - msg.linear.x*math.sin(turtlesim.turtles[name].pose[\"theta\"] )/scale,\n",
170-
" \"theta\": turtlesim.turtles[name].pose[\"theta\"] + msg.angular.z/ 180 * math.pi}\n",
171163
" \n",
164+
" def angle_slope():\n",
165+
" d_y = (math.sin(msg.theta+1/180)*math.cos(msg.theta+1/180)-math.sin(msg.theta)*math.cos(msg.theta))\n",
166+
" d_x = (math.cos(msg.theta+1/180) - math.cos(msg.theta))\n",
167+
" return math.atan2(d_y,d_x)\n",
168+
" \n",
169+
" poses[name] = {\"x\": 1/scale * math.cos(msg.theta) + 800,\n",
170+
" \"y\": -1/scale * math.sin(msg.theta)*math.cos(msg.theta) + 800,\n",
171+
" \"theta\": angle_slope()}\n",
172+
" ##msg.theta - math.atan2((-1/scale * math.sin(msg.theta)*math.cos(msg.theta) + 800),(1/scale * math.cos(msg.theta) + 800))\n",
172173
" \n",
173174
" turtlesim.move_turtles(new_poses=poses)\n",
174175
"\n",
175176
"\n",
176177
"\n",
177178
"\n",
178179
"def cb(msg):\n",
179-
" print(msg)\n",
180180
" move_turtles(msg)\n",
181181
"\n",
182182
"\n",
@@ -193,7 +193,7 @@
193193
{
194194
"data": {
195195
"application/vnd.jupyter.widget-view+json": {
196-
"model_id": "1f92a4ad257146fca78c363671b8181b",
196+
"model_id": "b9d3b46917c34d3097c23523095f14df",
197197
"version_major": 2,
198198
"version_minor": 0
199199
},
@@ -208,26 +208,17 @@
208208
"source": [
209209
"%%thread_cell2\n",
210210
"run = True\n",
211-
"i = 0\n",
211+
"i = 90\n",
212212
"pub = jr2.Publisher(moveNode, Pose, topic_name)\n",
213213
"\n",
214214
"while run:\n",
215215
" msg = Pose()\n",
216-
" msg.x = (i/1800*5)*math.sin(i / 180 * math.pi) + 11.08 / 2\n",
217-
" msg.y = (i/1800*5)*math.cos(i / 180 * math.pi) + 11.08 / 2\n",
218-
" msg.theta = - i / 180 * math.pi\n",
219-
" pub.send_msg(msg)\n",
216+
" msg.theta = i / 180 * math.pi\n",
217+
" pub.send_msg( msg)\n",
218+
" sleep(0.01)\n",
220219
" i += 1\n",
221220
"print(\"Done\")"
222221
]
223-
},
224-
{
225-
"cell_type": "code",
226-
"execution_count": null,
227-
"id": "606db6b1-f505-41b7-99d1-525b09cf662e",
228-
"metadata": {},
229-
"outputs": [],
230-
"source": []
231222
}
232223
],
233224
"metadata": {

notebooks/ROS2_Turtlesim_KeyboardControl.ipynb

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
{
6060
"data": {
6161
"application/vnd.jupyter.widget-view+json": {
62-
"model_id": "fc8ee6a25ba64a3bb7d88184717a5672",
62+
"model_id": "33726261d3ca48cfb41d862e9386ed5e",
6363
"version_major": 2,
6464
"version_minor": 0
6565
},
@@ -73,7 +73,7 @@
7373
{
7474
"data": {
7575
"application/vnd.jupyter.widget-view+json": {
76-
"model_id": "4b231f376d4145c3a3086ae7ea55bb62",
76+
"model_id": "62f02b8ea884432a85024d49449446dd",
7777
"version_major": 2,
7878
"version_minor": 0
7979
},
@@ -120,7 +120,7 @@
120120
{
121121
"data": {
122122
"application/vnd.jupyter.widget-view+json": {
123-
"model_id": "d4a4365909444d9495776ffc4f282e02",
123+
"model_id": "32c59818a7cd4fdfa129eca3b0cb238f",
124124
"version_major": 2,
125125
"version_minor": 0
126126
},
@@ -137,18 +137,6 @@
137137
"display(turtlesim.canvas)"
138138
]
139139
},
140-
{
141-
"cell_type": "code",
142-
"execution_count": null,
143-
"id": "347b508d-4568-4cc2-b8da-c05f26b6f606",
144-
"metadata": {
145-
"tags": []
146-
},
147-
"outputs": [],
148-
"source": [
149-
"!jupyter labextension install js"
150-
]
151-
},
152140
{
153141
"cell_type": "markdown",
154142
"id": "ece1ece3-54f6-4df8-be79-42d7f37f6e08",
@@ -159,10 +147,18 @@
159147
},
160148
{
161149
"cell_type": "code",
162-
"execution_count": null,
150+
"execution_count": 7,
163151
"id": "497db1e0-8c21-4ec0-b620-1607ab34d685",
164152
"metadata": {},
165-
"outputs": [],
153+
"outputs": [
154+
{
155+
"name": "stdout",
156+
"output_type": "stream",
157+
"text": [
158+
"{'x': 800, 'y': 800, 'theta': 0}\n"
159+
]
160+
}
161+
],
166162
"source": [
167163
"poses = {}\n",
168164
"\n",
@@ -174,20 +170,46 @@
174170
},
175171
{
176172
"cell_type": "code",
177-
"execution_count": null,
173+
"execution_count": 8,
178174
"id": "ac63dbbb-b388-4b18-890c-e3bcada044a9",
179175
"metadata": {},
180-
"outputs": [],
176+
"outputs": [
177+
{
178+
"data": {
179+
"text/plain": [
180+
"{'x': 800, 'y': 800, 'theta': 0}"
181+
]
182+
},
183+
"execution_count": 8,
184+
"metadata": {},
185+
"output_type": "execute_result"
186+
}
187+
],
181188
"source": [
182189
"turtlesim.turtles[name].pose"
183190
]
184191
},
185192
{
186193
"cell_type": "code",
187-
"execution_count": null,
194+
"execution_count": 9,
188195
"id": "cd2e66dc",
189196
"metadata": {},
190-
"outputs": [],
197+
"outputs": [
198+
{
199+
"data": {
200+
"application/vnd.jupyter.widget-view+json": {
201+
"model_id": "7806c8d4a3f04f5f8f6f64d0043ca113",
202+
"version_major": 2,
203+
"version_minor": 0
204+
},
205+
"text/plain": [
206+
"VBox(children=(HBox(children=(Button(description='Start', style=ButtonStyle()), Button(description='Stop', sty…"
207+
]
208+
},
209+
"metadata": {},
210+
"output_type": "display_data"
211+
}
212+
],
191213
"source": [
192214
"topic_name = '/keyboard_stream'\n",
193215
"def move_turtles(msg):\n",
@@ -255,18 +277,10 @@
255277
},
256278
{
257279
"cell_type": "code",
258-
"execution_count": 5,
280+
"execution_count": null,
259281
"id": "c0cd916b-41a7-4832-9470-328c1d30689b",
260282
"metadata": {},
261-
"outputs": [
262-
{
263-
"name": "stderr",
264-
"output_type": "stream",
265-
"text": [
266-
"UsageError: %%thread_cell is a cell magic, but the cell body is empty.\n"
267-
]
268-
}
269-
],
283+
"outputs": [],
270284
"source": []
271285
},
272286
{

0 commit comments

Comments
 (0)