A month ago, we presented in this blog two products whose tests had proved quite successful: a small rangefinder that worked over 10m, and a small DMX512-controlled motorized spotlight. We thought we'd make a fun little project by combining them, and there's nothing like this kind of testing to see if a product lives up to its promise...
The idea is quite simple: is it easy to make a little program that drives the projector to follow someone as they move along a hallway? And a subsidiary question: does it still work if you put two sensors a short distance apart, to monitor two lanes at once, or will the sensors interfere with each other?
Situation diagram
To start with, here's a little diagram to illustrate the situation. By placing two rangefinders on the wall at the end of the hallway, we can easily measure the position of a moving person.
Diagrams of the situation, bird's eye and front views
With a little trigonometry, we can find the two angles (rotation and inclination) at which the motorized spotlight must be oriented to aim at the person.
Hardware
This project requires a DMX512 motorized projector, a Yocto-RS485-V2 to communicate with this projector, two rangefinders, and the corresponding Yoctopuce interfaces. Since in our previous test of these rangefinders we had tested both the serial and I2C interfaces, we decided to continue testing both. So we used a Yocto-Serial for one lane and a Yocto-I2C for the other.
These three interface modules are connected to a YoctoHub-Ethernet, which enables us to control everything remotely from a central server.
Devices used for the project
Software
To keep this example simple, we've implemented the software in Python.
The code starts by connecting to the YoctoHub-Ethernet and instantiating objects to communicate with the two rangefinders and the DMX512 communication interface:
errmsg = YRefParam()
if YAPI.RegisterHub("192.168.1.100", errmsg) != YAPI.SUCCESS:
sys.exit("RegisterHub error: " + errmsg.value)
# locate Yoctopuce devices
dist1 = YSensor.FindSensor("dist1")
if not dist1.isOnline():
sys.exit("RangeFinder #1 not found")
dist2 = YSensor.FindSensor("dist2")
if not dist2.isOnline():
sys.exit("RangeFinder #2 not found")
dmxPortOut = YSerialPort.FindSerialPort("DMX-OUT.serialPort")
if not dmxPortOut.isOnline():
sys.exit("No Yocto-RS485-V2 with logical name DMX-OUT found")
Rangefinders are read using change-of-value callbacks: we've created a DetectionLane class which records the position of the last detection announced by a rangefinder. An instance is created for each lane. A second class, PanTiltHead, is used to compute the geometry and send DMX512 messages to drive the head.
lane1 = DetectionLane(dist1, 900, 13300)
# lane 2 is 0.8m from head, length = 12m50
lane2 = DetectionLane(dist2, -800, 12500)
# pan tilt head is 2m70 above ground, 7m from sensors
panTiltHead = PanTiltHead(dmxPortOut, 2700, 7000)
All the main code loop has to do is wait for rangefinder measures, and periodically direct the projector to the lane on which the last event was detected:
# Wait for 20ms before receiving rangefinder measures
YAPI.Sleep(20, errmsg)
# Send a DMX message (~50 Hz)
if lane2.event is None:
if lane1.event is None:
panTiltHead.pointTo(None, None)
else:
panTiltHead.pointTo(lane1, 0)
else:
if lane1.event is None or abs(lane1.speed) < abs(lane2.speed):
panTiltHead.pointTo(lane2, 0)
else:
panTiltHead.pointTo(lane1, 0)
And here's the result:
Automatic guiding of a spotlight using a rangefinder: it works!
If you're interested in seeing the trigonometric computations or the implementation of the DetectionLane and PanTiltHead classes, you can check out the detailed code on GitHub.