# Res-Scaling method beats PID controller. How?

### Scenario:

I have a robotic arm that has a (1D) lidar on its end, to orient itself with respect to the slant table in front of it. The lidar returns pretty accurate distance values in mm. The motor controlling the arm's motion towards the table (or away from it), uses time-duration values in seconds provided by the user and moves the arm fast or slow, at a pre-defined velocity.

e.g., arm_up(0.5) pulls the arm up at some velocity, for 0.5 seconds.

### What am I trying to do:

I am trying to control the the position of the arm before it comes to a certain distance from the table (200 mm). That is, it moves more when far and less when near it.

### Attempts:

I planned to use a controller to achieve this. First attempt included using a P-controller (P: Proportional) which involved fixing a gain Kp that gets multiplied with the error. I wanted the output of this controller to be in the range 0.1 to 1.0. I tried multiple values of Kp but did not get factor values proportional to the distance to be covered. When I set Kp=0.008, It works well when the error = 500 (mm), and the factor comes out to be = error x Kp, or 500 x 0.008=0.4, but when the error=10mm, factor=10 x 0.008=0.8 which is too below for the actuator to do anything, owing to the fact that all values must be in the range 0.1 to 1.0. Alternatively, setting higher values for Kp (as 0.01), requires me to clamp the output to a fixed value.

Kp = 0.003
if(LIDAR_1 > 200):
# P Controller
error = abs(200 - LIDAR_1)
duration = error * Kp
# To keep everything < 1.0
factor = duration%1.0
arm_down(factor)


Then, I used the feature scaler to scale the range (given by the lidar) to the desired range (0.1 to 1.0). To filter very high values, I used a mod operator a.k.a % in Python.

if(LIDAR_1 > 200):
# Using Feature Scaling
duration = ((LIDAR_1-200)*0.8/800) + 0.2
# To keep everything < 1.0
factor = duration%1.0
arm_down(factor)


### How is the (P) controller not standing up to its task ? Is it not meant to provide a value of Kp that satisfy all the cases (arm's distant or close encounters)

• Why do you want the output to be in that range? Have you also tried to saturate the output? – fibonatic Oct 3 '20 at 22:29
• The actuator for the motor (rather an actuator), accepts values in this range. Also, values above 5.0 are unsafe; so I limit my output to this range. Capping the output at 5.0 is saturating... right ? – Pe Dro Oct 4 '20 at 1:19
• Where is python? – Long Smith Oct 4 '20 at 10:07
• @long I am using Python to solve this problem – Pe Dro Oct 4 '20 at 13:25
• How did you build the error signal for the P-Controller? You have position reading from the sensor and current velocity of the robot. How did you create the error signal from these two signals? What unit of measurement do both of these have? – 50k4 Oct 5 '20 at 9:22

How is the (P) controller not standing up to its task ?

Well, just like you said - how is it not standing up to its task? What is it doing that makes you think it's not working? You said,

I tried multiple values of Kp but could not succeed

Nobody here knows what that means. "Could not succeed" could be a lot of problems. My guess is that you're trying to regulate something with only a proportional controller. Integral gains are the only way to drive steady-state error to zero.

What are some other things you could be doing wrong? Well, for starters, you said:

I am trying to control the the position of the arm [so]... it moves more when far and less when near [the table].

This sounds like you want a signed error, to drive your arm in two directions, yet your code you provided has error calculated as:

error = abs(200 - LIDAR_1)


If your error only has one sign (positive) then your arm only moves in one direction. Also, your error is calculated only with LIDAR_1, even though your entry into that calculation depends on LIDAR_1 and LIDAR_2. Are you sure that you don't want something like

error = LIDAR_2 - LIDAR1


or something similar? You haven't provided any diagrams showing what your values are, what the significance of 200 is, what angle it is you're trying to control, or how you're calculating that angle.

Next, following your code all the way through:

error = abs(200 - LIDAR_1)
duration = error * Kp
# To keep everything < 1.0
factor = factor%1.0
arm_down(factor)


Let's look at your code line-by-line:

1. Calculate the (absolute value of an) error,
2. Modify the error with a proportional gain to make a "duration,"
3. Modify a "factor,"
4. Use the factor to run arm_down.

Note: At no point in there did you use the duration. Maybe you meant factor = duration % 1.0?

But, finally, modulus is not your friend. This is definitely NOT what you want for it to be doing.

On the topic of the modulus, you said

to scale the range (given by the lidar) to the desired range (0.1 to 5.0). To filter very high values, I used a mod operator a.k.a % in Python.

First, you say that you want the desired range to be 0.1 to 5.0, but you're doing value % 1.0, so you're only going to get numbers between 0 and 0.999.

Second, you're making your control signal "wrap." Assuming you actually did value % 5.0, which I think is what you were intending given your stated desired range, then you get the following:

4.8 % 5.0 = 4.8
4.9 % 5.0 = 4.9
5.0 % 5.0 = 0
5.1 % 5.0 = 0.1
5.2 % 5.0 = 0.2


If you think of this like a cruise-control system, a very fast acceleration would have you push the pedal all the way to the floor, but if you wanted to accelerate even faster then your modulus operation actually has the controller take its foot completely off the pedal.

What you need instead is to clamp the value, like

if(duration > 5.0)
duration = 5.0


The best thing you can do for us, and yourself, is to make a drawing that shows where your lidar units are, what measurement you're expecting from them, and how those measurements relate to an angle you're trying to control.

You should then calculate that angle, using the diagram as a guide for developing the equation, and use the angle instead of raw lidar readings in your code.

Finally, structurally speaking, your PID/controller code should accept the angle/error and use that to calculate a raw control signal. If you have some downstream process that needs it to be in a particular range, then you can modify/saturate/clamp that signal inside that downstream process. This keeps your code more modular and easier to troubleshoot.

• Hi @Chuck, I agree that the information was not very clear; I have updated the question; Please have a look. – Pe Dro Oct 9 '20 at 15:51
• @PeDro - Only one lidar is pictured, still no relationship between lidar outputs and the angle you're trying to set. Did you resolve the other issues I pointed out above? – Chuck Oct 9 '20 at 17:35
• the current apparatus is using only a single lidar, also, I have again made edits, trying to clarify the case. – Pe Dro Oct 10 '20 at 4:36
• @PeDro - The units of the reference and the units of the output don't need to be the same - you should be accepting a position error as the input and the output should be whatever the motor needs to run. You're not using PID correctly because a PID controller needs to be able to drive in both directions, positive and negative. You can't set a "negative duration," so you can't drive the arm in the opposite direction. This means you can never correct an overshoot. – Chuck Oct 13 '20 at 12:59
• @PeDro - I'll point out too that, by setting a duration, you're not sending any analog output at all - you've got a bang-bang controller instead and it's not PID. Setting a 1 millisecond duration every millisecond and setting a 1000 millisecond duration every millisecond have the exact same instantaneous result - that your motor is ON. Something to consider. – Chuck Oct 13 '20 at 13:58