Skip to content

Commit 1546163

Browse files
authored
Merge pull request #121 from Open-STEM/ansel-58
Locating Nearby Object
2 parents c467859 + 5652c2f commit 1546163

8 files changed

Lines changed: 210 additions & 39 deletions

File tree

course/measuring_distance/locate_object.rst

Lines changed: 210 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,75 +5,246 @@ Another way to utilize the ultrasonic rangefinder is to use it to locate a nearb
55

66
.. admonition:: Try it out
77

8-
Before reading more, brainstorm different ways to use the ultrasonic rangefinder to locate a nearby object.
8+
What's the most effective way to locate an object? Try it out!
99

10-
How?
11-
~~~~
10+
Turn and Detect
11+
~~~~~~~~~~~~~~~
1212

13-
The easiest way to explain the intuition behind this process is sonar.
13+
To first detect an object, the robot can slowly spin in a circle while continuously polling the rangefinder.
14+
Then, when an object is detected, it can stop spinning, and head towards the object.
1415

15-
The robot will essentially spin in a circle and take a distance reading while it spins.
16+
How can we tell when an object is detected? Imagine that the robot is in the center of an empty room, and a
17+
random object is placed somewhere near the robot. The rangefinder would be giving large distance readings, until
18+
it reaches the object, at which point the distance reading would drop. It's the *change* in distance readings that
19+
hints that an object has been detected.
1620

17-
Then, when an object is detected to be within a certain distance, the robot will stop spinning and go towards the object.
21+
How can we find the change in distance readings over each iteration of the loop? We can store the previous distance
22+
reading in a variable, and compare it to the current distance reading. If this change is greater than some threshold,
23+
then we can assume that an object has been detected.
1824

19-
This is a very simple way to locate an object, but it is also very effective (and will be especially helpful during your final project).
25+
To code this, we can start by setting the drive motor speeds to spin in opposite directions to start spinning
26+
the robot in place. Then, once the change in distance reading is greater than
27+
the threshold, the robot can stop spinning and head towards the object.
2028

21-
The First Step
22-
~~~~~~~~~~~~~~
29+
In the following example code, we use a change threshold of 30 cm.
2330

24-
The first step is to spin the robot in a circle and stop when an object is detected.
31+
.. tab-set::
2532

26-
To do this, utilize a while loop which instucts the robot to spin in a circle while the distance reading is greater than a certain value.
33+
.. tab-item:: Python
2734

28-
In the following example code, our "distance threshold" is 20 cm.
35+
.. code-block:: python
2936
30-
.. error::
37+
changeThreshold = 30 # distance change in cm needed to trigger detection
3138
32-
TODO insert a video and code of a working example
39+
# store initial value for current distance
40+
currentDistance = rangefinder.distance()
3341
34-
The Second Step
35-
~~~~~~~~~~~~~~~
42+
# start spinning in place until an object is detected
43+
drivetrain.set_speed(5, -5)
44+
45+
while True: # doesn't actually repeat forever. loop will be broken if an object is detected
46+
47+
# update previous and current distance
48+
previousDistance = currentDistance
49+
currentDistance = rangefinder.distance()
50+
51+
# if sudden decrease in distance, then an object has been detected
52+
if previousDistance - currentDistance > changeThreshold:
53+
break # break out of the while loop
54+
55+
time.sleep(0.1)
56+
57+
# stop spinning drive motors
58+
drivetrain.stop()
59+
60+
61+
.. tab-item:: Blockly
62+
63+
.. image:: media/detection.png
64+
:width: 900
65+
66+
Improving Accuracy
67+
~~~~~~~~~~~~~~~~~~
68+
69+
Do you see any issues with this solution?
70+
71+
When the rangefinder distance dips below the threshold, the implication isn't that the robot has found an object,
72+
but rather that the robot has found its *edge*. So, the robot will aim for the edge of the object, rather than the center.
73+
74+
Instead, the robot should remember the heading it faces when detecting *both* edges. Then, the robot can aim for the center
75+
between those edges, and thus the center of the object. We can store each edge angle in variables, naming them :code:`firstAngle`
76+
and :code:`secondAngle`.
77+
78+
We're already quite familiar with turning until an edge is detected. Now, we'll need to detect *both* edges. However, it would be
79+
quite unwieldy and error-prone to just copy the edge detection code, so let's make a function to generalize this. Note that the existing
80+
code detects a sudden *decrease* in distance, but we want to handle sudden *increases* in distances too.
81+
82+
How can we support both behaviors in a single function? We can pass in a parameter to specify whether we want to detect an increase
83+
or decrease in distance! We can call this parameter :code:`isIncrease` and pass in a boolean (true or false) value.
84+
85+
If :code:`increase` is :code:`True`, then we want to detect an increase in distance, which is when :code:`currentDistance - previousDistance > changeThreshold`.
86+
87+
If :code:`increase` is :code:`False`, then we want to detect a decrease in distance, which is when :code:`previousDistance - currentDistance > changeThreshold`.
88+
89+
For more flexibility, let's add a parameter for the change threshold.
90+
91+
Here's the function definition:
92+
93+
.. tab-set::
94+
95+
.. tab-item:: Python
96+
97+
.. code-block:: python
98+
99+
def turnUntilEdge(isIncrease, changeThreshold):
100+
101+
# store initial value for current distance
102+
currentDistance = rangefinder.distance()
103+
104+
# start spinning in place until an object is detected
105+
drivetrain.set_speed(5, -5)
106+
107+
while True: # doesn't actually repeat forever. loop will be broken if an object is detected
108+
109+
# update previous and current distance
110+
previousDistance = currentDistance
111+
currentDistance = rangefinder.distance()
112+
113+
if isIncrease and currentDistance - previousDistance > changeThreshold:
114+
# if sudden increase in distance, then an object has been detected
115+
break
116+
elif not isIncrease and previousDistance - currentDistance > changeThreshold:
117+
# if sudden decrease in distance, then an object has been detected
118+
break
119+
120+
time.sleep(0.1)
121+
122+
# stop spinning drive motors
123+
drivetrain.stop()
124+
125+
126+
.. tab-item:: Blockly
127+
128+
.. image:: media/detectiondefinition.png
129+
:width: 900
130+
131+
Here's the equivalent function call to the turn and detection code in the previous section:
132+
133+
.. tab-set::
134+
135+
.. tab-item:: Python
136+
137+
.. code-block:: python
138+
139+
turnUntilEdge(False, 30)
140+
141+
.. tab-item:: Blockly
142+
143+
.. image:: media/detectioncall.png
144+
:width: 200
145+
146+
Now, it's time to write the full program to detect both edges and turn to the center.
147+
148+
Implementing Dual Edge Detection
149+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
150+
151+
Let's walk through each step of the process in code.
152+
153+
First, the robot should spin in place until it detects the first edge, then stop. This is simply the function call we saw earlier.
154+
155+
.. tab-set::
156+
157+
.. tab-item:: Python
158+
159+
.. code-block:: python
160+
161+
turnUntilEdge(False, 30)
162+
163+
.. tab-item:: Blockly
164+
165+
.. image:: media/detectioncall.png
166+
:width: 200
167+
168+
Next, we want to record the robot's heading for this first edge, and store it to :code:`firstAngle`.
169+
170+
.. tab-set::
171+
172+
.. tab-item:: Python
173+
174+
.. code-block:: python
175+
176+
firstAngle = imu.get_yaw()
177+
178+
.. tab-item:: Blockly
179+
180+
.. image:: media/firstangle.png
181+
:width: 200
182+
183+
Then, the robot should spin in place again until it detects the second edge, which is when there is a sudden increase in distance.
184+
185+
.. tab-set::
186+
187+
.. tab-item:: Python
188+
189+
.. code-block:: python
190+
191+
turnUntilEdge(True, 30)
192+
193+
.. tab-item:: Blockly
194+
195+
.. image:: media/turntoedgetrue.png
196+
:width: 200
36197

37-
The second step is to go towards the object and stop when the object is within a certain distance.
198+
Once the robot has detected the second edge, it should record its heading and store it to :code:`secondAngle`. Now, need to figure
199+
out how much the robot needs to backtrack to aim for the center of the object. We can do this by finding the difference between
200+
the two angles, and dividing by two. This is half the angle between the two edges, and if the robot backtracks by this amount,
201+
it will be facing the center of the object. Let's store this in a variable called :code:`angleToTurn`.
38202

39-
To do this, integrate the code from the previous example and add a while loop which instructs the robot to move forward while the distance reading is greater than a certain value.
203+
.. tab-set::
40204

41-
In the following example code, our "distance threshold" is 5 cm.
205+
.. tab-item:: Python
42206

43-
.. error::
207+
.. code-block:: python
44208
45-
TODO insert a video and code of a working example
209+
secondAngle = imu.get_yaw()
210+
angleToTurn = (firstAngle - secondAngle) / 2
46211
47-
The Problem with this Method
48-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
212+
.. tab-item:: Blockly
49213

50-
The problem with this method is that it is not very accurate.
214+
.. image:: media/angletoturn.png
215+
:width: 400
51216

52-
The reason for this is that our robot is currently acting on the "edge" of the object that it sees.
217+
Finally, the robot can turn this much to face the center of the object, and head towards it.
53218

54-
This means that the robot will not be able to accurately locate the center of the object.
219+
Here's the full code. Note that half-second pauses are added to make the robot's actions more visible:
55220

56-
The Solution
57-
~~~~~~~~~~~~
221+
.. tab-set::
58222

59-
The solution to this problem is to use "flags" to keep track of the edges of an object.
223+
.. tab-item:: Python
60224

61-
In this case, we can use a "first edge" flag and a "second edge" flag.
225+
.. code-block:: python
62226
63-
The first edge flag will be set to true when the robot first detects a sudden decrease in distance measurements (i.e. the robot detects the first edge of an object)
227+
# turn to first edge
228+
turnUntilEdge(False, 30)
64229
65-
When the first edge is set to be true, the robot will take note of that angle, let's call it "firstAngle"
230+
# store angle at first edge
231+
firstAngle = imu.get_yaw()
66232
67-
The second edge flag will be set to true when the robot detects a sudden increase in distance measurements (i.e. the robot detects the second edge of an object)
233+
time.sleep(0.5)
68234
69-
Now that the second edge is also set to be true, the robot can then take note of that angle, let's call it "secondAngle".
235+
# turn to second edge
236+
turnUntilEdge(True, 30)
70237
71-
We then know that the center of the object is at the angle halfway between firstAngle and secondAngle.
238+
# store angle at second edge and calculate angle to turn
239+
secondAngle = imu.get_yaw()
240+
angleToTurn = (firstAngle - secondAngle) / 2
72241
73-
To find that angle, we can take the average of firstAngle and secondAngle; and then save that angle as "centerAngle".
242+
time.sleep(0.5)
74243
75-
This method will allow the robot to accurately locate the center of an object.
244+
# turn to center of object
245+
drivetrain.turn(angleToTurn)
76246
77-
.. error::
247+
.. tab-item:: Blockly
78248

79-
TODO insert a video and code of a working example
249+
.. image:: media/fulldualedge.png
250+
:width: 400
39.3 KB
Loading
141 KB
Loading
26.1 KB
Loading
211 KB
Loading
14.4 KB
Loading
122 KB
Loading
25.2 KB
Loading

0 commit comments

Comments
 (0)