Referencing range finder to change servo behavior

Hi Everyone,

I am using a Micro Maestro and a Maxbotix ultrasonic range finder. I am writing subroutines to change the sweep behavior of the servo based on the reading from the range finder. The problem is that I don’t understand how to reference a range of readings, for example I want to activate “x” subroutine when the reading is less than 18 and more than 12, etc. I will be taking analog readings on pins 4 and 5 and using those to control servos on pins 0 and 1 respectively. Can anyone tell me the basic function that I need to use and how to use it to do this. I know it can be done with an “if” function and probably even a “while” function, but I don’t know how to implement the less than and greater than part of the equation.

Thanks!
Tim

Hello,

You need to use the command LOGICAL_AND - and it is a little tricky because you need to use some stack manipulation commands to get all of the values into the correct places. For example, this code will test whether the value on the top of the stack is less than 18 and greater than 12:

dup # copy the value, since we need to compare it twice
18 less_than
swap # puts the result of the comparison under the copy
12 greater_than
logical_and # combine the two results
if ...

By the way, what is your Maestro doing with the sonar data? This sounds like an interesting project!

-Paul

Thanks Paul!

I’m building an art installation in which there are four cello-like instruments who’s bowing actions are controlled by the servo, so as the audience gets closer, the bowing changes.

Could you help me a little bit further? I need to use the “if” function at the end of the code you posted to reference a subroutine in which the servo sweeps with a set speed. So that as the range finder senses someone moving closer, the sweep will get smaller and quicker. In fact, as far as that goes, will I have any problems if I write more than one of these functions, for example, one for 6 to 5 feet, one for 5 to 4 feet, etc.? I’m sorry to ask so many questions. I thought I would be able to get this done a lot sooner, but with working a full time job and general life getting in the way, it’s still not programmed, and the show opens on Thursday.

BTW, here is a link to the fundraiser site I used to gather funds for the installation, there is a video there which will explain the art exhibit a bit better: kck.st/dViHRi

Thanks again!!!
Tim

Hello,

Thanks for posting your video. I hope you will post another one when your installation is complete!
Anyway, I can help you more, but you are not really asking a specific question. Are you asking for code like this?

begin
dup # copy the value, so we can use the copy for test 1
dup # copy the value, since we need to do two comparisons on it
18 less_than
swap # puts the result of the comparison under the copy
12 greater_than
logical_and # combine the two results
if mysub1 end

dup # copy the value for test 2
... if mysub2 end
... # test 3, etc

drop # drop the original value
repeat

sub mysub1 ... return
sub mysub2 ... return

I recommend trying to write what you want one small piece at the time, using the single-stepping feature of the Control Center to analyze it if it does not work. If you get stuck, post your best attempt here and we can take a look at it.

-Paul

Hi Paul,

I’m apologize for not being very specific. Each cello has one range finder in it and the idea is that each cello would react to it’s own range finder exclusive of what the others are doing. I am trying to do this with as little hardware as possible, but I’m wondering if I need a controller for each unit. When a unit is reacting to a person within the specified range, I would like the sweep of that servo to continue until that person moves into a different specified range, or out of range completely. If the person moves closer, for example, into another specified range, I would like the servo to discontinue the current sweep behavior and start the new one as directed by the new range. I am attempting to control two units off of one controller. So I have the range finder in units 1 and 2 going to pins 4 and 5 of a controller while the servos are on pins 0 and 1. Is there a way to keep two servos sweeping fluidly directed by two separate analogue inputs?

This is some code I’m working on to describe the sweep motions:

begin
sixFeet
fiveFeet
fourFeet
threeFeet
twoFeet
repeat

sub sixFeet
3 0 acceleration
30 0 speed
  4032 0 servo # set servo 0 to 1.00 ms
  moving_wait
  6464 0 servo # 2.00 ms
  moving_wait
 
sub fiveFeet
5 0 acceleration
40 0 speed
  4437 0 servo # set servo 0 to 1.00 ms
  moving_wait
  6059 0 servo # 2.00 ms
  moving_wait

sub fourFeet
10 0 acceleration
50 0 speed
  4032 0 servo # set servo 0 to 1.00 ms
  moving_wait
  5654 0 servo # 2.00 ms
  moving_wait

sub threeFeet
15 0 acceleration
70 0 speed
  4437 0 servo # set servo 0 to 1.00 ms
  moving_wait
  5249 0 servo # 2.00 ms
  moving_wait

sub twoFeet
10 0 acceleration
50 0 speed
  4842 0 servo # set servo 0 to 1.00 ms
  moving_wait
  4844 0 servo # 2.00 ms
  moving_wait

sub moving_wait
  begin
    get_moving_state
  while
    # wait until it is no longer moving
  repeat
  return

I noticed while stepping through this code that because it goes one line at a time, there might be a delay before it moves on from servo 1 to beginning to move servo 2.

I noticed this code in your user manual for moving a servo to a set point when a potentiometer was in a certain range:

# Set the servo to 4000, 6000, or 8000 depending on an analog input, with hysteresis.
begin
  4000 0 300 servo_range
  6000 300 600 servo_range
  8000 600 1023 servo_range
repeat
 
# usage: <pos> <low> <high> servo_range
# If the pot is in the range specified by low and high,
# keeps servo 0 at pos until the pot moves out of this
# range, with hysteresis.
sub servo_range
  pot 2 pick less_than logical_not    # >= low
  pot 2 pick greater_than logical_not # <= high
  logical_and
  if
    begin
      pot 2 pick 10 minus less_than logical_not   # >= low - 10
      pot 2 pick 10 plus greater_than logical_not # <= high + 10
      logical_and
    while
      2 pick 0 servo
    repeat
  endif
  drop drop drop
  return
 
sub pot
  1 get_position
  return

Could this be modified for my needs, or do you think it would be best to stick with the first code you sent me to determine the correct range from the analogue input?

Again, I apologize if I’m not being specific enough. The arena of servo controlling is very very new to me. Thanks again for all your help!

Tim

Hi Paul

I managed to pull off a successful test. I ran an array of “if” statements and had predictable results from the servo. Here is the code I used:

begin
5248 0 servo # set servo to neutral

 2 get_position
 dup # copy the value, since we need to compare it twice
 144 less_than
 swap # puts the result of the comparison under the copy
 120 greater_than
 logical_and # combine the two results
 if sixFeet

else

2 get_position
dup # copy the value, since we need to compare it twice
120 less_than
swap # puts the result of the comparison under the copy
96 greater_than
logical_and # combine the two results
if fiveFeet

else

 2 get_position
 dup # copy the value, since we need to compare it twice
 96 less_than
 swap # puts the result of the comparison under the copy
 72 greater_than
 logical_and # combine the two results
 if fourFeet

else

2 get_position
dup # copy the value, since we need to compare it twice
72 less_than
swap # puts the result of the comparison under the copy
48 greater_than
logical_and # combine the two results
if threeFeet

else

 2 get_position
 dup # copy the value, since we need to compare it twice
 48 less_than
 swap # puts the result of the comparison under the copy
 24 greater_than
 logical_and # combine the two results
 if twoFeet

else

5248 0 servo

endif
endif
endif
endif
endif
repeat

sub sixFeet
3 0 acceleration
30 0 speed
  4032 0 servo # set servo 0 to 1.00 ms
  moving_wait
  6464 0 servo # 2.00 ms
  moving_wait
return
 
sub fiveFeet
5 0 acceleration
40 0 speed
  4437 0 servo # set servo 0 to 1.00 ms
  moving_wait
  6059 0 servo # 2.00 ms
  moving_wait
return

sub fourFeet
10 0 acceleration
50 0 speed
  4032 0 servo # set servo 0 to 1.00 ms
  moving_wait
  5654 0 servo # 2.00 ms
  moving_wait
return

sub threeFeet
15 0 acceleration
70 0 speed
  4437 0 servo # set servo 0 to 1.00 ms
  moving_wait
  5249 0 servo # 2.00 ms
  moving_wait
return

sub twoFeet
0 0 acceleration
90 0 speed
  4437 0 servo # set servo 0 to 1.00 ms
  moving_wait
  5249 0 servo # 2.00 ms
  moving_wait
return

sub moving_wait
  begin
    get_moving_state
  while
    # wait until it is no longer moving
  repeat
  return

If you see room for improvement, and I’m sure you do, please let me know. Also, if you think that using a second analogue input to control a second servo would work smoothly, please let me know that too.

Tim

Hello,

Thanks for the detailed explanation. It looks to me like you have the code working with one servo at a time, and you want to improve it so that the motions can happen simultaneously.

This is not a trivial change (without just using another Maestro), but it is definitely possible. You will have to re-write your subroutines so that they return immediately rather than waiting for the servo to complete its motion. This means that the subroutine will be responsible for setting the target only if the last motion on that channel has been completed. Which means you are going to have to keep track of what the last motion was, so that you can check that condition even as the sensor readings change.

The pot example uses subroutines that return immediately, but it does not have any memory of the previous state, so it is not going to help you do what you want. I think that I would try using PEEK and POKE to store the last target at a known location on the stack.

Here is an example of that use of PEEK and POKE.

-Paul

Hi Paul,

My show opened on March 25th and went very well, I’ll upload some photos and video soon. I had a problems with my range finders interfering with each other, which I expected and I’m on my way to the gallery soon to fix the problem. I am still interested in calling subroutines with an Arduino, but I’m not sure how to do it. From my research, I see I should probably be using the NewSoftSerial library, but all of the reference material I have found so far appears to be written to send commands to the servos rather than simply call out a subroutine. Can you send me any example code for how this should be done with the Arduino? Is it as simple as Serial.print(0xA7, 2) where “2” is the subroutine number?

Thanks!
Tim

Hello,

I am glad to hear that your show went well, and I am looking forward to seeing the videos!

Anyway, your question is mostly about C++ and the Arduino NewSoftSerial library. I do not know much about Arduino, but doesn’t Print take a string as an argument? You are passing it two numbers. If it needs a string, you should do something like

char command[2];
command[0] = 0xA7;
command[1] = 2;
Serial.print(command);

or

Serial.print("\xA7\x02");

-Paul