Today, we tried to set the coefficients of our PID algorithm. In order to do this, we used the Ziegler-Nichols method. The first step is to set the integral and the derivative gains to zero. Then, we increase the value of the proportional gain, to obtain the control loop oscillating constantly. Unfortunatly, we didn’t succeed in obtaining the special value of P (proportional gain). It seemed we had still some bugs on the Kalman filter. Samuel and Axel tested again the good working of the Kalman filter (we had to change some coefficients of the Kalman since we put the card on the Copter), and fixed it.
Samuel made a basic program, in order to change easily the PID coefficients, while the Copter is running : we can now observe the effects of the changes in live.
We worked also on some improvements of the code: for the STM32 and for the Kanade algorithm, to prepare the future work & debug. For example, the UART was configured at 1 000 000 bps, so the STM32 had only 72 instructions to perform between each UART interrupt. Since we only send 150 packets/s (~20 bytes), 57600 bps speed would be fine, and more convenient.
Today, I finalized the emission of the commands for the motors, from the Gumstix to the STM32 and their reception (check the validity of the packet and if it’s valid, update the commands sent to the motors).
Then, with Samuel, we added 0MQ exchanges on the Gumstix between Kalman and the PID and between the PID and the UART.
On the Gumstix, the program that handles the UART has two threads. One receives data from the sensors from the STM32 and sends it -thanks to 0MQ- to the program computing Kalman. The other one, receives the commands of the motors from the PID and sends them to the STM32.
We also attached the PCBs on the copter:
We configured Kalman with the new axes and the offset of roll and pitch angles.
Finally, we all tested the PID. The algorithm seems okay, however, we’ll have to set each coefficient properly.
We’re trying to control roll and pitch angles with a low thrust (in order to prevent any accident). Then, we’ll add a control on Z thanks to the Sharp sensor.
We’ve been working these last few days on the PCB, to display pictures and to save and load them from flash.
First, we developped a set of functions to erase, write and read the flash memory, using a SPI protocol. After testing it with text strings we displayed on the screen, we tried with pictures. However, the STM32 RAM is not big enough to handle a whole picture : we have at most 20kbytes RAM, and one picture takes 25kbytes (132*132 pixels of 12 bits, 4 for each RGB channel). So we read the flash per 256 bytes blocks that we send to the LCD screen, until transfer completion. Similarly, we write flash per 256 bytes blocks when receiving a picture from the UART connection.
In order to have a simple protocol for writing pictures, we do not allow two pictures to share a same memory sector (smallest erasable area). Since each sector is 4 kbytes wide, we use 7 sectors for each picture : that’s about 3 kbytes too large. However, this allows to store 146 pictures in the flash, which should be enough for our project. We did not work yet on the possibility of storing smaller pictures which could be used to refresh a small screen area. It may be useful for animations.
Finally, we use the screen configuration options to rotate pictures by 90, 180 or 270 degrees, changing the way the screen controller reads the RAM.
Moreover, we wrote a small program which uses OpenCV library (which is already used on the beagleboard for face detection and recognition) and converts a picture (jpeg, png, bmp,…) in a 132*132 12 bits picture and saves it in a file that can be sent to the PCB by UART.
After some over-the-internet discussions, we finally agreed on the following list of goals and milestones :
A interface task is placed between SVOX Pico and ALSA on the beagleboard – 04/18 Thibault
The beagleboard and Casper PCB communicate over an UART link – 19/04 Alain
We store and read images to and from the flash memory on the Casper PCB – 20/04 Alain
We are capable of having the beagleboard read a text file out loud – 20/04 Thibault
The beagleboard is able to recognized predetermined vocal commands – 21/04 Thibault
The LCD display adapts itself according to the robot’s orientation – 22/04 Alain
We are capable of controlling the robot’s tilt angle - 22/04 Thomas
We are capable of controlling the robot’s orientation (azimuth) – 22/04 Thomas
The robot as been assembled, and the devices (camera, PCB, …) are on board – 23/04 Thomas
We can make the robot track a face (the robot’s orientation follows slow mouvements) – 25/04 Alain The API we previously defined is accessible from LUA scripts -27/04 Thibault
The robot is capable of accessing a simple mail box (without encryption protocols) – 28/04 Alain
The robot has a simple remote http setup interface – 29/04 Thomas
We use a LUA script, using the API, to drive the robot for the demo – 29/04 Thomas The servomotors are being driven by a linux device driver on the beagleboard – 29/04 Thibault
As Samuel noticed that there is not formal description of the algorithm, I will do this now.
First of all, all the messages use the packet structure giving by Cédric here. So, they all have an ID and we know who is the sender. I will start by showing all the messages and then I’ll explain the algorithm.
List of the differents messages
There are a large number of messages and they all have specifics arguments.
For the algorithm, I use « interfaces » which are aliases of UARTs. The UARTs are used by the IrDA while the interfaces are used for the algorithm. For example, interface 0 represents the north (and is in reality UART 2), interface 1 is the east (but in reality it’s UART 5), etc.
All the blocks know :
their own ID (given by the STM32_ID),
their leader ID,
their orientation (which « interface » represents the north)
the number of member in their network
their « nearest » neighbors (who is in front of my UART2,3,4 and 5) : the Interface table.
They have a network table (for each ID, they know the position (i,j) and who was the leader when they had the information) and an election table (for each ID, they know his state in the election) . We will try to replace theses tables by a list in the future.
Every message except PING and PONG is answered by an ACK message. The PONG message contains the PING ID so it is itself a ACK. If there is no ACK, the message is resend.
When starting all of this, the block starts the timer 5 of the STM32 for the synchronization. (see section 4)
Each block sends PING messages in the 4 directions and start a timer for each direction.
When receiving a PING message, a block sends back a PONG message as fast as he can.
When it receives a PONG message, the block knows who is his neighbor and changes his Interface table. Furthermore, the block stops the timer.
If the block is aware that there was a neighbor but there is no answer at the end of the timer, it sends a RESET_NETWORK message (see section 5).
If this interface wasn’t ready before, the block has to check if he knew the neighbor (in order to notice a turn of the other block) or not (in order to start an election.)
Explanation of this check :
When receiving a PONG message from a « new neighbor », the block checks if the « new neighbor » was a neighbor before or not. This is checked by comparing the current interface table and the saved interface table.
If the interfaces tables don’t match, there is an election.
Else, the block sends to the block TURN (myID, rotation). The rotation is the difference of index between the current interfaces and the saved interfaces.
To compare the interfaces, the block waits a little bit for PONG message from all the interfaces.
3. The election
When it’s the first PONG from a new neighbor, an election takes place.
When starting an election, the block starts a timer. If his candidature isn’t refused at the end of the timer, he is elected and he broadcasts the information.
In the election, the blocks send messages for their « network » (all the blocks with the same leader). Here, the « network » can be a network of one block (so the leader ID is his ID).
The block sends a START message to his neighbors not in his « network » (and this is broadcast). All blocks sends a CANDIDATE message with his leaderID and the numbers of members in his network.
The winner of the election is the network with the larger number of blocks in his network. If this isn’t enough to decide , the lower leader ID is declared winner.
When receiving a CANDIDATE message, the block checks if the candidate can win or not. If he can’t, the block broadcasts a REJECTED message. If he cans, the block broadcasts the CANDIDATE message.
When receiving a REJECTED message with his leader ID, the block stops the timer : he can no longer win the election.
At the end of the timer, one network is the winner. It sends the ELECTED message and everyone broadcasts it.
4. After the election
After the election,the leader is in (0,0). If he was part of a network, he sends his information to the others by sending NETWORK messages.
For every NETWORK message received, the block analyzes it : if the leaderID of the NETWORK message isn’t his leaderID, it drops the message. Else, if the message contains a good information, the block changes his network table and transmits it.
Then he sends the POSITION message : [YOUR_POSITION leaderID, myOrientation, yourI, yourJ, myInterface]. The block which receive this calculate his new orientation (with « myOrientation », « myInterface ») and change his position in his network table. Then it calculate the position of his neighbors and sends the message.
The leader sends TIME messages (which are broadcasted if they are « young » enough.. That explains the synchroID) from times to times. In his TIME message, there is his current time (according to the STM32). The block that receive this message update his new « current time » (with a function which take into account the emission time) and then sends his current time to others, etc.
5. If someone leaves
If some block leaves the network, there is no PONG message to the PING message of his former neighbor. The former neighbor notices that and broadcasts a RESET_NETWORK message.
This message is broadcast to all the network and means that every data (leaderID, network table, …) must be restarted; the interface table is saved (for the TURN mechanism). A new election starts soon after. (plus the orientation, network information, synchro…).
The turn mechanism isn’t implemented yet and the ACK mechanism has to be implemented very soon.
Then, there will be messages for the applications (games, firmware upgrade, …).
Today, we made some improvements in the way casper moves. Now, it will reach a specified target (direction + bending) passing by every intermediary position, preventing casper from brutal moves… Moreover, we added a way of specifying the movement’s speed.
Besides, now we can control Casper remotely. We are currently using a Zigbee module but eventually it will be controlled by a wired UART connexion with the BeagleBoard. We specified the following protocol :
Command format :
x represents a number
A command can’t exceed 10 chars
S : start
E : end
A : absolute command indicator
R : relative command indicator
X : special command indicator
Absolute command :
xxx.xx : direction xxx (0 to 360) – bending xx (0 to 90)
Example : > SA145-90E
Casper moves to direction 145°, bending 90°
Relative command :
Exemples : > SR+300-10E
Casper’s direction increases by 300° and bending decreases by 10°
Special commands :
0 : YES
1 : NO
2 : 180°
3 : 360°
Example : > SX0E : Casper says mechanically yes.
Tomorrow, we will add speed management to these commands.
We had some hardware problems with our Led Driver today, but finally (thanks Alexi) we could test our programs for SPI and initialization of our Led Driver. Now we can control lights from the Led Driver. We are mapping light commands(from the Led Driver) to corresponding CAN messages.
We have progressed on the work on the CAN bootloader. We have been able to send data from the PC to the final card passing through a dummy card which transforms UART messages to CAN messages.
We have been able to simulate the sensors card on our TP card. Now we are able to send an event (pressing the botton) to central card.
Yesterday, Alexis gave us 3 MB Led so that we could check the IrDA of the blocks. So far, Cédric achieved to communicate through UART 2 and 3 without any problem. For UART 4 and 5, he encountered problems to receive messages due to missing characters in the UART data register.
Benjamin started to check the LED driver so that we could light the MB Leds soon.
Both of them used the oscilloscope to understand signals from the component.
Meanwhile, I fixed some bugs in the algorithm and my tests were successful on the GLiP blocks. Thoses blocks can only communicate through UART 2 & 3 so my job is now to test the algorithm on fully operating GLiP blocks (with their 4 IrDA transceivers), waiting for Cédric to test on our MB Led blocks. It seems that FreeRTOS scheduler is blocked when I tried to launch 16 tasks at once. I would try to fix this without reducing the numbers of tasks, cause each one is important…
Aujourd’hui, avec Bertrand, puis rejoins par Samuel et Axel, nous nous sommes concentrés sur la communication Gumstix/STM32. Nous avons enfin réussi à connecter comme nous le voulions les deux cartes.
Nous avons d’abord dû activer le GPIO 10, le pin correspondant n’étant pas configuré par défaut en tant que GPIO. Nous avons donc choisi de le configurer à chaque démarrage par l’intermédiaire d’un script shell, exécuté automatiquement, réalisant l’accès en mémoire nécessaire à la configuration, à l’aide de l’utilitaire memdev2.
Après avoir débugué notre carte à l’oscilloscope, et résolu un problème de soudure, nous avons donc enfin pu communiquer entre nos deux cartes. Les premiers essais ne furent malheureusement pas très fructueux : alors que les échanges de paquets se faisaient sans soucis entre PC et carte de TP STM32, avec le même protocole, nous avons à présent plus de 10% de perte de paquets (parfois 25%), et surtout, de longs moments (se décomptant en secondes) qui s’écoulent avec presqu’aucun paquet de valide. Les cartes sont donc inutilisables dans ces conditions.
Après analyse des données échangées, il semblerait que de nombreux octets ne sont pas reçus par la Gumstix, ceux-ci se perdant, à un niveau qu’il nous reste encore à déterminer. Exemple : sur des paquets qui devraient être de 21 octets, nous avons parfois observé plus de la moitié des octets perdus. Et ce, malgré le fait que nous avons redescendu la vitesse de l’UART à 115200 bps.
Les différentes pistes que nous avons à explorer demain, pour essayer de résoudre ce problème, sont les suivantes:
- Alexis nous a suggéré d’essayer de modifier le préscalaire de l’horloge concernée. Les quartzs n’étant pas forcément très fiables, il se peut que le souci vienne d’une vitesse de communication approximative, qu’on pourrait essayer de compenser de cette manière.
- Axel m’a suggéré de modifier légèrement le protocole. En effet, si on perd un octet, on perd au minimum deux paquets. Je rappelle le protocole :
On envoie un octet de start : 0xfe. Puis un octet identifiant le paquet : 0x4b. Ensuite on lit le nombre d’octets correspondant. Si on perd un octet, on va déborder sur le paquet suivant dans la lecture, et on ne le lira donc pas non plus. La solution proposée par Axel serait d’enregistrer la position de tout octet 0xfe croisé dans le contenu d’un paquet, et si le paquet est invalide, revenir à cette position pour « récupérer » ce paquet. Toutefois cela ne résoudra pas le problème, il s’agit juste de perdre moins de données à cause du protocole.
D’autre part, nous allons abandonner l’altimètre. Il semblerait que celui-ci soit trop peu précis, et trop complexe à utiliser. Nous allons donc plutôt faire des tests avec un détecteur à ultrasons ou un sharp, pour voir si nous obtenons quelque chose de plus satisfaisant.
Nous avons également été chercher l’hélicoptère, afin de pouvoir tester les commandes des moteurs très bientôt.