Cute Mechanical ScorPIon Robot

by hannu_hell in Circuits > Raspberry Pi

17635 Views, 157 Favorites, 0 Comments

Cute Mechanical ScorPIon Robot

scorpion_cover.jpg
scorpi.gif
1.jpg

This project is the design of a mechanical scorpion like robot. A real scorpion has eight legs where as this robot has four but owing to its tail gives a scorpion like look. Its movements are fast which gives it a cute and cartoon like look. Most of all it is able to move around swiftly on flat ground and has the ability to send real time video from the camera mounted on its tail. The camera can be positioned in different angles to get good footage. It is powered by a Raspberry Pi zero W and can be controlled from your computer via WiFi or a smart phone via Bluetooth. Since it has a PI zero W at its heart we can all come up with great ideas to further put this robot to use such as running remote scripts to retrieve data from APIs and so on. If you have any thoughts please feel free to share them and we could possible make it happen.

The robot was designed using Fusion360 and if you follow along you will be done with the build and on your way to controlling it. 3D printing most of the parts will not take much time but as is with any project the build order is important and I will try to document it as best I can.

Supplies

37.jpg
39.jpg
  • Emax ES08MD II Servos x 9 link
  • Emax ES9051 Servo x 1 link
  • Raspberry Pi Zero W x 1 link
  • Raspberry Pi Zero UPS module x 1 link
  • Radial Bearings (Outer Diameter - 7mm, Inner Diameter - 4mm) x 10 link
  • SS pin (4mm Diameter , 10 mm Length) x 10 link
  • Mini Buck Convertor (5V Regulator) x 1 link
  • Raspberry Pi zero camera module V1 and cable x 1 link
  • 7.4 V 1300mAH High Discharge LIPO Battery (Length-70mm, Width-35mm, Height-15mm) Any brand would be alright as long as its high discharge.
  • 3D Printer
  • Hot Glue Gun and Hot glue sticks
  • Super Glue
  • 1.5mm screw x 4 (optional)
  • M2.5-15mm Length Screws and Nuts x 4
  • 3D printing filament (I used white and grey)
  • 16 Channel 12-bit PCA9685 PWM module x 1 link

Leg Design

3_1.png
5.png

The robot has four legs and each leg has 2 Degrees of Freedom. It can move up and down and then sideways. Each leg is in two sections. The lower limb of each leg is fitted with the servo and then attached to the upper limb. The upper limb is fitted on to the servo on the base of the robot which acts as the body. The servo horns are pressed into the slot and then screwed in and fastened. After this make sure the 4mm Diameter bearings are fitted into the holes of the upper limb. Each upper limb has two servo horns that need to be fixed and two bearings that need to be fitted. This needs to be done prior to fitting the servos to the upper and lower limbs. There will be a small gap which can assist in pushing the limb into place with the servo and mounting on the servo horns. Before doing any assembling all servos must be programmed and positioned in their neutral position. If your fix the limb facing straight then it should be in zero position in which it can move up and down which is 90 degrees and 180 degrees respectively in the case of the lower limb. For the sideways motion of the upper limb, the neutral position should be facing sideways so that it can move in both directions.

Once the servo is fitted into the horn then the 4mm Diameter x 10 mm Length pin is pushed in the opposite side which can give the revolute joint the stability and freedom of motion it needs. After that, you can proceed to screw in the small screw which attaches the servo horn to the gear head of the servo though the cut out hole from outside. By now the leg limbs should be assembled. Next onto fixing the leg to the base of the body.

Body Base

21.png
7.png
22.png

The base of the body supports the the four legs of the robot and has four servos fitted screwed in. By now the servo horns have all been fitted on the legs and after screwing in the servos to the body base its a simple matter of fitting on the legs keeping in mind to set the servo neutral positions initially. After fitting in the servos, the pins which support the joint should be pushed in from the bottom of the base.

Assembling the Legs to the Body

assy1.gif
assy2.gif
assy3.gif
assy4.gif
assy5.gif
assy6.gif

Once the legs are assembled to the body the battery can be placed on top of the body base in the battery compartment. The charging wires and the discharge wires should be facing the open end so that when the robot is fully assembled the charging wire protrudes out allowing for easy access to recharge the battery but more on that later. A small amount of hot glue can be applied to the bottom of the battery to keep it in position although once the whole assembly is complete it will have very little room to move around so it may not be necessary at all.

At this stage you will need to wire up the buck convertor and keep it ready with enough wire length to connect to the battery as well as the input of the PCA9685 module. The buck convertor will step down the 7.4 V to 5V for the power needs of the servos. The Raspberry Pi Zero W will have its own independent power supply supplied by the UPS module. You can actually power the servos and the Pi with the battery but in order to do that you would need to supply power to the Pi first and after it boots up then power the PCA9685 module. This can be a bit of a hassle therefore I would recommend to use the UPS module to isolate the power needs.

Also you might have noticed in the videos that i drill some holes to mount the servo horns and servos. That is because I forgot to cut out the holes before hand when I assembled, but rest assured I have cut the holes in the design files so that there would be no need for any drilling if you intend to build it.

Assembling the Body

10.png
11.png
12.png
assy7.gif
assy8.gif
20.png

Before sliding the battery cover, the buck convertor need to be hot glued into the bottom plate of the battery cover and the Raspberry Pi Zero W with the UPS stacked on top of it. The 15mm length screws will go through the standoffs and the stands. After this the battery cover can be pushed in place and you can apply a bit of hot glue although it will be a snug fit. At this stage you will need to connect wires to the GPIO2(SDA), GPIO3(SCL), 5V, and GND of the Raspberry Pi Zero W and route it out for use by the PCA9685 module. Next you need to connect the camera cable of the Raspberry Pi and then route it out through the back allowing easy access to the tail section of the robot. After doing this you can stack on the PCA9685 platform and assemble the module on top of it.

Assembling the Tail Base Section

17.png
16.png
23.png

The tail base need to be glued to the back of the body base. First you would need to repeat the assembling procedure of the servo to the servo horn mentioned during the assembly of the leg section. The servo horn needs to pushed in and screwed in first. You could assemble the whole tail section and then glue it on which might be the easiest way to go. The important thing is to route the flexible camera cable through the back of the tail. The cutout slot in the mid section of the tail base section allows access to the on/off switch for the Raspberry Pi Zero W.

Assembling the Tail Mid Section and Camera

18.png
19.png

The mid section of the tail has the servo fixed to it therefore first push in the bearing and then screw the servo in position. Once done after getting the neutral position of the servo you can push it into the horn and then the pin on the opposite side. The camera cable should be routed from the back of the tail mid section. The back of the camera casing has the allocated space to screw the smaller ES9051 servo. The horn needs to be fixed to the tail mid section before pushing the servo in and the pin pushed in from the opposite end after the servo installed.

The camera is placed on the small standoff on the camera casing back. You may drill 1.5 mm holes through those standoffs and screw the camera in place although you could easily hot glue it in place as well. The screw holes for the Raspberry Pi Camera module are very small and if you intend to use screws you would require 1.5 mm screws.

Wiring and Electronics

scorpi_schematic.jpg
40.jpg

The wiring pretty simple though soldering the wires to the PCA9685 module was a bit tedious for me as I had cut off the female pin headers of the servos for easy routing of the wires from the robot limbs. However if you decide not to cut the female pin headers and find an better way to route the wires of the servos it will save a lot of time and less work. The PCA9685 allows the servos to plug in the correct orientation.

The battery is connected to "IN" terminals of the buck convertor and the tiny screw on the regulator is turned to adjust the output voltage to a steady 5V. Once you have 5V on your multi-meter then the negative terminal of the buck convertor output is connected to one terminal of the ON/OFF switch and the other terminal of the switch is connected to the ground of the PCA9685. The positive terminal of the buck convertor output is connected to the V+ terminal of the PCA9685. Both grounds of the Raspberry Pi Zero W and the PCA9685 are tied together.

The connections I made in for the different channels of the PCA96855 module will be useful if you want to follow along with the programming. Therefore listed below are the servo connections to the PCA9685 channels. I have named and numbered as if you are looking straight upfront at the robot.

  • Channel 6 - Front Right Upper Limb
  • Channel 7 - Back Left Upper Limb
  • Channel 8 - Front Left Upper Limb
  • Channel 9 - Back Right Upper Limb
  • Channel 10 - Front Right Lower Limb
  • Channel 11 - Back Left Lower Limb
  • Channel 12 - Front Left Lower Limb
  • Channel 13 - Back Right Lower Limb
  • Channel 14 - Tail Lower
  • Channel 15 - Tail Upper

Assembling the Top Cover

13.png
14.png
24.png

Once all the electronics are sorted out then its time to put the top cover of the robot. The top cover goes on top of the PCA9685 platform and then screwed in from the back. After the top cover is in place, the top cover front strut need to be glued to the bottom of the forward portion of the top cover. The front strut has cutouts for the on/off switch and the cutout for charging the UPS for the Raspberry PI zero W. The bottom section of the front strut goes below the body base and two 2 mm Diameter screws are used to keep it in place and added stability.

Motion Analysis

34.png
35.png
32.png
33.png
29.png

The scoPIon robot is able to move forwards, backwards, turn left and turn right. In addition to this it is able to do a dance by lowering left limbs and moving sideways in either directions. The position of the lower limbs in coordinate space determine how high the robot is from the ground. Since there is only a single Degree of Freedom (DOF) for moving up and down the mathematics is quite simple. Its simply a question of resolving the angle of a right angled triangle given the x and y coordinates from the point of assigned origin (O). The inverse kinematics of the motion can give a sense of how high the robot is from the ground.

For the motion of the robot we could go on to determine its center of gravity but simply by implementing a crude method of testing and analysis we can identify some positions which allow the robot to balance well on three of its legs. How well it balances has much to do with the design and assembly of the components. Once these positions are determined we can go on to determine the motion of the robot.

For the forward and backwards motion the diagonal legs are moved one at a time noting that when one leg lifts off the ground it can still well balance itself. The first set of diagonal legs move to the desired positions one after the other and then the second sets of diagonals repeat the same movement. After all the legs have moved they are returned back to its original position while the legs keep contact with the ground. This motion is generated by first lifting the limbs from the ground from the lower limb servos and then the servos on the body position them sideways in the desired position. Once in that position the servos on the base body move it sideways back to it original position. This motion allows the robot to move forwards or backwards.

The timing can determine the speed it moves but since this robot has a single degree of freedom moving up and down the range is limited to an extent where fast movements allow it to cover more distance.Turning left and right has the same principle with the difference being that the sideways motion takes place when the lower limbs are in touch with the ground.

Programming

testing.gif

Install the Rasbian OS Lite version from the official Raspberry Pi website on a 16G micro SD card to get started. Then boot your Raspberry Pi zero and you will be greeted to the command line interface. You can set up your WiFi by typing <sudo raspi-config>. If this is your first time on a Linux system I recommend you research a bit on it and then proceed. I use VIM to write my scripts but feel free to use any of the application you like.

First things first, in order to get the camera working please connect your camera and check if everything works well. Here is a great article on how to get started with the camera module from the official Raspberry Pi website. Once you are good to go we need to download a git repository for the camera to work by sending commands via the terminal or in our case the python script. Here is the link for that repository and the installation guide.

Now we are done with camera setup. You can test by running the start.sh script on the downloaded git repository after running the install.sh script. Once you run the start.sh script you can type in the IP address (You can get the IP address by typing <ifconfig> on the terminal) on the browser to open up a live camera feed. To stop you need to run the stop.sh script in the same folder.

We can control the robot using WiFi as well as Bluetooth. If you want to control using a smart phone on Bluetooth you need to download an app which can send bytes of characters such as BlueTerm 2, Blue Term or Bluetooth Terminal (Currently available for android system). If you prefer to control via WIFI which I personally prefer you can maneuver the robot around your house with the live camera feed on. You can also view the camera live feed on your smart phone as well.

In order to control via WIFI we need two scripts. One for the robot and one on your computer. The script on the robot can be changed to your preference of control. I have left the Bluetooth and WiFi code both on the script which runs on the robot, but before we get started we need to install the Adafruit PCA9685 library to control the servos. The PCA9685 module is a 16-channel controller that can control 16 PWM outputs via I2C communication. To install the library simply follow the instructions on this link. This library is not in the default python installation. To install it, simply open a terminal and enter the following command.

sudo pip3 install adafruit-circuitpython-servokit


ScorPIon Code

from adafruit_servokit import ServoKit
kit = ServoKit(channels=16)
import time
import os

def test_move(s, limit_x, limit_y):
   kit.servo[s].angle = limit_x
   time.sleep(2)
   kit.servo[s].angle = 90
   time.sleep(2)
   kit.servo[s].angle = limit_y
   time.sleep(2)
   kit.servo[s].angle = 90


def move_angle():
   while True:
       option = input("Move single servo? y/n")
       if option == "y":
           single = True
       else:
           single = False

       if not single:
           servos = input("Enter servos to move in the format <servonumber1.servonumber2..>")
           angles = input("Enter angle to move")
           try:
               a = int(angles)
               s = servos.split(".")
               for i in s:
                   kit.servo[int(i)].angle = a
           except:
               print("Input Invalid")


       if single:
           servo = input("Please enter servo number")
           angle = input("Please enter rotation angle")

           if servo == "q" or angle == "q":
               break

           try:
               servo = int(servo)
               angle = int(angle)
               kit.servo[servo].angle = angle
           except:
               print("Write valid numbers")


def walk_forward(t):
   #kit.servo[14].angle = 140
   #kit.servo[15].angle = 90

   kit.servo[6].angle = 90
   kit.servo[10].angle = 50

   kit.servo[7].angle = 90
   kit.servo[11].angle = 60

   kit.servo[8].angle = 90
   kit.servo[12].angle = 140

   kit.servo[9].angle = 90
   kit.servo[13].angle = 130


   kit.servo[10].angle = 70
   time.sleep(t)
   kit.servo[6].angle = 120
   time.sleep(t)
   kit.servo[10].angle = 50
   time.sleep(t)

   kit.servo[11].angle = 80
   time.sleep(t)
   kit.servo[7].angle = 60
   time.sleep(t)
   kit.servo[11].angle = 60

   time.sleep(0.1)
   kit.servo[6].angle = 90
   time.sleep(0.05)
   kit.servo[7].angle = 90


   kit.servo[12].angle = 110
   time.sleep(t)
   kit.servo[8].angle = 60
   time.sleep(t)
   kit.servo[12].angle = 140
   time.sleep(t)

   kit.servo[13].angle = 110
   time.sleep(t)
   kit.servo[9].angle = 120
   time.sleep(t)
   kit.servo[13].angle = 130
   time.sleep(t)

   time.sleep(0.1)
   kit.servo[8].angle = 90
   time.sleep(0.05)
   kit.servo[9].angle = 90


def walk_backward(t):
   #kit.servo[14].angle = 140
   #kit.servo[15].angle = 90

   kit.servo[6].angle = 90
   kit.servo[10].angle = 50

   kit.servo[7].angle = 90
   kit.servo[11].angle = 60

   kit.servo[8].angle = 90
   kit.servo[12].angle = 140

   kit.servo[9].angle = 90
   kit.servo[13].angle = 130


   kit.servo[10].angle = 70
   time.sleep(t)
   kit.servo[6].angle = 60
   time.sleep(t)
   kit.servo[10].angle = 50
   time.sleep(t)

   kit.servo[11].angle = 80
   time.sleep(t)
   kit.servo[7].angle = 120
   time.sleep(t)
   kit.servo[11].angle = 60

   time.sleep(0.1)
   kit.servo[6].angle = 90
   time.sleep(0.05)
   kit.servo[7].angle = 90


   kit.servo[12].angle = 110
   time.sleep(t)
   kit.servo[8].angle = 120
   time.sleep(t)
   kit.servo[12].angle = 140
   time.sleep(t)

   kit.servo[13].angle = 110
   time.sleep(t)
   kit.servo[9].angle = 60
   time.sleep(t)
   kit.servo[13].angle = 130
   time.sleep(t)

   time.sleep(0.1)
   kit.servo[8].angle = 90
   time.sleep(0.05)
   kit.servo[9].angle = 90


def turn_left(t):
   #kit.servo[14].angle = 140
   #kit.servo[15].angle = 90

   kit.servo[6].angle = 90
   kit.servo[10].angle = 50

   kit.servo[7].angle = 90
   kit.servo[11].angle = 60

   kit.servo[8].angle = 90
   kit.servo[12].angle = 140

   kit.servo[9].angle = 90
   kit.servo[13].angle = 130


   kit.servo[10].angle = 70
   time.sleep(t)
   kit.servo[11].angle = 80
   time.sleep(0.1)
   kit.servo[7].angle = 120
   time.sleep(t)
   kit.servo[6].angle = 120
   time.sleep(t)
   kit.servo[10].angle = 50
   time.sleep(t)
   kit.servo[11].angle = 60  

   time.sleep(0.1)

   kit.servo[12].angle = 110
   time.sleep(t)
   kit.servo[13].angle = 110
   time.sleep(0.1)
   kit.servo[8].angle = 120
   time.sleep(t)
   kit.servo[9].angle = 120
   time.sleep(t)
   kit.servo[12].angle = 140
   time.sleep(t)
   kit.servo[13].angle = 130

   time.sleep(0.1)


   for i in range(120, 90, -1):
       kit.servo[6].angle = i
       kit.servo[7].angle = i
       kit.servo[8].angle = i
       kit.servo[9].angle = i
       time.sleep(0.01)


def turn_right(t):
   #kit.servo[14].angle = 140
   #kit.servo[15].angle = 90

   kit.servo[6].angle = 90
   kit.servo[10].angle = 50

   kit.servo[7].angle = 90
   kit.servo[11].angle = 60

   kit.servo[8].angle = 90
   kit.servo[12].angle = 140

   kit.servo[9].angle = 90
   kit.servo[13].angle = 130


   kit.servo[10].angle = 70
   time.sleep(t)
   kit.servo[11].angle = 80
   time.sleep(0.1)
   kit.servo[7].angle = 60
   time.sleep(t)
   kit.servo[6].angle = 60
   time.sleep(t)
   kit.servo[10].angle = 50
   time.sleep(t)
   kit.servo[11].angle = 60  

   time.sleep(0.1)

   kit.servo[12].angle = 110
   time.sleep(t)
   kit.servo[13].angle = 110
   time.sleep(0.1)
   kit.servo[8].angle = 60
   time.sleep(t)
   kit.servo[9].angle = 60
   time.sleep(t)
   kit.servo[12].angle = 140
   time.sleep(t)
   kit.servo[13].angle = 130

   time.sleep(0.1)


   for i in range(60, 90):
       kit.servo[6].angle = i
       kit.servo[7].angle = i
       kit.servo[8].angle = i
       kit.servo[9].angle = i
       time.sleep(0.01)


def tail_movement():
   kit.servo[14].angle = 140
   kit.servo[15].angle = 80
   for i in range(140, 10, -1):
       kit.servo[14].angle = i
       time.sleep(0.01)
   time.sleep(0.1)
   for j in range(2):
       for i in range(10, 50):
           kit.servo[14].angle = i
           time.sleep(0.01)
       for i in range(50, 10, -1):
           kit.servo[14].angle = i
           time.sleep(0.01)
   time.sleep(2)

   kit.servo[14].angle = 120

   time.sleep(2)

   for i in range(80,170):
       kit.servo[15].angle = i
       time.sleep(0.01)
   time.sleep(0.1)
   for i in range(170, 50, -1):
       kit.servo[15].angle = i
       time.sleep(0.01)
   for i in range(50, 80):
       kit.servo[15].angle = i
       time.sleep(0.01)

   for i in range(120, 140):
       kit.servo[14].angle = i
       time.sleep(0.01)

def dance():

   kit.servo[14].angle = 140
   kit.servo[15].angle = 90

   kit.servo[6].angle = 90
   kit.servo[10].angle = 50

   kit.servo[7].angle = 90
   kit.servo[11].angle = 60

   kit.servo[8].angle = 90
   kit.servo[12].angle = 140

   kit.servo[9].angle = 90
   kit.servo[13].angle = 130

   for i in range(50, 100):
       kit.servo[10].angle = i
       time.sleep(0.01)
   for i in range(130, 80, -1):
       kit.servo[13].angle = i
       time.sleep(0.01)

   for j in range(2):
       for i in range(90, 120):
           kit.servo[6].angle = i
           kit.servo[9].angle = i
           time.sleep(0.01)
       for i in range(120, 60, -1):
           kit.servo[6].angle = i
           kit.servo[9].angle = i
           time.sleep(0.01)
       for i in range(60, 90):
           kit.servo[6].angle = i
           kit.servo[9].angle = i
           time.sleep(0.01)

   for i in range(80, 130):
       kit.servo[13].angle = i
       time.sleep(0.01)
   for i in range(100, 50, -1):
       kit.servo[10].angle = i
       time.sleep(0.01)

   time.sleep(2)

   for i in range(140, 90, -1):
       kit.servo[12].angle = i
       time.sleep(0.01)
   for i in range(60, 110):
       kit.servo[11].angle = i
       time.sleep(0.01)

   for j in range(2):
       for i in range(90, 120):
           kit.servo[8].angle = i
           kit.servo[7].angle = i
           time.sleep(0.01)
       for i in range(120, 60, -1):
           kit.servo[8].angle = i
           kit.servo[7].angle = i
           time.sleep(0.01)
       for i in range(60, 90):
           kit.servo[8].angle = i
           kit.servo[7].angle = i
           time.sleep(0.01)

   for i in range(110, 60, -1):
       kit.servo[11].angle = i
       time.sleep(0.01)
   for i in range(90, 140):
       kit.servo[12].angle = i
       time.sleep(0.01)

   time.sleep(1)

   kit.servo[10].angle = 70
   time.sleep(0.01)
   kit.servo[12].angle = 110
   time.sleep(0.1)
   kit.servo[6].angle = 120
   time.sleep(0.01)
   kit.servo[8].angle = 60
   time.sleep(0.01)
   kit.servo[10].angle = 50
   time.sleep(0.01)
   kit.servo[12].angle = 140

   time.sleep(1)

   for i in range(50, 180):
       kit.servo[10].angle = i
       time.sleep(0.01)
   for j in range(3):
       for i in range (120, 90, -1):
           kit.servo[6].angle = i
           time.sleep(0.01)
       for i in range(90, 120):
           kit.servo[6].angle = i
           time.sleep(0.01)
   for i in range(180, 150, -1):
       kit.servo[10].angle = i
       time.sleep(0.01)
   for i in range(140, 40, -1):
       kit.servo[12].angle = i
       time.sleep(0.01)
   for i in range(40, 140):
       kit.servo[12].angle = i
       time.sleep(0.01)
   for i in range(150, 50, -1):
       kit.servo[10].angle = i
       time.sleep(0.01)

   time.sleep(1)

   kit.servo[10].angle = 70
   time.sleep(0.01)
   kit.servo[12].angle = 110
   time.sleep(0.1)
   kit.servo[6].angle = 90
   time.sleep(0.01)
   kit.servo[8].angle = 90
   time.sleep(0.01)
   kit.servo[10].angle = 50
   time.sleep(0.01)
   kit.servo[12].angle = 140

   time.sleep(1)

   kit.servo[14].angle = 80
   kit.servo[15].angle = 120

   for i in range(120, 80, -1):
       kit.servo[15].angle = i
       time.sleep(0.01)
   for i in range(80, 140):
       kit.servo[14].angle = i
       time.sleep(0.01)

def show_off():
   for i in range(3):
      walk_forward(0.01)

   time.sleep(3)

   for i in range(3):
       walk_backward(0.01)

   time.sleep(3)

   for i in range(2):
       turn_left(0.01)
       time.sleep(0.1)

   time.sleep(3)

   for i in range(2):
       turn_right(0.01)
       time.sleep(0.1)

   time.sleep(1)

   tail_movement()
   time.sleep(2)
   dance()

#move_angle()
#tail_movement()
#dance()

#------------------------------------------------------------------------------------------
#************************* BLUETOOTH *****************************************************

import bluetooth

server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)

port = 1
server_sock.bind(("",port))
server_sock.listen(1)

client_sock, address = server_sock.accept()
print("Accepted Connection from ", address)

while True:
   recvdata = client_sock.recv(1024)
   print("Received {} through Bluetooth".format(recvdata))
   if (recvdata == b'Q'):
       print("Exiting")
       break
   if (recvdata == b'f'):
       for i in range(2):
           walk_forward(0.01)
           time.sleep(0.1)
   if (recvdata == b'b'):
       for i in range(2):
           walk_backward(0.01)
           time.sleep(0.1)
   if (recvdata == b'r'):
       for i in range(2):
           turn_right(0.01)
           time.sleep(0.1)
   if (recvdata == b'l'):
       for i in range(2):
           turn_left(0.01)
           time.sleep(0.1)
   if (recvdata == b's'):
       show_off()
   if (recvdata == b'c'):
       os.system("/home/pi/RPi_Cam_Web_Interface/start.sh")
   if (recvdata == b'x'):
       os.system("/home/pi/RPi_Cam_Web_Interface/stop.sh")
   if (recvdata == b'u'):
       kit.servo[14].angle = 60
       kit.servo[15].angle = 100
   if (recvdata == b'm'):
       kit.servo[14].angle = 80
       kit.servo[15].angle = 80
   if (recvdata == b'd'):
       kit.servo[14].angle = 110
       kit.servo[15].angle = 80

client_sock.close()
server_sock.close()

#------------------------------------------------------------------------------------------
#******************************************************************************************

#------------------------------------------------------------------------------------------
#************************************** WIFI **********************************************


#import socket
#s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#time.sleep(20)
#
#
#HOST = "192.168.0.104" # IP address of your Raspberry Pi Zero W
#PORT = 5555
#
#s.connect((HOST, PORT))
#
#while True:
#   s_msg = s.recv(1024)
#   s_msg = s_msg.decode()
#   if s_msg == "Q":
#       break
#
#   if s_msg == "F":
#       for i in range(3):
#           walk_forward(0.01)
#   if s_msg == "SF":
#       walk_forward(0.01)
#   if s_msg == "FF":
#       for i in range(5):
#           walk_forward(0.01)
#
#   if s_msg == "B":
#       for i in range(3):
#           walk_backward(0.01)
#   if s_msg == "SB":
#       walk_backward(0.01)
#   if s_msg == "FB":
#       for i in range(5):
#           walk_backward(0.01)
#
#   if s_msg == "R":
#       for i in range(3):
#           turn_right(0.01)
#   if s_msg == "SR":
#       turn_right(0.01)
#   if s_msg == "FR":
#       for i in range(5):
#           turn_right(0.01)
#
#   if s_msg == "L":
#       for i in range(3):
#           turn_left(0.01)
#   if s_msg == "SL":
#       turn_left(0.01)
#   if s_msg == "FL":
#       for i in range(5):
#           turn_left(0.01)
#
#   if s_msg == "C":
#       os.system("/home/pi/RPi_Cam_Web_Interface/start.sh")
#   if s_msg == "X":
#       os.system("/home/pi/RPi_Cam_Web_Interface/stop.sh")
#
#   if s_msg == "T":
#       tail_movement()
#   if s_msg == "S":
#       show_off()
#   if s_msg == "U":
#       kit.servo[14].angle = 10
#       kit.servo[15].angle = 100
#   if s_msg == "M":
#       kit.servo[14].angle = 90
#       kit.servo[15].angle = 90
#   if s_msg == "D":
#       kit.servo[14].angle = 120
#       kit.servo[15].angle = 70
#   if s_msg == "RT":
#       kit.servo[15].angle = 80
#       kit.servo[14].angle = 140
#       
#
#   if len(s_msg) > 2:
#       a = s_msg.split(",")
#       base_angle = int(a[0].strip("BT"))
#       tail_angle = int(a[1].strip("TT"))
#       kit.servo[14].angle = base_angle
#       kit.servo[15].angle = tail_angle
#
#   if s_msg == "Q":
#       break
#
#
#s.close()
#os.system("/home/pi/RPi_Cam_Web_Interface/stop.sh")
#time.sleep(1)
#
#kit.servo[14].angle = 140
#kit.servo[15].angle = 90
#kit.servo[6].angle = 90
#kit.servo[10].angle = 50
#kit.servo[7].angle = 90
#kit.servo[11].angle = 60
#kit.servo[8].angle = 90
#kit.servo[12].angle = 140   
#kit.servo[9].angle = 90
#kit.servo[13].angle = 130
#
#time.sleep(1)
#
#os.system("cd /home/pi")
#os.system("sudo shutdown -h now")


In the above code I have commented out the WiFi control code and set to control via Bluetooth. You can comment out the Bluetooth section of the code and uncomment the WIFI code to control via WIFI.

We start by importing the adafruit_servokit library and then define some functions for moving the robot. All the functions are pretty self explanatory but feel free to send a message if you have any inquiries.

Now we need to run the above script on startup of the Raspberry Pi Zero W. We can use crontab to do that. Type in the following commands to run the script on startup

sudo crontab -e 

Now add a new entry at the very bottom by specifying @reboot to run the command on boot up.

@reboot sudo python3 /home/pi/Desktop/scorpion.py    # This is the location of the script

Now save the file and exit. Your program will now run on boot. To play it safe i would recommend you run the script once to check if everything works well before updating the crontab. Once everything is good to go you can add the command to crontab to run the script on start-up.

Computer Control of ScorPIon on WIFI

The script need to be run from you computer and the keystrokes send to move the robot. I have tried my best to make the script readable and self explanatory but feel free to inquire if you have any issues. P.S. The top part of the script is just for the sake of presentation. ;=)



import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

HOST = ""
PORT = 5555


print("                         ___")
print("                       /  /")
print("                      |  |")
print("                       \ |")
print("                        | |")
print("                        | |")
print("                         \ \\")
print("                      _____\ \______")
print("                    /               \\")
print("             ____ /                   \ ____")
print("           /    |                     |    \\")
print("         /   ___ ---------------------- ____  \\")
print("        \  |                              |  /")
print("          \ \                             / /")
print("            ```                           '''")
print("                         _____  ______")
print("                        ||   ||   ||")
print("  ___   __  ___   ___   ||___||   ||    ___     ___")
print(" ||__ ||   || || ||_||  ||        ||   || || ||/  ||")
print(" __|| ||__ ||_|| || \\\ ||      __||__ ||_|| ||   ||")

print("\n")
print("Powered by Raspberry Pi zero W")
print("Based on Python")
print("Raspberry Pi Camera Interface - (github:silvanmelchior/RPi_Cam_Web_Interafce)")
print("Program and 3D model - Hannu (github:hannu-hell)")
print("This is an open source project and all rights granted to modify and reproduce")
print("and sell as the user may deem fit provided credit is given where credit is due :)")
print("\n")

time.sleep(1)


print("ScorPiion servo arrangement diagram")
print("\n")
print("        15")
print("         |")
print("        14")
print("       | |")
print("  13--9  7--11")
print("     |    |")
print("     |    |")
print("  10--6  8--12")
print("       .^.")
print("\n")
time.sleep(1)

print("[alert] Please turn on the scoPIOn to establish connection")
print("[notice] Waiting to establish connection..[on_hold]")

s.bind((HOST, PORT))
s.listen(1)
conn, address = s.accept()
print("[status] Connected to ScorPIon [OK]\n")

commands = ["Q", "RT", "F", "SF", "FF", "B", "SB", "FB", "R", "SR", "FR", "L", "SL", "FL", "C", "X", "T", "S", "U", "M", "D", "CC"]

while True:
   print("                            =======ACTIVE FUNCTIONS========\n")
   print("[F][SF][FF] Walk forward -> SF for less steps and FF for more steps (default set to normal)")
   print("[B][SB][FB] Walk backward -> SB for less steps and FB for more steps (default set to normal)")
   print("[R][SR][FR]Turn Right -> SR for less steps and FR for more steps (default set to normal)")
   print("[L][SL][FL] Turn Left -> SL for less steps and FL for more steps (default set to normal)")
   print("[C] Camera On")
   print("[X] Camera Off")
   print("[T] Flick tail")
   print("[RT] Reset tail to resting position")
   print("[S] Show-Off")
   print("[U] Camera set to high position")
   print("[M] Camera set to mid position")
   print("[D] Camera set to low position")
   print("[CC] Custom Camera control")
   print("[Q] QUIT")
   print("\n")
   command = input("[alert] Please enter a command from the above menu to control scorPIon\n")
   command = command.upper()

   base = ""
   tail = ""

   if command in commands:
      if command == "CC":
         BT = input("[alert] Enter angle for base tail movement between 0 - 140 (140-forward and 0-backward)\n")
         TT = input ("[alert] Enter angle for tail tip movement between 50 - 170 (50-forward and 170-backward)\n")
         try:
            BT = int(BT)
            TT = int(TT)
            if (0 <= BT <= 140):
               base = "BT"+str(BT)
            else:
               print("[warning] Tail Base angle should be between 0-140")
            if (50 <= TT <= 170):
               tail = "TT"+str(TT)
            else:
               print("[warning] Tail Tip angle should be between 50-170")
            command = base+","+tail
            if len(command) < 8:
               command = ""
         except:
            print("[warning] Not a valid input")

      msg = command.encode()
      conn.send(msg)
      if command == "Q":
         break

print("\n")
conn.close()
print("[notice] Adios Amigo, Hasta La vista, Goodbye, 再见, Ciao, Das Vidanya")
time.sleep(1)
print("[notice] Killing all processes..")
time.sleep(2)
print("[notice] Shutting down scorPion")
for i in range(15, 0, -1):
   print(f"[alert]Shutting Down progress [{i}]")
   time.sleep(1)
print("[alert] Please switch off scorPi")


Conclusion

ScorPIon 🦂 Cute Mechanical scorpion

Building the scorPIon robot can have some challenges specially after the build. One such challenge was that the 3D printed limbs did not have enough traction of the ground for it to move specially on smooth surfaces. So as you might have noticed in the video i have drilled some holes on the tip of the lower limbs and zip tied some rubber pieces to create more friction for it to move on smooth surfaces such as tiled floors. After that it was able to move well.

Another aspect that needs attention is that during the testing stages I used without the UPS module, hence the LIPO was sharing the power for both the servos and the PI itself. It works fine, but the problem arises on reboot of the PI. Therefore I had used a DIP switch to control the power up order of the robot on boot. Initially the PI is powered on and when the startup script runs power is supplied to the servos (After a time gap of around 30 seconds). You could altogether avoid this problem by using the UPS but if you feel you could adapt to this you could do without the UPS as well.

Overall it is a cute looking useful robot to monitor around your home and it was a fun build. Hope you liked this instructable. Thank you for reading till the end. :)

3D model and code files