There was a lot of discution about the servo mapper this week-end and we did not seem to found a consensus how to fix the problem related the it's usage.

To have the servo service work properly, the user should be able to

  • map the range they are using to the position the servo should take
  • modify the limits a servo should have 
  • Set and get any parameters about the servo in the range they are using (input)

The problem with the current implementation of the mapper is that the limits (min/max) are set on the output wich lead to many confusion and logic problems

by example, if I have a map(0,90,90,180) 

If I want to limits the range from 0-90 to 10-90... I must use servo.setMin(100)!!!

and if the map is more complex, like (45,135,38,140) I have to make a manual computation to find how I must set the limits

It also make it hard to compare the position of the servo with it's min/max

So I really think the limits should be set using the Input value

But fixing that potentially break some backward compatibility.

Some path of solution have been proposed or my understanding of what proposed

  • Leave it that way: don't break any script, but will always make services that modify servo position hard to do and be solid in all situation
  • Remove the mapper and use gain/shift parameters: I'm not against the idea, but not convince it's easy to use in all the situation. By example how do I replicate what a map(45,135,38,140) do with the gain/shift parameters? I need to do manual computation to find the right parameters?
  • Keep it this way, but have getMin reverse map the setMin. That will help fixing the problem of comparing position to min/max, but can still be very confusing.

 

What I think is the best solutions is to remove the setMin/setMax and replace them with more explicit method like setMinInput, setMaxOutput etc. That will break scripts that can potentially have problem with the map setting, but better have the script not run than use bad parameters and burn servo or break part. The new setMin/max methods will also compute their counter part (Input/Output) by mapping or reversemapping the value. by example with a map(0,90,90,180), a setMinInput(10) will be the same as doing setMinOutput(100). That will also have the flexibility to apply the limits to the range we want (Input or Output), but I still beleive that using it on the Input value is much more better

Any though?

 

 

Mats

7 years 10 months ago

Hi

When you talk about complexity, it's the complexity for the developers, not so much for the users of MRL. 

So given your 3 options, I vote for this solution that is very close to the third option that you suggest.

  • Keep it the way it is to avoid breaking scripts, but add two methods, getMinInput and getMaxInput in mapper and make them avaliable also in Servo. They should return the minOutput and maxOutput recalculated to the corresponding input values. The reason to have this calculated is because you can use something like in your example: map(0,90,90,180)  and then setMinMax(100,180) to override the output limits.
  • In this case the getMinInput would return 10 ( 0 input will still correspond to 90 output, and 90 input will correspond to 180 output, and 10 input will correspond to 100 output).
  • That makes it possible for services like Tracking or Jaw to internally use make calculations based in the values from getMinInput and getMaxInput. When using those values in moveTo, the servo/mapper will handle the correct calculations from the Input calues to the Output values.

/Mats

the complexity I want to avoid is to have the user need play with two different range of numbers to set the servo.

so your proposed solution do nothing to help in that way.

In your solution, user still have to do setMin(100) while what he want to do is to prevent an accidental moveTo(0) to happen. And that make no sense for me. It's certainly not user friendly for a beginner user who just want his slider to not go below 10 have to do a setMin(100). Then for another servo that use map(0,180,0,180) and setInverted(True), he need to use setMin(170) to have his slider not go below 10.

That's what I want to prevent. You should be able to set the limits on the same range that you use it for the moveTo()

the solution you propose help the developper, but not the regular user

Aha. Different assumptions.

I think we have a different view of how mappings are used. 

When people make their own scripts no mapping is needed at all, with the exception of perhaps the case where a servo needs to be reveresed.

All other mappings are used in the case where the user doesn't have control over the moveTo parameters, like when using Gael's gestures on a different InMoov build.

So to be able to use all the different Gestures that Gael created, there was a need to be able to move the same way. In this case the user doesn't have any control over the input, they are defined in the gestures and needs to be map:ed to get the expected output values to the servos.

The other case is similar. Lets say that the Tracking service was built to always send a moveTo value between 0 and 1. Then the mapping would then be map(0,1,0,180) for a full move, or map(0,1,70,110) to limit the movement i.e for example for an eye.

A third scenario may be that the servo is controlled by an potentiometer sending an analog value. That value could be between 0 and 1023 if it has 10-bit resolution. A map(0,1023,0,180) would make the servo move all the way.

So mapping is used in the cases where the user don't have control over the input values

Therefore it makes no sense to have the user calculate backwards to set the servo limits on the input values, and very natural to have the limits set on the Output values.

 

Mats, our understanding on how the mapper is used is not different.

In almost all the script people use, the input range is different than in output range.

Most of the user use gael script as reference. Gael use map that way (0,180, MIN, MAX) so his gesture can be use by other user. 

In my script, I use angle of the part the servo is moving as input, and the output is the position the servo should take to have the choosen angle. For most case, the mapping is still (0,180,0,180) but in some case like with the bicep, if I want to elbow to bend at 60°, the servo have to move to position 80. The mapper can compute that for me.

So user use the mapper all the time.

The problem I see is that there is currently no way to set the limits with the reference people use. It most be done in another reference range (output)

I will continue with one of your example

you have your service that read the potentiometer and do servo.moveTo(potentiometer value).

the servo.map is (0,1023,0,180)

everything work fine on the software part.

You mount all your parts and test and find out that when the potentiometer value reach 999, problem may arise, servo could jam, or pieces may break, or it`s just going too far for what I want. The problem is that I could not set the limits with that value. It need to be converted in the output range and that`s where I beleive the confusion happen.

servo.setMinMax() is not explicit enough. so user may try to use it with their input value and that gives resuilt completely different than what expected. 

That's why I think using setMinMaxInput and setMinMaxOutput will improved things. this will allow to set the limits using either input or output value and are clear what the method are taking as parameter values. Both method should do exactly the same thing, so your example, setMinMaxInput(0,999) = setMinMaxOutput(0, 175.8)

If I may play the part of a noob trying to implement this which isn't far off.

 

map(a,b,c,d)

-I think the mapping functon is OK. It seems like a and b only need to 0, 90 or 0,180. I know where I want the servo to be based on whether it is a 90 or 180 deg servo. 

-c and d should define the values you want the servo to move. I use the sliders to figure those out. c and d should define the min and max positions that I want the servo to be limited to. I do not know why these have to be set in a different line with setlimit comands

- c and d should be reversible to account for servo reversing. There should be no need for the invert function.

- having the map function handle these removes two lines of script for each servo. You can define the servo in one line. This would be of help for us noobs.

- you should be able to set an intitial position for a servo before it connects so it does not rail the servo to 0 or 180 which rakes parts.

-if this brakes scripts that is OK, as long as how to correctly use it is shown on the servo help page

 

Noob thoughts

Perry

 

I agree with most of what you said Perry

The initial position for the servo before it connect have been taken into account with the revision of the servo service. It's now possible to do it. But the InMoov services have not been updated yet to use that new functionnality of the servo service. InMoov services are probably the next thing we should review.

Hi !!!

I was thinking maping was so simple, but not at all :)

If we want standardise a little those things, I am like in front of a white page without pen, when I see this 3 samples ( based on values inside InMoov3.Deep.AB.V9.py )

 
head.jaw.setMinMax(42,101)
head.jaw.map(0,180,42,101)
 
leftArm.rotate.setMinMax(40,180)
leftArm.rotate.map(40,180,60,142)
 
 
leftArm.bicep.setMinMax(5,95)
leftArm.bicep.map(0,180,45,140)
 
So lets decompose , what I understand :
head.jaw.map(A,B,C,D)
 
head.jaw.setMinMax(42,101)
setMinMax is actuly used to tell servo physical limits ( ok, based on playin with cursor )
head.jaw.map(0,180,42,101)
We put again limits in the map into C,D,
This is the base I use ( without setMinMax ) , at this time I understand
 
leftArm.rotate.setMinMax(40,180)
leftArm.rotate.map(40,180,60,142)
This thing had caused severe headache to me, it hurt a lot :) . Why previous limits used in C,D and now in A,B ?
 
leftArm.bicep.setMinMax(5,95)
leftArm.bicep.map(0,180,45,140)
Now I'm stucked in front of the screen, jaw opened , and can't speak more like about inverted servos :)
 
 
 

 

That is why I want to clarify the usage of the mapper and setMinMax and that the object of this post

The mapper is always active, even if you don`t define it in your script. By default it`s map(0,180,0,180)

the method setMinMax()  don`t change the mapping value, but will set limits the the value output by the mapper.If you use (only) servo.setMinMax(40,180)  it mean the mapper will still map value 0-180 will still map to value 0-180 (no change) but when the output value is lower than 40, the mapper will make it = 40.

ie. input value of 0 is map to 0, then change to 40 by the mapper

the value of 10 is map to 10 then change to 40

the value of 45 is map to 45 and remain unchanged (>40), so all value <40 are discarded and set to 40

 

using map (0,180, 40,180) have similar behaviour, but is not the same

so with a input value of 0, it will map to a value of 40,

at a input value of 10, it will map to 48 (i did not do the map, but it's about)

at a input 40 it will output maybe 64 and so on until 180 = 180.

so the map() will take an input range and will make it fit into the output range.

 

map() will also set the minMax at the same time

so, it's like doing

map(0,180,40,180) then

setMinMax(40,180)

so any setMinMax() that are before the mapping are overwriten by the map() and have no effect

but if set after the map(), the behaviour will be different.

I hope this help you draw on your page :)

Hello Anthony,

The mappings in InMoov3.Deep.AB.V9.py is odd and that also gave me headaches.

When I was building InMoov02, I used some HK15338 servos which have a limited rotation to 90 degrees.

To fool the pcb driver board, I used some resistors, but the position is not very accurate. Therefore a servo  with resistor might have then a range that exceed 0--->180.

During my build proceess, Calamity was busy setting another way mappings and min max would function.

In fact, later I realised that min and max didn't have any more effect as it was included into mapping. Which I thought was not very logic...

So normally my script should not be looking like this anymore:

leftArm.rotate.setMinMax(40,180)
leftArm.rotate.map(40,180,60,142)
 
leftArm.bicep.setMinMax(5,95)
leftArm.bicep.map(0,180,45,140)
 
But more like this:
leftArm.rotate.map(40,180,60,142)
 
leftArm.bicep.map(0,180,45,140)
 
As you know, there is some default min and max which are already set into the InMoov service.
We definitely need enlightment on that to move on since it has been changed and re changed a lot with past versions

 

I see the light it is not far :)
At this time no need to use setMinMax anymore

Servo physical limits are set into map input
If we want to overide we play with map output
 

By defaut input=output , in this case
leftArm.bicep.map(0,90,0,90)

So, for 1 servo maping we need 4 personnal parameters and a syntaxe like this ?

leftArm.bicep.map(min_input,max_input,min_output,max_output)
 

Config exemple for biceps

[min_input]
bicep=0

max_input]
bicep=90

[min_output]
bicep=0

[max_output]
bicep=90