SPiDER Cam Robot

by dslrdiy in Circuits > Raspberry Pi

746 Views, 4 Favorites, 0 Comments

SPiDER Cam Robot

DSC00450.JPG

Most of the Timelapses that you can see in the internet are static. It means that an enthusiast uses a camera a tripod and press record. Some drones offer you the ability to take timelapses while moving, but most of the drones only can fly for about 20 minutes so your Timelapses will be limited by this time.

Some great rigs for Timelapses are really expensive and can only work between the size of the slider of the rig, like edelkrone or ...

Most of the gimbals offers you Pivot timelapses but they will remain over the tripod you will be using.

And what about using a gimbal with a mechanism that can move the gimbal more than 1meter and work for more that 20min?

That's where the SpiDER Cam Robot comes in action!

Essentially is a zip line robot that will coordinate the shutter signal of the gimbal.

And we change the pan tilt motion to, pan tilt and displacement with more creative and gorgeous Timelapses.

Downloads

Supplies

The main part of this project is the brain a Raspberry Pi Pico W. Small and wireless controller. This is important because it will be easier to control and move the robot around.

A really capable stepper motor according to your rig. Heavier rigs will require a bigger motor I suggest down below.

And of course the main track where everything moves, the rope. Use a thick reliable Paracord rope. Remember your gimbal and your camera will be attached to it.

Supplies:

  1. Raspberry Pi Pico W
  2. Nema17 5.1 planetary gearbox stepper motor
  3. 2 NPF Sony batteries or a V Mount Battery.
  4. DC to DC cable
  5. USB Micro 5 pin male connector
  6. Shutter cable for your camera
  7. 2 Optoisolator module
  8. TC stepper motor driver
  9. Stepper motor module
  10. 2.5mm to 3.5mm adapter
  11. 3.5mm female module
  12. 3d printed parts (files down below)

Programming the Pi Pico W

tho1.png
tho2.png
tho3.png

First of all we will need to program the Pi Pico W, for this we will use Thonny, as the code is written in micropython.

You can download Thonny in the next link:

Installing it is pretty simple just click next and install.

Once installed you can plug your Pi Pico W in boot mode.

For this press the Bootsel button hold it and connect it using and micro B to USB A cable.

Then go to Thonny and right click on the bottom right menu.

Select program and select the language and model Raspberry Pi Pico W

Wait for a couple of seconds and Thonny will install Micropython in your Pi Pico W.

Once done it, download the code down below and copy it into the Raspberry Pi Pico W.

For this we will use Thonny as well.

Enter the file and copy the Lib folder and the main.py program

Note: Before copying the main.py file you can change the name of your HOTSTOP of your smartphone and the password.

And you are done.

import network
import socket
import uasyncio
import time
import random
from machine import Pin, PWM, time_pulse_us
import stepper

### You need neopixel.py in the same folder
#from neopixel import NeoPixel


### config.py should define something like: password = "mywifi_password"
#import config

##############################################################################
# 0) Configuration
##############################################################################

SSID = "HOME1235"
PASSWORD = "pass1234"

# For controlling the on-board LED
led = Pin("LED", Pin.OUT)

# Create global wlan object
wlan = network.WLAN(network.STA_IF)
wlan.active(True)

##############################################################################
# 1) Wi-Fi Connection Helpers
##############################################################################

def wifi_connect():
"""
Continuously retry Wi-Fi connection until successful.
"""
global wlan

attempt = 0
while True:
attempt += 1

if not wlan.isconnected():
print(f"Attempt {attempt}: Connecting to Wi-Fi...")
wlan.connect(SSID, PASSWORD)

# Wait up to 10 seconds in small increments to see if it connects
max_wait = 10
while max_wait > 0:
if wlan.isconnected():
print("Connected! Network config:", wlan.ifconfig())
return # Exit the function when connected
max_wait -= 1
time.sleep(1)

# If still not connected after 10s, wait a bit before retrying
print("Failed to connect. Retrying in 5 seconds...")
time.sleep(5)
else:
# Already connected
print("Already connected! Network config:", wlan.ifconfig())
return

async def wifi_monitor_task():
"""
Periodically checks if the Wi-Fi is still connected.
If not, it attempts to reconnect.
"""
while True:
if not wlan.isconnected():
print("Wi-Fi dropped; reconnecting...")
try:
wifi_connect()
except RuntimeError as e:
print("Reconnect failed:", e)
await uasyncio.sleep(10)



enm = Pin(0, Pin.OUT)
dirm = Pin(1, Pin.OUT)
stepm = Pin(2, Pin.OUT)
inshot = Pin(3, Pin.IN, Pin.PULL_UP)
outshot = Pin(4, Pin.OUT)

outshot.value(0)

s1 = stepper.Stepper(stepm,dirm,steps_per_rev=800,speed_sps=800)

s1.stop()
s1.overwrite_pos(0)
s1.target(0)
s1.speed(800)
s1.track_target()


current_duty = 0
current_pos = 0
vuelta = 1600
a_pos = 0
b_pos = 0
steps = 0
speed = 800
photos = 20
status = 0
predelay = 1
last_inshot = 1

async def test_status():
global status
global last_inshot
global predelay
while True:
if inshot.value() == 0 and last_inshot == 1 and status == 1:
print("shoot")
last_inshot = 0
#shot()
await uasyncio.sleep(0.1)
outshot.value(1)
await uasyncio.sleep(1)
outshot.value(0)
#await uasyncio.sleep(predelay)
move_step()
last_inshot = 1
await uasyncio.sleep(0.1)
def set_A():
global current_pos
global vuelta
global a_pos
a_pos = current_pos
print ("A_POS:")
print (a_pos)
def go_A():
global current_pos
global vuelta
global a_pos
global s1
s1.target(a_pos)
print ("GO_A:")
print (a_pos)
def go_B():
global current_pos
global vuelta
global b_pos
global s1
s1.target(b_pos)
print ("GO_B:")
print (b_pos)
def set_B():
global current_pos
global vuelta
global b_pos
b_pos = current_pos
print ("B_POS:")
print (b_pos)
calculate_steps()
def minus():
global speed
speed = speed - 100
if speed <= 0:
speed = 0
print ("speed:")
print (speed)
def plus():
global speed
speed = speed + 100
if speed > 6400:
speed = 6400
print ("speed:")
print (speed)

def photo_minus():
global photos
photos = photos - 5
if photos <= 0:
photos = 0
print ("photos:")
print (photos)

def photo_plus():
global photos
photos = photos + 5
if photos > 1000:
photos = 1000
print ("photos:")
print (photos)

def start():
global status
global current_pos
global a_pos
current_pos = a_pos
status = 1
print("start")
def stop():
global status
status = 0
print("stop")

def e_stop():
enm.value(1)
print ("E_Stop")
def shot():
await uasyncio.sleep(0.1)
outshot.value(1)
await uasyncio.sleep(1)
outshot.value(0)

def move_left():
global current_pos
global vuelta
current_pos = current_pos + vuelta
s1.target(current_pos)
print ("Left")
def move_right():
global current_pos
global vuelta
current_pos = current_pos - vuelta
s1.target(current_pos)
print ("Right")
def calculate_steps():
global a_pos
global b_pos
global photos
global steps
steps = abs(int((a_pos - b_pos)/(photos-1)))
if b_pos < a_pos:
steps = -steps
print(steps)
def move_step():
global steps
global current_pos
global s1
global status
global last_inshot
global a_pos
global b_pos
current_pos = current_pos + steps
s1.target(current_pos)
print(current_pos)
if b_pos < a_pos:
if current_pos <= b_pos:
status = 0
print("finish")
elif b_pos > a_pos:
if current_pos >= b_pos:
status = 0
print("finish")


def web_page():
global photos
global steps
global speed
global current_pos
global status
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Zumo Robot Control</title>
</head>
<center><b>
<h1>SPiDER CAM Robot Control</h1>
<table><tr>
<td><form action="./left">
<input type="submit" value="Left" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="Stop" style="height:120px; width:120px" />
</form></td>
<td><form action="./right">
<input type="submit" value="Right" style="height:120px; width:120px" />
</form></td>
</tr></table>
<table><tr>
<td><form action="./photominus">
<input type="submit" value="---" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="{photos}" style="height:120px; width:120px" />
</form></td>
<td><form action="./photoplus">
<input type="submit" value="+++" style="height:120px; width:120px" />
</form></td>
</tr></table>
<table><tr>
<td><form action="./seta">
<input type="submit" value="setA" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="step:{steps}" style="height:120px; width:120px" />
</form></td>
<td><form action="./setb">
<input type="submit" value="setB" style="height:120px; width:120px" />
</form></td>
</tr></table>
<table><tr>
<td><form action="./goa">
<input type="submit" value="Go A" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="a:{current_pos}" style="height:120px; width:120px" />
</form></td>
<td><form action="./gob">
<input type="submit" value="Go B" style="height:120px; width:120px" />
</form></td>
</tr></table>
<table><tr>
<td><form action="./minus">
<input type="submit" value="-spd-" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="spd:{speed}" style="height:120px; width:120px" />
</form></td>
<td><form action="./plus">
<input type="submit" value="+spd+" style="height:120px; width:120px" />
</form></td>
</tr></table>
<table><tr>
<td><form action="./start">
<input type="submit" value="start" style="height:120px; width:120px" />
</form></td>
<td><form action="./stop">
<input type="submit" value="stop" style="height:120px; width:120px" />
</form></td>
<td><form action="./lock">
<input type="submit" value="{status}" style="height:120px; width:120px" />
</form></td>
</tr></table>
<form action="./estop">
<input type="submit" value="E-Stop" style="height:120px; width:120px" />
</form>
</body>
</html>
"""
return str(html)

##############################################################################
# 7) Asynchronous Tasks
##############################################################################

async def handle_client(reader, writer):
"""
Handle one HTTP client request (non-blocking).
"""
global emergency_stop
try:
request = await reader.read(1024)
except Exception as e:
print("Error reading request:", e)
return

request_str = request.decode() if request else ""
# Movement only if no emergency stop
if "GET /favicon.ico" in request:
await writer.aclose()
return
if "/seta?" in request_str:
set_A()
elif "/setb?" in request_str:
set_B()
elif "/goa?" in request_str:
go_A()
elif "/gob?" in request_str:
go_B()
elif "/photominus?" in request_str:
photo_minus()
elif "/photoplus?" in request_str:
photo_plus()
elif "/minus?" in request_str:
minus()
elif "/plus?" in request_str:
plus()
elif "/start?" in request_str:
start()
elif "/stop?" in request_str:
stop()
elif "/left?" in request_str:
move_left()
elif "/right?" in request_str:
move_right()
elif "/estop?" in request_str:
e_stop()
# Send response
resp = web_page()
await writer.awrite("HTTP/1.1 200 OK\r\n")
await writer.awrite("Content-Type: text/html\r\n")
await writer.awrite("Connection: close\r\n\r\n")
await writer.awrite(resp)
await writer.drain()
await writer.wait_closed()
#await writer.aclose()

async def webserver_task():
server = await uasyncio.start_server(handle_client, "0.0.0.0", 80)
print("Web server listening on http://{}:80".format(wlan.ifconfig()[0]))
return server

##############################################################################
# 8) Main Entry Point
##############################################################################

async def main():
# 1) Ensure we are connected to Wi-Fi at startup
wifi_connect()
# 2) Create Wi-Fi monitor task to reconnect if dropped
uasyncio.create_task(wifi_monitor_task())
# 3) Start distance measure task
uasyncio.create_task(test_status())
# 4) Start webserver
server = await webserver_task()

# 5) Simple blink in a loop
while True:
led.value(1)
await uasyncio.sleep(0.1) # Non-blocking
led.value(0)
await uasyncio.sleep(1)

import sys

try:
uasyncio.run(main())
except Exception as e:
# Open (or create) error.log in append mode
with open("error.log", "a") as f:
# Print a full traceback to the file
sys.print_exception(e, f)
print("An error occurred and has been logged to error.log.")

Assembling the Pi Pico W Controller

robotschematic.png

Once we have our Pi Pico W Controller programmed, we can start the assembly using a soldering iron, flux and tin.

Follow the diagram to connect every part of the Controller.

First we will need to solder the stepper motor drive module. Solder the positive and negative pins to the GND and 3V of the Pico W. Then solder the Enable, Direction and Step pins to the GPIOs according to the diagram.

Then we will solder the Input Optocoupler isolation module, this module let us get the shutter signal from our gimbal to the controller. we will solder the output GND of the module to the GND of the Pico W. the we will solder the OUT pin of the module to the GPIO of the controller according to the diagram. Then I will solder the negative INPUT pin of the module to the 2.5mm shutter pin of the gimbal cable. The positive INPUT of the module to the 3V pin of the controller. And the GND or Sleeve pin of the gimbal cable to the Ground of the controller board.

Then solder the Output Optocoupler Isolation module to the controller. Solder the GPIO pin to the INPUT positive of the module and the GND pin of the controller to the negative INPUT of the module. Then solder the negative OUTPUT of the module to the SLEEVE or GND of the 3.5mm female module and the positive OUTPUT pin to the ring and tip of the 3.5mm female module.

Now we will solder the DC input to the DC jack and solder positive of the DC jack to the positive of the stepper motor module and the negative to the negative, then solder the DC jack to the buck converter module to obtain the 5V for the Raspberry Pi Pico W, solder the OUTPUT buck module to the VBUS pin and the GND pin.

And you are done.

Assembling the SpiDER Cam Robot

DSC00453JPEG.jpg
DSC00435JPEG.jpg
DSC00436JPEG.jpg
DSC00437JPEG.jpg
DSC00438JPEG.jpg
DSC00439JPEG.jpg
DSC00440JPEG.jpg
DSC00441JPEG.jpg
DSC00442JPEG.jpg
DSC00443JPEG.jpg
DSC00444JPEG.jpg
DSC00445JPEG.jpg
DSC00446JPEG.jpg
DSC00447JPEG.jpg
DSC00448JPEG.jpg
DSC00449JPEG.jpg
DSC00450JPEG.jpg
DSC00451JPEG.jpg
DSC00452JPEG.jpg

For this step I used old 3d printed parts, but can find similar part in your local hardware store.

The most important parts are the two wheels of the trolley, and the motor plate these parts have to be aligned.

I attached two nema 17 motor plates on each side to use as an axis for the 3d printed u-wheel.

I used 8mm rod to hold the ball hearing and the u wheel and two 8mm rod retainer.

everything is attached to a 10 inch 2020 aluminum extrusion.

then I attached the stepper motor to the motor plate and the 2020 aluminum extrusion.

I attached the timing gear according to the shaft of your stepper motor. I used a 8mm GT2-30 gear.

Then attached the rope clamp to the motor plate using 2 of the m3 screws.

Attach the controller box on the side of the 2020 extrusion and the battery plate.

and you are done.

Using the SpiDER Cam Robot

Configure the Name of your hotspot and password of your smartphone device to the default SSID and password (SSID: ROBOT1234 Pass: pass1234) or active it if you changed your SSID and pass in the main.py file.

Find the IP address of the device in your smartphone.

Enter to your navigator and enter the IP address of the connected device to your HOTSTOP.

You will see the webpage of the S-Pi-DER Robot Controller.

The first row will let you move the robot left or right and stop the robot in case you pressed to many times left or right.

The second row will let you increase or decreased the number of photos you will take, this number must be the same number of photos your gimbal will sent to the camera.

The third row will let you setup the A position and the B position, the A position will be the initial position. and the B position will the final position. In the middle you will see the number of steps the stepper motor of the robot will take between every photo.

The fourth row will let you go to the A position and go to the B position, and in the middle will let you see the actual position in steps number, this is just a referential number.

The fifth row will let you increase or decrease the speed of the stepper motor, decrease the speed if the motor is struggling moving the whole rig, and increase it if you need more quick photos. Remember that the speed depends on the rig size, if your Rig is really heavy the inertia of the movement might cause the motor to struggle in the movement.

the sixth row will let you start or stop the process. The start button will activate the input and output optocouplers, so the S-Pi-DER Cam robot can now receive the shutter signal and send the shutter signal to your camera. The 3place in this row shows you the STATUS if 1 the Robot is activated, if 0 the robot is not ACTIVATED.

The last row have the Emergency STOP, in case you will need to stop the robot for an emergency.

Now let combine the gimbal and the S-PI-DER Cam Robot to create amazing time lapses.

Downloads

Setting Up the Zip Line

Here are some tips and warnings setting up your Zip Line Rope.

First find the correct place to setup your rope, remember to check the surroundings.

If you are in a place where there are people, let them know that you are placing a rope for their safety. If there is a security personnel ask for permission so that way the rope wont hurt or bother anyone.

Place the rope in a sturdy and safety pole or tree. Remember that the whole rig is about 10 pounds at least. And this 10 pounds must be over a tensioned rope.

Find for safety rope knots tutorials in YouTube to learn about the best knots to place and tension your rope.

And finally place the main rig of the robot before placing your rope and tighten it.

DISCLAIMER: Avoid to place the rope where people or vehicles circulate, you can hurt anyone.

2nd DISCLAIMER: Be careful to place the rope in high places or electrified poles, you can hurt yourself.

Using the Gimbal and the S-Pi-DER Cam Robot

Timeline 1

First you will need to understand the app of your gimbal, I have and old Zhiyun Weebill S gimbal so most of the modern gimbals will have the same application and power.

First we will balance our camera in the gimbal according to its manual. and attach the gimbal to the rig. Be very careful in this step because we don't want accidents with our gear. secure all the nuts and bolt well before leaving your rig hanging out.

Connect the battery to the DC port and connect the gimbal shutter port to the S-Pi-DER Cam Robot input and the S-Pi-DER Cam Robot Output to the shutter cable of your camera.

Inside the gimbal app I can select the Trajectory Option, this let insert an initial position and a final position, the amount of time between every photo and the amount of time the process will take. By this parameters I will calculate the number of photos the camera will take, this number should be the same or near the same in the S-Pi-DER Cam Robot.

Enable your smartphone HOTSTOP and the enter the S-Pi-DER Cam Robot webpage, move the robot to the initial position and press the SET A button, also setup the initial framing on your gimbal app.

Move the the S-Pi-DER Cam Robot to the final position and press SET B button, and setup the framing of the final position on your gimbal app.

Setup the same amount of photos in the gimbal app and the the S-Pi-DER Cam Robot webpage.

Press GO A so the robot will go to the initial position.

Press Start so the the S-Pi-DER Cam Robot will start waiting for the shutter signal of the gimbal.

And press start of the Gimbal App so the gimbal will sent the shutter signal and start moving.

And you are done. Wait to the gimbal and the S-Pi-DER Cam Robot finish their routines.

Notes: Remember to check the whole rig to avoid hurting someone and check the sturdiness of the poles/trees and tension of the rope every couple of minutes.

Advantages and Disadvantages

Advantages, the complete movement of your whole rig and a great timelapse. You cannot debate about it.

But there are some disadvantages:

A rope can be annoying for some people, specially in a park. So please be careful where you place the rope.

Some places might not have any pole or trees around, but if your handy enough, you can find a place between hills so you can place some large metal poles and secure the zip line rope there.