by limor » Thu Nov 18, 2010 1:08 pm
by limor
Thu Nov 18, 2010 1:08 pm
I spent some time looking through the OpenServo source code thinking that these guys at openservo know their stuff when it comes to servo control so why not use their code. and they sure do.
The main loop of openservo looks like this:
- Code: Select all
main() {
...
for() {
if (adc_position_value_is_ready()) { ... do stuff ... }
if (adc_power_value_is_ready()) { ... }
if (twi_data_in_receive_buffer()) { .... }
}
}
registers.c - a set of global variables used everywhere. Instead of just using a bunch of global variables, the registers.c functions provide two functions: registers_read_word(), registers_write_word() - they disable interrupts, then they read/write from the global variables, then re-enable interrupts. this ensures there is no race condition where an interrupt changes values as they are being read or written to.
There are also helper routines like fixed_multiply() which i think are used to multiplies floats or longs.
There are 3 interrupts in use:
- ADC sampling is done every 10ms (timer0). this is also the timeframe for changing any control parameters. The advantage of doing it only 100 times/sec is that you get 6-10 point change in the potentiometer sample. so you can use this to determine speed with some degree of precision (if you sample 1000 times/sec you will see 1-2 point change and this makes it hard to determine speed without a speed estimator or a brutal average).
- TWI - for communications over i2c. In our AX12 this is irrelevant since we use the UART interface at 1mhz.
- pulse - pin0changed interrupt + uses timer2 interrupt to measure pulse length
The openservo can be compiled to different requirements by setting /unsetting #defines.
- React to RC pwm signal instead (in addition to) I2C. The pulse pwm signal may be coming from RC remote control for example.
- Apply IPD control for position control
- Apply PID control for position control (difference?)
- Estimate velocity by emulating the servo mechanical/electrical model (uses floating point calculations)
- Motion control - Control speed of servo and get it to follow a smooth pre-defined curve (Beizer curve, there's a utility you can download from openservo.org to help create these curves with up to 8 waypoints). go through predefined way-points at given time. so you can control a complex real-time path. this is similar to humanoid/hexapod gait control paradigm only the path is stored in the servo. personally i'd prefer to do this from an embedded linux controlling the speed and curves and modifying them to react to the environment.
So if you want IPD control, you just need to set the #define accordingly. the main loop will call the routine and the routine will change the PWM duty cycle that goes to the motor, every time it is called. this happens 100 times/sec every time the ADC is interrupt happens.
About "pulse control" - it waits for RC pwm signal (1ms - 2ms) representing 0..1023 available target positions and goes there at full speed.
- Code: Select all
registers_write_word(REG_SEEK_POSITION_HI, REG_SEEK_POSITION_LO, pulse_position);
registers_write_word(REG_SEEK_VELOCITY_HI, REG_SEEK_VELOCITY_LO, 0);
I spent some time looking through the OpenServo source code thinking that these guys at openservo know their stuff when it comes to servo control so why not use their code. and they sure do.
The main loop of openservo looks like this:
- Code: Select all
main() {
...
for() {
if (adc_position_value_is_ready()) { ... do stuff ... }
if (adc_power_value_is_ready()) { ... }
if (twi_data_in_receive_buffer()) { .... }
}
}
registers.c - a set of global variables used everywhere. Instead of just using a bunch of global variables, the registers.c functions provide two functions: registers_read_word(), registers_write_word() - they disable interrupts, then they read/write from the global variables, then re-enable interrupts. this ensures there is no race condition where an interrupt changes values as they are being read or written to.
There are also helper routines like fixed_multiply() which i think are used to multiplies floats or longs.
There are 3 interrupts in use:
- ADC sampling is done every 10ms (timer0). this is also the timeframe for changing any control parameters. The advantage of doing it only 100 times/sec is that you get 6-10 point change in the potentiometer sample. so you can use this to determine speed with some degree of precision (if you sample 1000 times/sec you will see 1-2 point change and this makes it hard to determine speed without a speed estimator or a brutal average).
- TWI - for communications over i2c. In our AX12 this is irrelevant since we use the UART interface at 1mhz.
- pulse - pin0changed interrupt + uses timer2 interrupt to measure pulse length
The openservo can be compiled to different requirements by setting /unsetting #defines.
- React to RC pwm signal instead (in addition to) I2C. The pulse pwm signal may be coming from RC remote control for example.
- Apply IPD control for position control
- Apply PID control for position control (difference?)
- Estimate velocity by emulating the servo mechanical/electrical model (uses floating point calculations)
- Motion control - Control speed of servo and get it to follow a smooth pre-defined curve (Beizer curve, there's a utility you can download from openservo.org to help create these curves with up to 8 waypoints). go through predefined way-points at given time. so you can control a complex real-time path. this is similar to humanoid/hexapod gait control paradigm only the path is stored in the servo. personally i'd prefer to do this from an embedded linux controlling the speed and curves and modifying them to react to the environment.
So if you want IPD control, you just need to set the #define accordingly. the main loop will call the routine and the routine will change the PWM duty cycle that goes to the motor, every time it is called. this happens 100 times/sec every time the ADC is interrupt happens.
About "pulse control" - it waits for RC pwm signal (1ms - 2ms) representing 0..1023 available target positions and goes there at full speed.
- Code: Select all
registers_write_word(REG_SEEK_POSITION_HI, REG_SEEK_POSITION_LO, pulse_position);
registers_write_word(REG_SEEK_VELOCITY_HI, REG_SEEK_VELOCITY_LO, 0);