PDA

View Full Version : [Question(s)] On Bioloid: How to read the sensors' value with Matlab?



cybernetics
07-26-2011, 03:40 PM
I want to read the input of my IR sensor with MATLAB, but I'm not capable.

I've checked on the Bioloid site (http://support.robotis.com/en/) under Software Help > Embedded C > Example > IR Sensor, the C exemple:


PORTA &= ~0x80; // ADC Port 1 IR ON

_delay_us(12); // Short Delay for rising sensor signal
ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear
ADCSRA |= (1 << ADSC); // AD-Conversion Start

while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete

PORTA = 0xFC; // IR-LED Off

printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

but I don't get this code neither. :confused:
For example, what does this means: |= ?

iBot
07-26-2011, 04:28 PM
It is amazing you go to a University where they neither help you with C, or with Google.

You need a C tutorial !

Look here:
http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B

It's not your fault, I guess Laval goes on the list of degree factories! I hope the :veryhappy: is cos you got a scholarship, else you are being ripped off.

tician
07-26-2011, 09:57 PM
Which port? There are six available ADC ports (labeled with a number of '|' on the CM-510 exterior). You can only read from one at a time and must select one before you attempt to acquire a value. Check my post (http://www.robotis.com/xe/qna_en/69858) on the Robotis QnA for a compilation of the preprocessor definitions/macros from the Robotis example code. I tried to explain almost everything available to the CM-510/700. All else fails, RTFM (http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf).

Seriously though, a c/c++ tutorial will do wonders. I still refer to this site (http://www.cplusplus.com/doc/tutorial/) frequently for the specifics of the standard c library functions.



// The 'DDRA', 'PORTA', and 'PINA' are preprocessor definitions specific to the AVR
// microcontroller architecture (internal registers): RTFM for more
// Read/Write to 'PORTA' controls the usage of pull-up resistors when in input mode
// Read/Write to 'PORTA' also controls the state (high or low) when in output mode
// Read/Write to 'DDRA' controls whether Pins on PORTA are in input or output mode
// Read from 'PINA' gives the current state of the pins on PORTA (high or low)
// Write a '1' to any bit of 'PINA' toggles the current state of the pin if
// in output mode (bit-wise XOR).
// So in earlier code, the line:
DDRA |= 0x80;
// sets PIN7 of PORTA to be an output pin.

// So later, when we encounter:
PORTA &= ~0x80;
// This is bit-wise AND'ing the current value of PORTA with the one's complement
// of 0x80 and storing it back into PORTA
// expanded form: PORTA = (PORTA&0x7F);
// This clears bit-7 of PORTA, which forces PIN7 of PORTA to a low state while
// leaving all other pins on PORTA to be exactly as they were.

// You are attempting to use "PORT 1" of the CM-510 with an IR sensor, so you have
// to select which ADC port/channel you intend to use (fortunately, Robotis made
// their "PORT 1" use ADC channel 1)
ADMUX = 1;
// Selects channel 1 as the current ADC port/channel as input to the converter

_delay_us(12); // Short Delay for rising sensor signal

// Next, you are logical OR'ing the ADCSRA register of the ATmega2561 with
// '1' left-bit shifted 'ADIF' times (where ADIF is a preprocessor definition for
// an integer value corresponding to the bit location within the register.
// I do not recall its exact value and do not feel like downloading the
// datasheet again to find out.)
// e.g. (1<<4) == (0b0000 0001<<4) == (0b0001 0000) == (0x10) == 1*(2^4)
// if ADIF were == 4, the expanded form would be: ADCSRA = (ADCSRA | (0x10));
// which sets bit-4 of ADCSRA
ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear
// The next line begins the new conversion
ADCSRA |= (1 << ADSC); // AD-Conversion Start

// while ( the ADC complete flag bit is clear ); // !(0)==1, !(N!=0)==0; (0 to 1 and nonzero to 0)
while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete

PORTA = 0xFC; // IR-LED Off
printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

cybernetics
07-27-2011, 01:42 PM
I've been trying for a couple of hours to traduce this in Matlab and here is what I've got:


bitand(wait_time,~0x80); % ADC Port 1 IR ON

wait_time(0.012); % Short Delay for rising sensor signal


ADCSRA=bitand(ADCSRA,(bitsll(1,ADIF))); % AD-Conversion Interrupt Flag Clear
ADCSRA=bitand(ADCSRA,(bitsll(1,ADSC))); % AD-Conversion Start

while( ~(ADCSRA & bitsll(1,ADIF)) ); % Wait until AD-Conversion complete

PORTA = 0xFC; % IR-LED Off

fprintf('%d\n', ADC); % Print Value on USART
wait_time(0.050);

And it doesn't work -_-
How do I put the "0x80" in Matlab?

tician
07-27-2011, 03:21 PM
http://forums.trossenrobotics.com/picture.php?albumid=87&pictureid=679



The Robotis Embedded-C code for the CM-5/510/700 is meant to run on the CM-5/510/700 only. 'ADCSRA', 'DDRA', 'PORTA', 'PINA', 'ADC', and many others are all C-preprocessor definitions/macros which make sense only when compiled for the AVR ATmega2561 microcontroller on the CM-5/510/700. You cannot convert the Embedded-C examples to MATLAB or any other programming language that cannot be run on an AVR microcontroller.

As to "0x80", I have two words for you: 'F1' and 'google'.

Hopefully I will be able to fix my last post when I get home to a functional internet connection.

cybernetics
07-27-2011, 03:31 PM
ok I thought there was a way to read sensors' values with Matlab (hence my original question)

Oh I didn't know F1 could open a tab.

By the way, I do not post any questions before having looked on the Internet for at least 1 hour. I kind of get what is 0x80, but I just don't really know how to convert it in numbers.

I'll try using Matlab and C at the same time.

Thanks.

tician
07-27-2011, 04:29 PM
Gave myself a good smack upside the head for somehow NOT reading the 'Matlab' in the thread title.

'F1' - the nearly universal key to press for "HELP".


I'm just rambling on now...
Smallest possible unit is a 'bit'. There are two states '1' and '0'. There are four bits in a 'nibble', which is a single hexadecimal digit. There are eight bits, or two nibbles, in a byte.

When considering only a single byte (two hexadecimal digits):
0x80 [hexadecimal] == 0b1000 0000 [binary (notation varies)] == (1<<7) [c/c++ bit-shift left operation] == 1*(2^7) [unsigned decimal] == 128 [unsigned decimal] == -128 [signed decimal]

More signed conversion examples (that should be correct):
0x7F [hex] == 127 [unsigned decimal] == 127 [signed decimal]
0x8F [hex] == 143 [unsigned decimal] == -113 [signed decimal]
1000 1111 => (one's complement or bit-wise negation) => 0111 0000 => (plus 1) => 0111 0001 => -(64+32+16+1) == -113
0x82 [hex] == 130 [unsigned decimal] == -126 [signed decimal]
1000 0010 => 0111 1101 => 0111 1110 => -(64+32+16+8+4+2) == -126
0xFF [hex] == 255 [unsigned decimal] == -1 [signed decimal]
1111 1111 => 0000 0000 => 0000 0001 => -(1) == -1

cybernetics
07-28-2011, 09:03 AM
ok thanks a lot.

I'm going to program in C finally in order to use the IR sensors and the gyro, and then I'm going to try to export the data in a Excel file and then access it with Matlab to do graphics.

When I run the program to read the IR sensor in C, this line

while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion completenever (well I've wait a couple of minutes) stops. Do you know why?

Thanks

Edit: The value always stays zero.

cybernetics
07-29-2011, 10:07 AM
Now it works for port 1, but it gives be value between 0 and 50 instead of 500 (as with RoboPlus Manager), but it's still good.

cybernetics
07-29-2011, 02:47 PM
However, when I put more than one sensor, let's say two IR sensors, the data vary between 0 and 10 and are really not precised. Do you know why? Here's a part of my code:




while(1)
{
iteration+=1;

DDRA |= 0x80;

PORTA &= ~0x80; // ADC Port 1 IR ON

ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // ADC Enable, Clock 1/64div.

ADMUX = 1; //ADC_PORT_1; // ADC Port 1 Select

_delay_ms(12); // Short Delay for rising sensor signal
ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear
ADCSRA |= (1 << ADSC); // AD-Conversion Start

while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete

PORTA = 0xFC; // IR-LED Off

printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

if(iteration>4500)
break;
IR_data[1][iteration]=ADC;



DDRA |= 0x80;

PORTA &= ~0x80; // ADC Port 1 IR ON

ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // ADC Enable, Clock 1/64div.

ADMUX = 2; //ADC_PORT_2; // ADC Port 2 Select

_delay_ms(12); // Short Delay for rising sensor signal
ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear
ADCSRA |= (1 << ADSC); // AD-Conversion Start

while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete

PORTA = 0xFC; // IR-LED Off

printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

if(iteration>4500)
break;
IR_data[2][iteration]=ADC;

tician
07-29-2011, 04:51 PM
As for your code, you are not setting the second IR PORT's pin to an output or turning on its LED.



DDRA |= 0x80;
PORTA &= ~0x80; // ADC Port 1 IR ON

...then again...
DDRA |= 0x80;
PORTA &= ~0x80; // ADC Port 1 IR ON

I also noticed you were trying to store the ADC values on the CM-510 in a 2D array. At 10-bit per sample, you need two bytes to store each sample. With 'N' samples (IR PORTS) per 4500 iterations, you will need (9000*N) bytes for storage. The ATmega2561 on the CM-510 has only 8kibibytes of Internal Data SRAM (arranged as 4096 16-bit locations). Simple answer, don't try to store them on the CM-510. Not simple answer, you can possibly keep the program small enough to stay in the bootloader section and write the data to the FLASH memory, but what is the point? You are already outputting the data over the serial port, so why store it?

Also, why are you checking 'if (iteration>4500)' after each sample? You incremented 'iteration' only at the top so it won't change until the loop reaches the top again. If you did decide to increment it between samples, why would you use a 2D array (you would be wasting lots of memory storing data along a diagonal of multiple 6x6 arrays)?


I went through the header file (http://www.robotis.com/xe/qna_en/69858) again and simplified it even more. The preprocessor definitions/macros should all work, but I have not tested all of them yet. Give them a try.



#include "CM_510_700.h"

ADC_ENABLE;
EXT_PORTS_ENABLE;

while (1)
{
iteration+=1;

EXT_PORT_ON(EXT_PORT_1);
USE_ADC_PORT_1;
_delay_ms(12);

ADC_BEGIN;

while(ADC_INCOMPLETE);

EXT_PORT_OFF(EXT_PORT_1);

printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

if(iteration>4500)
break;

IR_data[1][iteration]=ADC;

EXT_PORT_ON(EXT_PORT_2);
USE_ADC_PORT_2;
_delay_ms(12);

ADC_BEGIN;

while(ADC_INCOMPLETE);

EXT_PORT_OFF(EXT_PORT_2);

printf( "%d\r\n", ADC); // Print Value on USART
_delay_ms(50);

if(iteration>4500)
break;

IR_data[2][iteration]=ADC;
}

cybernetics
08-01-2011, 09:19 AM
I also noticed you were trying to store the ADC values on the CM-510 in a 2D array. At 10-bit per sample, you need two bytes to store each sample. With 'N' samples (IR PORTS) per 4500 iterations, you will need (9000*N) bytes for storage. The ATmega2561 on the CM-510 has only 8kibibytes of Internal Data SRAM (arranged as 4096 16-bit locations). Simple answer, don't try to store them on the CM-510. Not simple answer, you can possibly keep the program small enough to stay in the bootloader section and write the data to the FLASH memory, but what is the point? You are already outputting the data over the serial port, so why store it?Interesting. I keep them in memory because I need to transfer these values to Matlab to do graphics, but in fact, I don't really know how to do it. I try using the matPutVariable (http://www.mathworks.com/help/techdoc/apiref/matputvariable.html) and I even try to use an example (http://www.mathworks.com/help/techdoc/apiref/rmvd_matlablink__45.html) code, but I keep getting "No such file directory" errors when I include "mat.h".

avr-gcc.exe: automode.o: No such file or directory
avr-gcc.exe: binmode.o: No such file or directory
avr-gcc.exe: crt0.o: No such file or directory
avr-gcc.exe: gcrt0.o: No such file or directory
avr-gcc.exe: textmode.o: No such file or directory
avr-gcc.exe: textreadmode.o: No such file or directory
avr-gcc.exe: automode.o: No such file or directory
avr-gcc.exe: binmode.o: No such file or directory
avr-gcc.exe: crt0.o: No such file or directory
avr-gcc.exe: gcrt0.o: No such file or directory
avr-gcc.exe: textmode.o: No such file or directory
avr-gcc.exe: textreadmode.o: No such file or directory And yet I seem to have include all libraries in the configuration options. And I've looked at solutions posted in lots of forums, but nothing seems to work.


Thanks for the code. You should edit: "USE_ADC_2" for "USE_ADC_PORT_2", but everything else was good.

EDIT: I've put the GYRO on port 3 and 4 and the two IR on port 5 and 6, and I cannot read the IR values - but when the IR are on port 1 and 2, I can; and I did change USE_ADC_PORT_1 and 2 for 5 and 6.


Also, why are you checking 'if (iteration>4500)' after each sample? You incremented 'iteration' only at the top so it won't change until the loop reaches the top again. If you did decide to increment it between samples, why would you use a 2D array (you would be wasting lots of memory storing data along a diagonal of multiple 6x6 arrays)?Oups. I've removed one of these lines of code.

tician
08-01-2011, 02:36 PM
You seem to be missing the point again. There is no operating system on the CM-510. Nor is there a file system, a shell/terminal, or any other PC-type device or program on the CM-510. The CM-510 consists of an 8-bit processor core, a bunch of peripherals (ADC, USART, SPI, etc.), and a very limited amount of RAM (far too little RAM to create a very large matrix). You cannot create a Matlab matrix on the CM-510 and then copy/paste the file from the CM-510 to a PC (as if the file were stored on a USB thumbdrive). The only way you are going to get the values off of the CM-510 is to send them as binary or plain-text over the RS-232 serial port.


The code below will print out all six ADC port values in comma separated format:
"Counter,ADC_1,ADC_2,ADC_3,ADC_4,ADC_5,ADC_6\r\n"

It is up to you to create a small program to run on the PC to read in the plain-text ASCII characters as they are printed over the serial port (could use C/C++, python, or any other programming language you want that can access the serial port/USB2Dynamixel in RS-232 mode). You can then easily import the CSV data into Matlab (maybe not in real time though, I cannot remember).

You can change which External Port to use with the IR sensors, but you must remember to change the 'EXT_PORT_N' and 'i==(N-1)' lines to match the actual External Port usage.



#include "serial.h" // The Robotis serial library
#include "CM_510_700.h"

unsigned short int Counter=0;

ADC_ENABLE;
EXT_PORTS_ENABLE;

while (1)
{
_delay_ms(50);
printf("\r\n%d", Counter++);

for (int i=0; i<6; i++)
{
// Because USE_ADC_PORT_1 is "ADMUX = 1", we can add an integer to this
// in the loop to end up with "USE_ADC_PORT_1+i;" == "ADMUX = 1+i;"
USE_ADC_PORT_1+i;

// Enable the IR LED on the IR Sensor on PORT 1
if (i==0)
{
EXT_PORT_ON(EXT_PORT_1);
_delay_ms(12);
}
// Enable the IR LED on the IR Sensor on PORT 2
else if (i==1)
{
EXT_PORT_ON(EXT_PORT_2);
_delay_ms(12);
}

ADC_BEGIN;

while(ADC_INCOMPLETE);

// Disable the IR LED on the IR Sensor on PORT 1
if (i==0)
{
EXT_PORT_OFF(EXT_PORT_1);
}
// Disable the IR LED on the IR Sensor on PORT 2
else if (i==1)
{
EXT_PORT_OFF(EXT_PORT_2);
}

// Print ADC Value on USART in comma separated format
printf(",%d", ADC);
}
}

cybernetics
08-02-2011, 01:47 PM
It is up to you to create a small program to run on the PC to read in the plain-text ASCII characters as they are printed over the serial port (could use C/C++, python, or any other programming language you want that can access the serial port/USB2Dynamixel in RS-232 mode).

I've learned Matlab and Maple in a course at school and I don't have any other programming courses. I've learned a bit of C and C++ by myself, but not enough to be able to do such things. I've looked on the Internet how to do such program and I would need more time than I have to be able to do it. So I'll copy paste the result. Thanks anyway.

tician
08-02-2011, 03:48 PM
Here (http://www.mathworks.com/help/techdoc/matlab_external/f62852.html) and here (http://www.mathworks.com/help/techdoc/ref/serial.fscanf.html).

I have not used Matlab in a while so you would need to heavily debug the code. You would also need to replace the ',' (comma) in the CM-510 code's output with a ' ' (space), and remove the '\r' (it's a windows thing, and utterly useless (or even dangerous) elsewhere).



% not sure this is right syntax/function, but just create an empty matrix 65536 rows by 7 columns
data=zeros(65536,7);

% open a serial port (replace 'COM1' with whatever your USB2Dynamixel is named)
ser=serial('COM1', 'BaudRate',57600,'Parity','none');
fopen(ser);

for i=1:65536
% I doubt this is right either. "[1,7]" might need to be replaced by "[1,inf]" or simply "7" or "inf"
data(i,:)=fscanf(ser, '%d %d %d %d %d %d %d', [1,7]);
end

% display it or plot it however you want
% first column should be the counter (from 0 to 65535)
data

It should at least be a start with a language you sort of know.

cybernetics
08-04-2011, 01:41 PM
I've tried several ways to code it, and I've tried with both with RS232 and TTL, but I always get this error:


??? Subscripted assignment dimension mismatch.

tician
08-04-2011, 03:24 PM
Definitely does not work yet, but getting closer?...


data=zeros(65536,7);

ser=serial('COM1', 'Baudrate', 57600, 'Parity', 'none');
fopen(ser);


for i=1:65535
% get line of data as string without 'LF' terminator
tline=fgetl(ser);
% extract data separated by ' ' into a cell array
carray=textscan(tline, '%d%d%d%d%d%d%d', 'delimeter', ' ');
% hopefully extract each value in the cell array into a column
% in row 'i' in the matrix named 'data'
for j=1:7
data(i,j)=cell2mat(carray(j))
end;
end;


fclose(ser);
delete(ser);
clear ser;

cybernetics
08-08-2011, 09:57 AM
It's not getting any data this way neither.


Warning: A timeout occurred before the Terminator was reached.

tline =

''


??? Error while evaluating TimerFcn for timer 'timer-1'

Error using ==> textscan
First input can not be empty.EDIT: Why is it 65535? It's 2^16 addresses but still...