Time to change the title of the blog. After a break due to workload it's time to pick this project up again. But the name needs to change. This will be the second change in direction. The original version of the the robot was going to use Python while I looked a Python. After getting a Raspberry Pi 2 with more resources available I switched to Java and Camel since that was my day job.
Over the past few weeks and months we've been looking at Golang (Go) in the office and it's been very impressive. Go would be a perfect fit for the robot due to it's compact nature. So time to rework the old java code to Go.
Raspberry Pi Go Hex Robot
PiHexGo is a Raspbery Pi Golang hexapod robot. This blog tracks all the development of the robot and provides the design and code examples used to develop the robot.
Friday, February 19, 2016
Tuesday, April 21, 2015
AVR PWM Capture of 6 Channel Spektrum RC Receiver
I use the Spektrum receiver out of convienience as I have a Spektrum radio handy. The way the receiver outputs the channels will effect the way I multiplex the signals. When looking at the outputs of the receiver in the Saleae Logic analyzer it can be seen that the channels are not sent at the same time. They are sent sequentially. My device was set up to monitor the channels in numeric order. Channel 0 on the receiver mapping to channel 0 on the analyzer.
See the closeup of the receiver to the right. The channels mappings by default in the first setup are:
Channel 0 - THRO
Channel 1 - AILE
Channel 2 - ELEV
Channel 3 - RUDD
Channel 4 - GEAR
Channel 5 - AUX1
Looking at the output of those channels in the logic analyzer we can see how this particular Spektrum receiver outputs the PWM pulses. It would be interesting to test the output of additional receivers to determine how they output. As this is the receiver I will install in my PiHexJ robot there is not incentive to do that now.
At the end of my previous post Raspberry Pi Based Java Hex Robot: PiHexJ Radio Control Intro - The Control Event I has some open questions regarding the processing of the receiver signals by the AVR microprocessor. I outlined my design for multiplexing the signals to take advantage of the AVR microprocessors input capture feature for PWM decoding. I had concerns about how this would operate.
The multiplexer uses uses three pins to switch the multiplexer input to the the output pin (Z). The input pins high low like a binary counter. By using pins 0-2 on PORTD on PORTD I can use a counter to count between the input channels. The device supports 8 channels I am using 6. By setting the PORTD ouput I can switch the input to the input capture pin (14 - ICP1)
I am using PORTD pins on my AVR microprocessor to drive the multiplexer. PORTD is convenient as it still allows the programmer to connected and keeps the I2C (SDA and SCL) pins free.
My Pin mappings are:
While it works it does take over 51 ms to get all the channels switched to the avr. This is due to the way the capture has to wait for the leading edge of the signals. I experimented with changing the order of the input to the multiplexer to optimize the way the AVR reads the signals. This looked promising when reordering the channels in the Saleae Logic Analyzer.
When optimized this way the total time is reduced to 10.5 ms. This looked promising so I ran a test using the multiplexer to see if it could catch the transitions. In theory it should as the switch time of the multiplexer is rated at 5 nano seconds with a max of 12 nano seconds. In practice however....
In practice it did not work. The processor overhead is too much and channel six above shows that the pulses are not being multiplexed correctly. I tried ordering the input channels staggered to avoid this and still gain. This final output blow looks more promising.
The output of all 6 channels are being sent to the AVR and measured. Shown on channel 6 above. The total time is 31.7ms. While not as quick as the optimal, it does work and it is still faster than over 50ms. This will be the layout I used for the initial RC control of the robot.
Spektrum AR610 Receiver |
See the closeup of the receiver to the right. The channels mappings by default in the first setup are:
Channel 0 - THRO
Channel 1 - AILE
Channel 2 - ELEV
Channel 3 - RUDD
Channel 4 - GEAR
Channel 5 - AUX1
Spektrum Receiver Output |
At the end of my previous post Raspberry Pi Based Java Hex Robot: PiHexJ Radio Control Intro - The Control Event I has some open questions regarding the processing of the receiver signals by the AVR microprocessor. I outlined my design for multiplexing the signals to take advantage of the AVR microprocessors input capture feature for PWM decoding. I had concerns about how this would operate.
Multiplexing the PWM signals from the AR610 Spektrum Receiver
The test bed is shown in the picture below. I am using a 74HC4051 8 Channel Multiplexer to multiplex the Spektrum AR610 6 channel receiver. The questions to address are will there be issues with overlapping signals causing incorrect results on switching and what will the latency be.
Test Setup for RC Receiver Multiplexing |
74HC4051 Pin Description |
74HC4051 Function Table |
AVR Mega 328 pin diagram |
My Pin mappings are:
AVR | Multiplexer |
PD0 | S0 |
PD1 | S1 |
PD2 | S2 |
PD3 | E |
PB0 | Z |
And PD4 is used to connect to the raspberry pi to signal when the control input has changed.
For the AVR firmware C code check out my github the file is in the hexfirmware repository https://github.com/margic/hexfirmware/blob/master/Firmware/firmware.c rather than the main PiHexJ repository. The AVR is initialized with the input capture interrupt enabled. The following code is triggered when the ICR interrupt is fired. It grabs the value from the timer register and stores it in an array channels[]. The index, plex, is then updated. The value is compared and if changed the update count is updated. I do this so I don't trigger updates to the raspberry pi if no input changed have occurred. PORTD pin PIEVENT is turned on to signal the pi to read the channels via I2C as per my design. PIEVENT corresponds to pin PD4.
Finally the multiplexer is stepped to the next input.
ISR(TIMER1_CAPT_vect){ // Input capture interrupt handler // first time is triggered in leading edge if(PINB & (0x01)){ // pin high // clear counter and set to trailing edge TCNT1 = 0; TCCR1B &= ~(1<<ICES1); }else{ // get counter from ICR1 // reset to look for leading edge uint_fast16_t counter = ICR1 + 47; // add some counts for processor over head // if(channels[plex] != counter){ updatedCount += 1; } channels[plex] = counter; if(plex == NUMCHANNELS - 1){ if(updatedCount != 0){ PORTD |= (1 << PIEVENT); } updatedCount = 0; plex = 0; }else{ plex += 1; } switchMultiplex(); TCCR1B |= (1<<ICES1); } } void switchMultiplex(){ uint8_t temp = PORTD & 0xF0; PORTD = temp | plex; }
The code works as intended. The following diagram shows the output on channel 6 of the logic analyzer.
Multiplexed Signal at AVR ICR Pin Channel 6 |
Optimized Receiver Channels |
Failed Multiplexing Channel 6 |
The output of all 6 channels are being sent to the AVR and measured. Shown on channel 6 above. The total time is 31.7ms. While not as quick as the optimal, it does work and it is still faster than over 50ms. This will be the layout I used for the initial RC control of the robot.
Conclusion
There will be additional latency as the Raspberry Pi will read the values from the AVR controller to determine the values the receiver is receiving from the radio. In other applications this would not be acceptable. However in this application a estimated 50ms latency before the robot responds should negligible. This can be revisited if it proves to be a problem.
Next Steps
The next tasks are to make the Raspberry Pi respond to the AVR signal (PD4 going high) and have it request the values via I2C. I am currently developing the simulator file that will test the AVR code. I will publish it when complete.
Thursday, April 16, 2015
PiHexJ Radio Control Intro - The Control Event
Adding radio control to the PiHexJ robot is not straight forward. In a simple radio controlled object there is a 1:1 relationship between the movement of a control stick to the movement of a control servo. Push the stick forward servo moves forward. For the walking robot moving the stick forward should result in a coordinated/animated movement of up to 18 servos on the robot.
My challenge is to interpret that single input and turn it into a series of motor movements. In this post I'll look at the conceptual design for capturing the control input. I designed PiHexJ around the concept of an event bus to allow for multiple sources to generate input events the robot should react to. This is implemented in Java on the Raspberry Pi with a Guava Eventbus and Camel used to integration with the bus and other elements.
EventBus Logical |
This design supports the concept of a control event being created in the RC module and posting that event to the even bus. A controller should then process that event and "something" should happen. So how to generate a control event for the event bus?
Creating a Control Event
The Raspberry Pi is not going to be very effective accurately timing the PWM signals from the receiver. I needed something more reliable. I had an AVR micro controller I expected would be suitable for the job. I intended to use this read receiver and sensor inputs and provide these to the Raspberry Pi for processing. The best way to do this would be to use a time capture feature on the AVR. There is a good document providing an example of this available from Atmel Using Timer Capture to Measure PWM Duty Cycle. This method is an excellent way as it allows the PWM to be captured using interrupts minimizing the overhead on the device. However there is a problem. The AVR I am using only has a single timer available. This means it's impossible to handle decoding the 6 channels of Radio Control receiver I am using.The PiHexJ does have one advantage over a typical radio controlled device. Latency is not a problem. I have flown radio controlled aircraft, planes and helicopters. In those applications it's essential that the latency of the radio control is kept to a minimum as the aircraft should respond immediately to input. This is less of a problem in the robot. Each response to a control input is a series of motions that take some time to complete. Therefore latency should not be so perceptible. That provided the option of using a multiplexor between the receiver and the AVR micro controller as shown in the diagram below.
I intend to sample each channel then step the multiplexer to the next input and sample the pulse from it. RC PWM is easier to read as duty cycle is not important only the duration of the pulse counts. Pulse frequency is approximately 50hz so in theory it should be possible to sample receiver output somewhere in the region of 100-200ms.
Design of PiHexJ Radio Control Components |
Building the Test Bed
Prototyping the Radio Control |
The second picture shows the initial layout of the micro controller (28pin IC) and the multiplexer (16pin IC) with the receiver connected via green signal wires.
Multiplexing the receiver signals |
The next steps are:
- Add power and add the feed to leds to show multiplexer channel selection.
- Add GPIO event signal from AVR to Raspberry Pi to signal a sample is ready
- Add mechanism to read samples from AVR with Raspberry PI
- Generate the control event in the Raspberry Pi.
Questions to Address
The purpose of the testing is to determine if multiplexing can operate without significant latency?
Can the multiplexer switching be monitored via LEDs?
How can the AVR handle multiplexed PWM timer capture. Can I detect if the multiplexor switches onto a channel mid way during an input pulse?
Wednesday, April 15, 2015
Optimizing Java PCA9685 driver using Saleae Logic 8 Analyzer Part 2
PiHexJ Java PCA9685 Driver
At the end of the last post I had changed the driver to update all the servo channel registers in one sequential write to the I2C Bus. Instead of writing <<address>><<value>><<address>><<value>> I changed it to write <<address>><<value>><<value>>
In the last test I was writing address = 0 and writing all the registers. The PCA9685 has a the auto increment flag bit 5 on mode 1 register. I was not sure if when set I had to start at 0 and increment all registers. I did not know if the register auto increment was reset on a I2C stop command.
I reworked the driver to allow this test to allow the driver to update starting from the first updated servo from the servo update buffer. The PCA9685 did works as expected. I can write servos starting from any channel and write the registers in a sequence. This mean updating the driver to optimize this write pattern. In my initial test it took 3ms to update a single servo without register auto increment. It took 6.5ms to write all registers using auto increment. This is a much faster write rate and the pay is when writing 3 or more servos.
The time for a single update is 0.56ms. This is way faster that both the 6.5ms sequential write or the 3ms for a single. With this scenario sequential write with register auto increment is always faster.
Writing 3 servos took 1.275ms. These were three adjacent servo channels 0,1&2. It would take longer if writing 0 and 16 as the sequential write has to start at the lowest channel and auto increment over each channel to the last channel.
I have to have to add tests for single write operations to ensure I haven't broken those operations even thought I would recommend using the optimized sequential write mode. I have yet to see a Java PCA9685 driver that works this way.
At the end of the last post I had changed the driver to update all the servo channel registers in one sequential write to the I2C Bus. Instead of writing <<address>><<value>><<address>><<value>> I changed it to write <<address>><<value>><<value>>
In the last test I was writing address = 0 and writing all the registers. The PCA9685 has a the auto increment flag bit 5 on mode 1 register. I was not sure if when set I had to start at 0 and increment all registers. I did not know if the register auto increment was reset on a I2C stop command.
I reworked the driver to allow this test to allow the driver to update starting from the first updated servo from the servo update buffer. The PCA9685 did works as expected. I can write servos starting from any channel and write the registers in a sequence. This mean updating the driver to optimize this write pattern. In my initial test it took 3ms to update a single servo without register auto increment. It took 6.5ms to write all registers using auto increment. This is a much faster write rate and the pay is when writing 3 or more servos.
Optimized Single Servo Update
With the optimized driver starting from the first updated servo the times are improved further. The screenshot before shows the write of a single servo.Update single servo 0.56ms |
Optimized Multiple Servo Update
The following screen is showing updating 3 servos.
3 Servo sequential servo update 1.275ms |
Writing 3 servos took 1.275ms. These were three adjacent servo channels 0,1&2. It would take longer if writing 0 and 16 as the sequential write has to start at the lowest channel and auto increment over each channel to the last channel.
Changes to the Driver
With the results of the tests I made the following:- Added UpdateSequential flag - It is unlikely to mix the modes between single servo updates vs sequential servo update. It made sense to make this a constructor on the servo driver. This means the driver update mode is set when the driver is created.
- Used the buffer to buffer update events - the update device method checks the first update and uses that channel to set the first register address. It then loops through the updates taking either the new settings in the update buffer or uses a cached value to write the current value back to the register to avoid writing empty values when sequentially writing registers.
- Added test for the new writing methods
I have to have to add tests for single write operations to ensure I haven't broken those operations even thought I would recommend using the optimized sequential write mode. I have yet to see a Java PCA9685 driver that works this way.
Sunday, April 12, 2015
Optimizing Java PCA9685 driver using Saleae Logic 8 Analyzer Part 1
In my previous post Saleae Logic 8 to Monitor Servo PWM and I2C to PCA9685 I was able to verify my servo updates were performing as expected by monitoring both the I2C traffic and the resulting PWM pulses on the servo channels. The was one observation that gave cause for concern was the time it took to update to update a single servo. It takes 4 register updates to change the servo PWM pulse. That means 4 I2C updates passing the device address, register address and value to write in the register.
In the logic analyzer screenshot below the green and yellow channels show the I2C traffic for the single servo update. The duration is annotated in the window on the right. The duration is approx 3ms. This doesn't sound a lot. However a typical leg move will require updates to at least 3 servos and possibly upto 9. That means a min of 18ms for all servos to be updated. Sequencing a smooth motion with a 18ms+ latency will not be effective.
The PCA9685 data sheet describes bit 5 of the MODE1 register enables auto-increment.
The foot note describes the following...
When the Auto Increment flag is set, AI = 1, the Control register is automatically incremented after a read or write. This allows the user to program the registers sequentially.
I wanted to see the effect of using the auto increment function by setting this flag. The datasheet provides an example of using this with the flag clear.
This shows setting the device address, followed by the registers starting with register 0 to set auto increment. I ran a test using this approach writing all registers up to register 69 that corresponds to the 16th servo channel.
I ran a test setting all the registers from register 0 as per the datasheet example to evaluate the time take. The screen shot below from the Saleae logic analyzer shows the I2C traffic. The time for writing all 69 registers is 6.5ms compared to 3ms for 4 registers individually. This is a significant improvement and will be way more effective for creating smoother motions when animating the servos to perform leg movements.
The next test I will try to further optimize the writes is to determine if I have to start at register zero when using auto increment. The datasheet is not specific about this but does hint at the possibility as it mentions auto increment also works on registers 250 - 254 before rolling over to register 0. What is not clear is if the I start at servo 0 register, register 6 and right some registers what will happen.
The Salae logic analyzer will help determine the what will happen to the servo outputs. I will post an update once I have the test results. I have checked in the code used to test this use case. It obviously will need further updates once the optimization testing is done.
In the logic analyzer screenshot below the green and yellow channels show the I2C traffic for the single servo update. The duration is annotated in the window on the right. The duration is approx 3ms. This doesn't sound a lot. However a typical leg move will require updates to at least 3 servos and possibly upto 9. That means a min of 18ms for all servos to be updated. Sequencing a smooth motion with a 18ms+ latency will not be effective.
Time annotation showing duration of 1 servo update |
MODE1 Register |
The foot note describes the following...
When the Auto Increment flag is set, AI = 1, the Control register is automatically incremented after a read or write. This allows the user to program the registers sequentially.
I wanted to see the effect of using the auto increment function by setting this flag. The datasheet provides an example of using this with the flag clear.
Write all registers |
I ran a test setting all the registers from register 0 as per the datasheet example to evaluate the time take. The screen shot below from the Saleae logic analyzer shows the I2C traffic. The time for writing all 69 registers is 6.5ms compared to 3ms for 4 registers individually. This is a significant improvement and will be way more effective for creating smoother motions when animating the servos to perform leg movements.
Time annotation showing duration of sequential 16 servo update |
The Salae logic analyzer will help determine the what will happen to the servo outputs. I will post an update once I have the test results. I have checked in the code used to test this use case. It obviously will need further updates once the optimization testing is done.
Subscribe to:
Posts (Atom)