top of page
Search
Writer's pictureWilliam

Technology = Rubbing Two Sticks Together

Updated: Mar 22, 2021



As you may have heard, we finally got our Brick Pi working! This means, we actually have a robot to play around with.

Our next goal was to figure out how to hook up a PS4 controller to our BrickPi, so that we could control our robot using the controller.

I'll be using python3 once again, and luckily for us, there's a package for basically everything, and interpreting PS4 actions are no exception!

I started by looking at the source code on github: https://github.com/ArturSpirin/pyPS4Controller

The important stuff we need to worry about is https://github.com/ArturSpirin/pyPS4Controller/blob/master/pyPS4Controller/controller.py, which is also what we import.

In here, there is a class called actions. These would be the default actions for each thing we do on the controller, for example, pressing the triangle button, moving the left joystick up, etc.

Now, what we can do with this, is that we can overwrite these actions with our own functions. This means that, instead of printing "on_x_press" when we press x, we can make it do something else!

Example:

```python
class MyController(Controller):
​
    def __init__(self, **kwargs):
        Controller.__init__(self, **kwargs)
        
    def on_triangle_press(self): # overwriting the function 
        print("Green is not a creative colour!") 
```

Awesome. So now what we need to do is remap all the actions we want into the movement we want. We just need to overwrite the functions, combine them with our motor code, and we should be all good to go... right?

Well, unfortunately, if it were that easy, I don't think I would be blogging about it :)

The problem we face here is that the stick is analog. This means that, we have an additional two things to worry about instead of just a single button press, we have to worry about magnitude and direction (OH YEAH!!!)

Since it is a joystick, we have to worry about the direction that the joystick is pointed. However, if we look at the source code for these actions:

```py
def on_L3_up(self, value):
    print("on_L3_up: {}".format(value))
​
def on_L3_down(self, value):
    print("on_L3_down: {}".format(value))
​
def on_L3_left(self, value):
    print("on_L3_left: {}".format(value))
​
def on_L3_right(self, value):
    print("on_L3_right: {}".format(value))
​
def on_L3_y_at_rest(self):
    """L3 joystick is at rest after the joystick was moved and let go off"""
    print("on_L3_y_at_rest")
​
def on_L3_x_at_rest(self):
    """L3 joystick is at rest after the joystick was moved and let go off"""
    print("on_L3_x_at_rest")
​
def on_L3_press(self):
    """L3 joystick is clicked. This event is only detected when connecting without ds4drv"""
    print("on_L3_press")
​
def on_L3_release(self):
    """L3 joystick is released after the click. This event is only detected when connecting without ds4drv"""
    print("on_L3_release")
```

We see that the four functions that occur when we move the joystick take a value as an argument, presumably the magnitude. The magnitude can be controlled by moving the joystick more to one direction. If we dive a bit deeper into the source code, we'll also find that this function will only be called when the value, or magnitude, is greater than 0. This means that, if our joystick is up, our up value would be positive, but our down value would be negative. This means that since the down value is less than 0, it wouldn't be returned, and the function wouldn't be called.

(P.S Actually, there's just an x and y value, and left only gets returned when the x value is less than 0, right only returned when x is greater than 0 etc.)

But where lies the problem? Well, notice that the functions are all separate. This means that we need to somehow synchronize them all when we eventually want to move our motor.

To do this, we are going to utilize that the controller is a class. This means that we can assign variables that each of the four functions (and any of the functions in the same class) can use them.

Firstly, what I want to do is, based on the amount up or down the controller is, combined with the left and right it is, get a pair of values, and then use those values on the motors.

How can we do this? Well, what we can do is first, get the coordinates of the controller at any given moment. This can be done quite simply by updating a variable with whatever the value is when one of the above functions is called, that is, when the controller notices we go up or down, we update the up/down (I'll refer to this as the y value) value to whatever it is. We can do the same for the left/right, and I'll be calling this the x value.

To give an example of this in python, here's an example with the up function:

​
```python
def on_L3_up(self, value):
    self.y = value
    self.update()
```

The first line is defining the function, that is, what it is called and what it takes as parameters. In this case, it just has a value parameter.

The next line sets the y value to the value passed into the function. The self part at the start means it updates for the entire class, meaning that other functions can access that variable, as it is part of the class itself.

The next line calls the self.update() function, which is in the class itself. This function will take the current x and y values, and make the motors move based on their current values.

Alright, now that's done, we need to write the self.update() function. We have our two values x and y, and so we can use those in our calculations.

Remind ourselves how motors work. If we want to go left, we can move the right motor faster than the left motor, and if we want to go right, we want to move the left motor faster than the right. I devised a formula to work out motor speeds based on the x and y values.

Firstly, we need to figure out the general direction which we are going. This can be done by checking if x is positive or negative. Positive means we are moving more towards the right, negative means we are moving more towards the left.

Next, we assign an initial motor value, based on the y value. I chose to set the max y value to be the max motor value, and I did this with some weird logarithms stuff. Again here, we need to check if we are going forwards or backwards, based on whether the y value is positive or negative.

Finally, to get our turn, we need to subtract some value from one of the motors to get our turn. I worked this out by going with a similar approach to finding the motor values, except slightly less influential.

Writing this as code, we get something that looks like this:

```python
def update(self): # somehow convert a number from -32000 - 32000
    k1 = self.x ** LOG_VAL_2
    if self.x >= 0: # if its left
        k2 = k1 # 
        k1 = 0
    else:
        k2 = 0
    BP.set_motor_power(BP.PORT_B, (self.y **​ LOG_VAL) - k1)
    BP.set_motor_power(BP.PORT_C, (self.y **​ LOG_VAL) - k2)
```

This function will, once called, will take the current x and y values, and then move the robot based on them. The log_vals are there so I can work out initial motor speeds and how much I need to subtract to account for turning.

That should be all for this week, hopefully next week we can get the robot working well!


1 view0 comments

Recent Posts

See All

Comments


bottom of page