Most of the bots etc employs multiple servos. It is possible to do more with a lot less?
This is a Sampai that uses a single long sculling oar called a yuloh for both propulsion and steering. The operator row the oar back and forth similar to a fish's tail.
Analysis
When the oar pushes against the water, it causes an opposite reaction force. This force can be resolved into a vertical (Fy) and horizontal (Fx) vector components along the axis of the boat. Fx moves the boat forward while Fy tries to move it sideways.The sideways force (Fy) cancels out over time when the oar swings symmetrically side to side.
What happens when the Oar is offset to one side?
The vertical component no longer cancels out (Fy' - Fy >0), and so the boat turns right.
When the angle ɵ is small, most of the force is in the side way direction. By slowing down the swing during small ɵ, this thrust can be minimized.
So what happens if the forward motion is faster than the return?
There will be more force produced in the forward pass. (In theory) the boat can be made to move backwards.
Could all of this be duplicated using a single servo using some clever programming?
Links:
http://www.theholidayspot.com/chinese_new_year/crafts/chinese_sampan/ How to make a model
http://freepages.genealogy.rootsweb.ancestry.com/~fassitt/cranks/cranky_sampans.html
I have written a table driven servo driver for my STM8 breakout board.
ServoStep.Table point to a table that contains the From, To values as well as the number of Steps.
const ServoTable_t Forward_Tbl[]= { { TIM2_SERVO_SCALE(0),TIM2_SERVO_SCALE(30),5 }, { TIM2_SERVO_SCALE(30),TIM2_SERVO_SCALE(90),1 }, { TIM2_SERVO_SCALE(90),TIM2_SERVO_SCALE(90),5 }, { TIM2_SERVO_SCALE(90),TIM2_SERVO_SCALE(-30),20}, { TIM2_SERVO_SCALE(-30),TIM2_SERVO_SCALE(-90),1}, { TIM2_SERVO_SCALE(-90),TIM2_SERVO_SCALE(-90),5}, { TIM2_SERVO_SCALE(-90),TIM2_SERVO_SCALE(0),15}, { END_TABLE,END_TABLE,0}, };
The forward thrust is produced when the servo moves rapidly between (30° to 90°) and (-30° to -90°). The servo moves slowly between these end points to minimize the unwanted thrust.
Once the Timer and interrupt is initialized, channel 2 of timer2 (pin 20) is used to generate a PWM waveform to drive a servo. The period is set at 20ms (50Hz) and the waveform is 1ms to 2ms.
The following IRQ gets called at the end of each cycle and the servo is incremented/decremented. If it reach the maximum steps, then the next entry of the table is executed. If it reaches the end, it wraps around to the beginning of the table.
// Call everytime TIMer2 overflows (50Hz) @far @interrupt void TIM2_Update_IRQ(void) { TIM2->SR1 &= ~TIM2_SR1_UIF; // Clear update IRQ if(!ServoStep.Table) // turn off servo { ServoStep.PWM = SERVO_OFF; ServoStep.Index = 0; } else { if(!ServoStep.Steps) // Initialize PWM parm { if(ServoStep.Table[ServoStep.Index].From == END_TABLE) ServoStep.Index = 0; ServoStep.PWM = ServoStep.Table[ServoStep.Index].From; ServoStep.Delta =(ServoStep.Table[ServoStep.Index].To - ServoStep.Table[ServoStep.Index].From)/ ServoStep.Table[ServoStep.Index].Steps; ServoStep.Steps = ServoStep.Table[ServoStep.Index].Steps; ServoStep.Index++; } else // Step the servo { ServoStep.PWM += ServoStep.Delta; ServoStep.Steps--; } } if ((ServoStep.PWM >= SERVO_MIN) && (ServoStep.PWM <= SERVO_MAX)) { // Set PWM TIM2->CCR2H = ServoStep.PWM >>8; TIM2->CCR2L = ServoStep.PWM; } }
I made a "C" shaped cutout on a foam board. I screwed in a standoff with a pan head screw on one end and glue it to a styrofoam tray. I used some White Glue. (It wasn't strong enough, so I added in some hot glue to the side afterwards.)
The "Tower 9g microservo" is mounted on a small piece of clear plastic (from a dollar store picture frame).
The servo arm is attached to the oak with the wire from a couple of paper clips.
I used the insert from a "wire spicing" connector to extend the length of the paper clip.
The servo is powered from a USB battery pack. The inertia from heavy battery pack helps to stabilize the erratic movement of the boat.
I ported the IR Remote code from my Automatic audio source switching project. Straightly speaking this has nothing to do with propulsion, but it makes life a bit easy for running a demo without a tether.
I have to make a few minor modifications as STM8S003 (8-bit) has less resources than STM32F030 (ARM M0).
- I have to share timer 2 for both servo and IR remote. Servo PWM is set for 50ms timer reload (0x9c40). Measuring input pulse width is slightly more work as the code has to keep track of whether or not a timer reload has occurred.
- STM8S003 input capture cannot be triggered on both edges, so the code has to toggle between rising and falling edge.
- The usual I/O register, IRQ difference.
I am using my TV remote with NEC2 IR protocol. Each byte sent is followed by its 1's complement for error checking. NEC2 extends the 8-bit address code to 16-bit by sneaking it into the 2nd byte. Byte 3 is 1's complement of command byte in byte 2.
The following shows the raw IR code captured from the debugger. The device code for my Insignia (Bestbuy house brand) TV is 0x8615 and the command code is 0x15.
IR Commands for the menu navigation buttons: Left = 22, Right = 21, Up = 66, Down = 67, Select = 24.
My logic analyzer can only decode NEC1 IR protocol, so it complains about address error and stopped processing. :(
I added a 7 segments LED for visual feedback for the IR commands. F= forward, L = Left, r = Right and Off = Stop.
IR receiver is at lower left hand corner.
The dog almost eat my homework. STVD delete 1 of the source file, failed to update 2 files before it crashed. It did managed to write the deleted file to a temp file. Ooops. :(
I got the IR remote code and servo code working together. The row boat can now be controlled from my TV remote. I have added left turn and right turn to the code.
I have recorded a new video of the boat in action for the contest entry:
Source code have been pushed to github: https://github.com/FPGA-Computer/rowboat