I am currently trying to figure out a configuration in controlling servos connected to Arduinos using Nodebot johnny-five.io hosted on an RPi. My main goal is to make a hexapod from scratch; I don't like the idea of kits because it's all cookie cutter code and parts that you put together and it's more or less a remote controlled car where you didn't learn anything.
I just learned the basics about servos (which I'm selecting servos over stepper motors). And unfortunately, as a default, servo speed cant be controlled via PWM, only position. So the way around this is to create a loop that increments the servo 1 degree (or more) at a time with an X ms delay in the loop until the servo has reached the desired position. Now, this is fine and all if you're only controlling one servo at a time or X amount of servos at a time for moving to one set position to another. But I'm shooting for a fluid motion here; I want one leg to start moving before another leg has stopped. True fluidity in motion, to accomplish this, I would need an infinite loop that would check on input states set by control commands that the API would receive.
The problem here is that while loops are not asynchronous. So, I need to find a way to kick a loop off that sets the different servos at different speed ranges and different positions, and at the end of the loop checks for new input state updates. And I need to do this without creating a memory leak.
One way would be to create a set of dependency scripts that worked asynchronously for each of the servos (3 servos per leg, 6 legs, 18 servos thus 18 mini dependencies), but I'm not sure if that would be too much overhead or put much strain onto the RPi.
Am I overthinking this?
You could create a simple servo class and give each instance its own speed and starting position using an 'update' method. Use a longer delay to have the servo move slower. In the main loop you can continuously check for some input, tell the servos to move if necessary, using the update method.
#include <Servo.h>
class HexapodLegServo
{
Servo servo; // Servo instance
int pos; // Position/angle of the servo
int delay_millis; // 'Delay' between each update
long prev_millis;
int degree_change; // Angle increase each update
int start_pos; // Position to start at
int pin; // Pin servo is connected to
public:
HexapodLegServo (int whichPin, int delayMillis=15, int startPos=0)
{
pin = whichPin;
delay_millis = delayMillis;
start_pos = startPos;
degree_change = 1;
}
void attachToPin()
{
servo.attach(pin);
setStartPos();
}
void setStartPos() // Set initial position of the servo
{
servo.write(start_pos);
pos = start_pos;
}
void update() // Servo sweeps from end to end, and back
{
if (millis()-prev_millis > delay_millis)
{
prev_millis = millis();
servo.write(pos);
pos += degree_change;
if ((pos <= 0) || (pos >= 180))
{
degree_change *= -1; // Start moving in the reverse direction
}
}
}
};
// Main script
const int BUTTON = 4; // Button on pin 4
HexapodLegServo right_leg(9);
HexapodLegServo left_leg(10, 30, 90);
void setup()
{
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP); // Using a button to tell servos when to move
right_leg.attachToPin();
left_leg.attachToPin();
}
void loop()
{
if (digitalRead(BUTTON) == LOW) // If button is pushed (can be any type of input)
{ // update the position of the servos continuously
right_leg.update();
left_leg.update();
}
}
i am almost done with my pixel pacman
and im trying to figure out how the monsters should avoid the collision with the power up and the energizer pills
if you run the game you will see dots which represents
yellow is the pacman
red are the ghosts
purple are simple pills
green are energizer pills(if you eat them you can eat the ghosts).
my problem here is that the im having hard time that the monsters will not "eat" the red or green dots. here is my main struct
struct info {
int curX;
int curY;
char color;
int alive;
};
also every monster move is represented like this
if ( rand1 == 1){ //random up
if(infos[i].curY > 50){
for (j = 0 ;j < 18 ; j++){
set_board(infos[i].curX,infos[i].curY,0);
set_board(infos[i].curX,--infos[i].curY,4);
if the monster is the same location as the pixel it will override it( a thing that i dont want to happen)
It seems that your issue is a display one, not a game logic one. From what I understand, when the ghost goes over the pellets, they get erased. It seems that the answer here is simply to redraw the pellets after the ghost goes over them. You call this pixel pacman: does that mean that every entity is a displayed as a one pixel ? If so, the solution is simply to turn the pixel back to its original colour instead of black (I guess that is the colour you chose when there is nothing to display) when the ghost has moved to the next location.
hi i planning to made a multiple servo controlling with serial as control trigger signal on AVR with C and codevision
but when the trigger is true, the servo running in crazy loop, it back to original position (0 degree) instead of stay on desired position, my tutor give me hint to use "wait ... until" statement with the old data comparison but i'm not found the way to utilize it yet on google
because utilizing break; at the end of the if left the chip freeze until it reset
and the old code(it runs the servo forth and back continously)
while (1)
{
while(UCSRA & (1<<RXC))
{
// Place your code here
//data=UDR;
PORTC=UDR;
data=UDR;
//PORTB=data;
} ;
if (data== 0x0A || data== 0x0B)
{
if (data== 0x0A)
{
old_data=data;
PORTA=0x00;
PORTA.1=1;
movservo0(90,7);
movservo1(15,3);
}
if (data== 0x0B)
{
old_data=data;
PORTA=0x00;
PORTA.1=1;
movservo0(15,3);
movservo1(90,7);
}
}
}
as for movservo0 (another movservo() almost had same code)
void set_servo1(unchar derajat)
{ unchar n;
servo2=1;
delay_us(750);
for(n=0; n<derajat; n++)
{
delay_us(12);
};
servo2=0;
delay_ms(10);
}
void movservo0(unsigned char sudut, unsigned char speed)
{
unchar i;
set_servo1(sudut);
for (i=1;i<=sudut;i+=speed){
set_servo1(i);
delay_ms(100/speed);
}
}
This new code is a bit better.
The top of the while(UCSRA & (1<<RXC)) loop is fine this way. The loop body will only be entered if there is a character to be read. (Although, I'm not sure why you are expecting to read '\n' or vertical tab.)
A minor problem is the way the UDR is read. The act of reading UDR erases the contents. So you should be using
data=UDR;
PORTC=data;
The jumping of the servos appears to be in the moveservo() function. This function always sets the angle to 1 and then gradually increases the angle of the servo until it reaches the desired angle.
setservo() appears to be an attempt to perform PWM to drive a servo, but it doesn't work correctly. To keep the servo at a desired angle, you have to keep switching the pin from 0 to 1 at the correct times, not just once like this function does. Have you looked at the PWM functions of the timers? You just set these up and they run in the background. An alternative is to use a timer to set an interrupt to wake up and toggle the pins as you need.
If you want to do the switching of the pins without interrupts, then you should be using delays in the while(1) loop itself. Just use the setservo() functions to change some variables.
while(1)
{
// read the UART if ready and set the target values
// this part only runs occasionally
while(UCSRA & (1<<RXC))
{
// etc
}
// The rest of the loop body runs every time
// adjust the servo values toward the target values
// use a counter to determine if to adjust during this loop iteration
// e.g., a slow speed counts to a higher number before adjusting
delay(750);
for(int i = 0; i < NUM_INTERVALS; ++i)
{
// decide whether to set pins to 1 or 0 based on the servo values
delay(INTERVAL);
}
}
I don't know about your particular servos, but they typically have a period where most of the time the pin is 0, and then a short time in the period where the pin is 1. You will need to adjust NUM_INTERVALS and INTERVAL and 750 to add up to the correct length of time.
There is so much wrong with this snippet, it is hard to start. It is difficult to say why the servo is moving, as it is only ever "moved" to the same value every time. (Unless, you have omitted some code that sets it to some other value.)
Typically, when receiving UART, the processor should wait until the character is received. This is accomplished by this
while(UCSRA & (1<<RXC) == 0);
Note the ; creating an empty body of the while loop. This is probably what your tutor meant. When the flag is set, then the loop exits and the data is ready to be read.
while(UCSRA & (1<<RXC) == 0);
data = UDR;
Next, you have a block which looks like you meant to be part of a while loop, but it isn't. The body of the loop is the single statement abbove it. The block gets executed every time.
{
if(data=0x0a)
{
olddata=data;
movservo0(90,7); //move servo to certain degree
}
}
Another error is the condition in the if statement. It looks like you are trying to test if data is 0x0A, but that is not what is happening. Instead, you set data to be 0x0A, and then execute the inner part every time. The condition you probably want is if(data == 0x0A). Note the == instead of =.
So your code is equivalent to
while(1)
{
while(ucsra & (1<<RXC)) data=udr; // maybe read UDR into data
data=0x0a; // set data anyway
olddata=data;
movservo0(90,7); //move servo to certain degree
}
Again, for the jumping servo, I suspect some code that is omitted here that is also runs every time. Or else, the moveservo() function has a problem itself.
I am writing a program that interacts with the LED's connected on my breadboard that will simulate a "ball" bouncing up and down. Everything has worked correctly up until the point where the height of the bounce decrements (simulating loss of momentum). The LED's keep lighting on past the 9th LED and start acting bizarre.
The problems arose in this line of code:
digitalWrite(ledPin[activeLED], HIGH);
activeLED += dir;
if (activeLED == bounceLimit){
dir = -1;
bounceLimit--;
}
if (activeLED == 0) {
dir = 1;
}
The activeLED is the LED being lit, they light in succession to the top until it hits the bounceLimit variable, where it changes direction and lights downward until zero and starts upwards again. The problem lies that in the next time it goes up, the bounceLimit should be set lower at 8 (then 7, 6, etc.) but it seems that it isn't set any lower and the LED's continue to light to the top and then start to act weird.
I managed to fix it by adding another condition
if (activeLED == bounceLimit && dir = 1){
dir = -1;
bounceLimit--;
}
But why is the extra condition needed? Wouldn't the limit be hit only when the direction is upwards (1) anyways?
So if bounceLimit is (say) 9 to start, when you hit the top you take one away to make bounceLimit equal to 8. But then when the LED goes down in the very next iteration the active LED is then also 8, which triggers your if statement again.
I am working on a robot, it's part of the summer robotics workshop in our college. We are using C-STAMP micro controllers by A-WIT. I was able to make it move, turn left, turn right, move backward. I have even managed to make it go along the black tape using a contrast sensor.
I send the robot at 30-45 degrees toward the black tape on the table and it aligns itself and starts to move along the black tape. It jerks a little, probably due to my programming logic below, it's running a while-loop and constantly checking if-statements, so it ends up trying to turn left and right every few milliseconds, which explains the jerking part. But it's okay, it works, not as smooth as I want it to work but it works! Problem is that I can't make my robot go into a rectangular path of the black tape. As soon as it reaches the corner of the black tape it just keeps going straight instead of making left or right turn.
My 2 sensors are located right underneath the robot, next to the front wheel, almost at the floor level. It has "index" value ranging from 0 to 8. Eight being the brightest contrast and zero being the darkest contrast. So when the robot moves into the black-tape-zone, the index value drops, and based on that, I have an if-statement telling my robot to either turn left or right.
Here's my attempt. To avoid confusion I didn't post the entire source code, but only the logical part responsible for the movement of my robot along the black tape.
while(1) {
// don't worry about these.
// 10 and 9 represent Sensor's PIN location on the motherboard
V = ANALOGIN(10, 1, 0, 0, 0);
V2 = ANALOGIN(9, 1, 0, 0, 0);
// i got this "formula" from the example in my Manual.
// V stands for voltage of the sensor.
// it gives me the index value of the sensor. 0 = darkest, 8 = lightest.
index = ((-(V - 5) / 5) * 8 + 0.5);
index2 = ((-(V2 - 5) / 5) * 8 + 0.5);
// i've tweaked the position of the sensors so index > 7 is just right number.
// the robot will move anywhere on the table just fine with index > 7.
// as soon as it drops to or below 7 (i.e. finds black tape), the robot will
// either turn left or right and then go forward.
// lp & rp represent left-wheel pin and right-wheel pin, 1 means run forever.
// if i change it from 1 to 100, it will go forward for 100ms.
if (index > 7 && index2 > 7)
goForward(lp, rp, 1);
if (index <= 7) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
// this is the tricky part. i've added this code last minute
// trying to make my robot turn, but i didn't work.
if (index > 4) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
}
}
else if (index2 <= 7) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
// this is also the last minute addition. it's same code as above
// but it's for the 2nd sensor.
if (index2 > 4) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
}
}
}
I've spent the entire day trying to figure it out. I've pretty much exhausted all avenues. Asking for the solution on stackoverflow is my very last option now.
Thanks in advance!
If you have any questions about the code, let me know, but comments should be self-explanatory.
This is my goForward function in case anyone wonders:
void goForward(BYTE lp, BYTE rp, WORD t)
{
WORD i;
for(i = 0; i < t; i = i + 1){
PULSOUT(lp, 400, 1, 1);
PULSOUT(rp, 800, 1, 1);
PAUSE(17);
}
}
UPDATE: Here's what I've come up with so far. I've erased all my if-statements that I've posted earlier and decided to write the logic from scratch:
// if there's enough bright light in both sensors at the same time
// robot will move forward forever.
if (index > 7 && index2 > 7)
goForward(lp, rp, 1);
// but if there's not enough bright light anymore (i.e. reached black tape)
// proceed to the else-statement.
else {
// if left sensor detects the black tape then turn right
// if doesn't detect the black tape then keep going forward
if (index2 <= 7)
turnRight(lp, rp, 1);
else
goForward(lp, rp, 1);
// if right sensor detects the black tape then turn left
// if it doesn't detect the black tape then keep going forward
if (index <= 7)
turnLeft(lp, rp, 1);
else
goForward(lp, rp, 1);
}
// The reason for turnLeft and turnRight is to keep robot re-alligning
// to the black tape. It will happen so fast (every 1ms) that the jerking
// shouldn't even be noticeable.
You need to trap the sudden occurrence: [cycle n] "I see the tape" -> [cycle n+1] "I don't see the tape" that happens when you encounter a corner.
A state machine is a good way of handling this type of problem. With a state machine the code for a specific state is isolated, it only runs when the state is true. This isolation prevents "cross-talk" and gives you one known code block for each state.
Given your example the flow might be something like:
:Loop
State == Moving forward and on tape.
read sensors
delta = abs(valueLastCycle - valueThisCycle);
if (delta >= bigChangeValue){
// the tape was just gone.
// change state and handle this situation.
CurrentState = suddenValueChange;
}else
if (index <= 7) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
else if (index2 <= 7) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
}
...
State == suddenValueChange
...
code that handles sudden value change event
maybe:
Stop Robot;
Move backwards slowly until on tape or distance x
etc...
Goto Loop
Increasing the scan rate might seem to help....but the faster the robot moves the faster your scan rate....i.e. You might still jump from on tape -> off tape in which case your current code flounders.
My guess is that the goForward immediately after the turnLeft/TurnRight "cancels" the turning?
It depends on how turning is implemented though
Make sure your "plan" makes sense before worrying about the code.
Start by moving the robot around by hand and observing when the sensors pass over black and white areas. Try to come up with a behaviour, and simulate it by hand. If this doesn't work as you want, then you need to revise the behaviour.
A suggestion: You may wish to add some more loops to ensure that if something seems wrong, the robot corrects itself before resuming normal behaviour. (E.g. rather than turning right/left for 100ms, do that behaviour as long as it takes for the sensors to see the right thing.)
Does your while loop run fast enough to catch a corner? If both sensors report that they are on the tape, and in the next cycle both sensors are off the tape, you can't detect that, right? The sensor reports the same values (8) for being on the tape and off the tape?
Try making your code faster. Without seeing the entire code, it's hard to make a recommendation, but it seems that you might be evaluating if statements that are not necessary. Add an ELSE after the first IF, once you know that you are going straight.
It seems that your goforward implementation is blocking the loop for too long. 1 does not mean run forever, it does one pass of the for-loop, and then you PAUSE 17 (msec?). What's the PAUSE for? Remove it. This probably causes the jerking, and it prevents the next set of sensor values from coming in.
Right now, by the looks of your code, every time the robot sees black, it goes forward forever. I'm not really sure how going forward is implemented.
In pseudocode, your code says:
if you see black
go forward forever
if you don't see black
turn left or right
Do you see how your robot would go forward forever with that kind of logic? Again, I have no idea how going forward is implemented so I could be wrong.