lucysrausch / hoverboard-firmware-hack

New Hoverboard Firmware Hack. Now written from scratch and generally much better.
GNU General Public License v3.0
702 stars 407 forks source link

Actual RPM #42

Open mbudris opened 6 years ago

mbudris commented 6 years ago

Would it be possible to get an actual motor RPMs using hall sensors and have this info put into debug UART? If this is already possible, could you put me on the way, because I'm not much of a coder.

btsimonh commented 6 years ago

@mbudris - look at my fork, specifically the https://github.com/btsimonh/hoverboard-firmware-hack/tree/withflashcalibration branch. It has ability to get speed in m/s and posn in m for each wheel from serial, although to get it on a traditional UART port will take a couple of mods. (I use a software serial on separate GPIO pins so I can still have the sensor boards in place).

mbudris commented 6 years ago

Nice! Which values to I put into setScopeChannel() to get the speed and distance? I guess you have a wheel diameter defined also, to calculate the speed?

btsimonh commented 6 years ago

HallData[0].HallPosn_m/HallData[1].HallPosn_m - floating point posn. HallData[0].HallSpeed_m_per_s/HallData[1].HallSpeed_m_per_s - floating point speed. defaults to 6.5". if your wheels are bigger, use

define WHEEL_SIZE_INCHES 8.5

e.g. for 8.5" .....

The data structure is in hallinterrupts.h, and there's a fair few comments in hallinterrupts.c

I've not checked the speed for accuracy - it could be out by a factor of 2, or other if I've screwed up the timer initialisation.

SpacePokemon commented 6 years ago

@btsimonh thanks again for your awesome post!! I am trying to debug using PlatformIO and I came across with this post.

Am I understanding correctly that I need to use RX/TX (ex. PB10, PB11 or other GPIOs) for serial debugging and I cannot with (SWDIO/SWCLK)?

Also I am quite lost with the pinouts and defined GPIO_PINs, I am looking through the datasheet and it is hard to find the addresses that corresponding to the pins (ex. PB1~, PA1~, PC1~).

I went into the stm32f1xx_hal_gpio.h and found GPIO pin's declaration but I do not know what GPIO_PIN_0 to15 correspond to which pins (ex. PB1~, PA1~, PC1~) on the microprocessor.

I would gratefully appreciate it If you could direct me to which section or page of the datasheet I need to look through to understand this.

And again, thank you so much for your work!!

btsimonh commented 6 years ago

you COULD re-purpose SWDIO/SWCLK to software serial, but then not sure how you would re-program, so did not dare; you would also be forever plugging and unplugging the STLINK. I did consider a modified STLINK with some 'shared ram' (I have an ESP8266 with basic SWD in it, but could not be aed to add prgramming functionality). Ref pinouts, grab the user manuals** for the STM chip (I use the manuals for the GD32 chip, but a lot of their register names are off by one, so it's confusing; e.g. our TIM1 it their TIM0 - probably just get around ST patents. ref addresses, in platform.IO, hover over 'GPIOA', right click 'goto definition' takes you places of interest. On my board (YST off ebay £15), I use pin28 & 40, which are very conveniently brought to the edge of the board and marked AO28 and AO40 (AO44 is also there!). p.s. PA0 = GPIOA bit 0, PB15 = GPIOB bit 15 (GPIO_PIN_15), etc. there are GPIOA/B/C/D/E, each with 16 'pins', availability according to processor version. you can read/write a whole 16 bit GPIO, or set or clear individual bits, through registers on the CPU. I find the HAL badly documented, but have tried to stick with it.

SpacePokemon commented 6 years ago

@btsimonh Thank you for your kind reply! I was able to figure out the ports(A to E) and pins(0 to16) as well as how they are declared by looking into the config.h file, thanks to your help.

I read through the softwareserial.c multiple times but I am not quite sure why HAL_GPIO_Init is called twice (assuming DOTX is declared) also I am having hard time where softwareserialtimer and softwareserialtimerTX is called. Are you using PB2 and PC9 both as TX pins within this code?

Additionally is it structured so that when softwareserail_send() is called within the main loop, it fills the buffer then it send automatically according to TIM2 or TIM3?

I could figure the above questions by just playing around with it but I am getting "ERROR1" code which I am having hard time fixing it.

So far I have figured out that commenting out the "CONTROL_SENSOR" and uncommenting "CONTROL_ADC" causes the problem.

EDIT: I figured it out that when you want to use ADC control, you also need to comment out INCLUDE_PROTOCOL.

EDIT 2: I was able to debug using Serial Port Monitor + FTDI Cable. I set the Databits to 7 and I am getting flawless and gorgeous serial debug data!!!!!!!!!!!!!!!!!!!!!

Again I really appreciate your work and your help! Have a wonderful week!

btsimonh commented 6 years ago

HAL_GPIO_Init is called once for each pin (RX and TX). Yes, data is sent in the TX timer if available (one interrupt per bit). should be n81.... I'm not going to guarantee the data will always be perfect, or that the input will always get the correct character; hence why the 'machine protocol' has checksum. But so far, I've not had any issues :).

btsimonh commented 6 years ago

p.s. just moved everything to a new branch https://github.com/btsimonh/hoverboard-firmware-hack/tree/pidcontrol - implements PID based control for Speed and Position (separately). So now you can ask it for 95 mm/s, and it will try to be at 95mm/s. Or you can ask it to move by 100mm, and it will move by 100mm, and then stay there!. Speeds below 40mm/s not supported.... (need sinusoidal control and a different scheme for that).

SpacePokemon commented 6 years ago

@btsimonh
That was the next thing I was gonna do!!!!!!! You are awesome! How did you find your PID coefficients??

btsimonh commented 6 years ago

they are pretty un-tuned at the moment. It struck me that the current PWM input is not Speed at all, but instead 'Power to the wheels'. For position, I figured the integration term was what 'pulled it in', so I've used 0.5., 0.5, 0.0, with 'power) (PWM) capped at 200. But in actuality, position probably needs to drive 'Speed' not 'power', because you want it to behave the same whatever the load.
For speed, I used 0.2, 0.1, 0.0. The output is used to increment the PWM, and CHANGES are capped at 20 per cycle (100ms). (i.e. it would take a second to get to pwm=200 from standing). Interestingly, my board must have some form of resetting current fuse; when I abuse it, the board goes dead for 15-20 minutes unless the 36v is physically disconnected.

SpacePokemon commented 6 years ago

@btsimonh Are you currently using the hoverboard's bldc motors??

btsimonh commented 6 years ago

yes. just done a load of mods to enable the PID constants to be adjusted and stored in flash; but no time to commit now; must work on urgent 'real work'.

btsimonh commented 6 years ago

ok, pushed into my pidcontrol branch. This allows you to use 'f' commands to set variables (e.g. fpkp100 to set the Position P values to 1.00), allowing the PID values to be tuned. when done, use 'Fm1234' to write to permanent flash, and fa to print all. No time to tune properly for me, but if you get some good values, let me know. quick tests don't seem to make much sense (maybe because it's not under load?)... hint: use '?' to list all commands...

walterinho commented 6 years ago

Hi @btsimonh, thanks for the work that you have done, it's really awesome.

Now I'm reading your code and it seems easy to understand but I would like to ask you some quick questions that could help me save some time. What I'm trying to do is to connect a raspberry pi w to the USART2 port and control the velocity or position of both wheels. At the moment I used your code and modified the config.h file to control it using USART2 (NiklasFauth's functionality) and it works fine. The problem is that as you know that doesn't work with the PID control that you built. Could you please explain me a little bit about what would be the correct steps to follow for the configuration that I want to get and how to use it.

Again, thanks for your time!

btsimonh commented 6 years ago

Hi walterinho, since I don't have access to the USARTs (occupied with sensor boards...), I've not specifically kept the functionality. One of the issue you will face is that the USART2 control uses DMA to read the USART, reading 4 byte chunks. So, if you get 3 bytes on the serial, you get nothing until the fourth arrives... this does not lend itself to a generic protocol or text based UI :(. And is rather scary as if one byte was lost, the whole serial control falls down.

So... what would you need to do to implement this....? 1/ easy bit... protocol.c contains two function pointers: int (send_serial_data)( unsigned char data, int len ) = softwareserial_Send; int (send_serial_data_wait)( unsigned char data, int len ) = softwareserial_Send_Wait; Provide functions with the same form, and set the function pointers to your functions. (look at comms.c for ideas?) 2/ more challenging... main.c calls into protocol with individual characters (line 356 in the current push): while (softwareserial_available() > 0){ //if (mb_update() < 0){ // only if we're modbus idle and not our address short inputc = softwareserial_getrx(); protocol_byte( (unsigned char) inputc ); //} } replace this with something which receives characters from USART2. I would suggest interrupt receive from the UART is not difficult to implement. I don't like the whole 'DMA with timeout' suggested in various ST groups.... but then I've never dealt with something so competent with DMA?

When you are done, please either offer a pull request on my repo, or just send me where your repo is, an I'll try to incorporate for others :). (not that I can test, but....)

have fun, S p.s. if you get some good PID values, post them here ! atm some things just don;t make sense to me. p.p.s. just pushed some minor updates to my pidcontrol branch.

walterinho commented 6 years ago

Hi again @btsimonh, thank you for your quick answer, and sorry for taking so long to answer you I've been working on others parts of my project.

I thought about what you said about the USART2 (I don't like DMA anymore haha), and as there's not a specific reason why I should keep using USART2, I just did the same as you by using pins 28 and 40.

I would like to ask you two questions:

I have access to an OptiTrack system and now I will make some tests for accuracy, and I will let you know if I get some good values for the PID.

Thank you very much for your time and sorry for the silly questions, I have experience in coding, but not with embedded systems.

btsimonh commented 6 years ago

Like this: :) rpizeroinstallation

I take 12v from the sensor board, through a DC-DC (a bit worried if the pot gets knocked!). I use a USB-serial for the serial, and have internal STLINK for programming.

I have found that the software serial loses some chars at 9600, maybe either increasing the interrupt rate for rx, or adjusting interrupt priorities so that the serial RX is highest, may help.

I THINK it should work with any pair of pins. But if you are going to use the USART2 pins, I would recommend at least trying to use interrupt drive USART Rx over software serial. DMA driver TX should be fine. Still not sure about how to USE the PID control. thinking of going for only position control, and use that to demand speed by incrementing position in a timer.... but need to cater with 'whoops, I hit something' (have interfaced a Wiimote to drive the simple ascii interface, and had some 'driving' issues - collisions).

p-h-a-i-l commented 6 years ago

slightly off topic: Is there a specific reason why you are using the USB to serial adapter instead of the raspis usart directly? Could save you an adapter and an usb hub

walterinho commented 6 years ago

Hi,

@p-h-a-i-l , I'm using the raspis usart pins to communicate with the board, and it works fine...

@btsimonh, your project looks really nice, I tried the PID control for position and it works fine (I still have to test it with the optitrack).

I don't know why but I'm losing some commands in the board, and sometimes what I receive from the board doesn't look good at all. Maybe you have any advice about this. (maybe increasing the baudrate or adjusting the priorities - I don't know how to do that haha)

btw, I sent you an email, I hope this doesn't bother you.

Thanks =)

SpacePokemon commented 6 years ago

@btsimonh It has been a while! I hope everything is going well on your end! I just found some time to get back to where I have left off.

So far, I have modified your PID controller

`

    // Compute integral
    pid->iterm += error;//(pid->Ki * error);
if (pid->iterm > pid->omax)
    pid->iterm = pid->omax;
else if (pid->iterm < pid->omin)
    pid->iterm = pid->omin;

// Compute differential on input
float dinput = (error - pid->lastin)/pid->sampletime;//in - pid->lastin;
// Compute PID output
float out = pid->Kp * error + pid->Ki * pid->iterm + pid->Kd * dinput;
// Apply limit to output value
if (out > pid->omax)
    out = pid->omax;
else if (out < pid->omin)
    out = pid->omin;

// Output to pointed variable
(*pid->output) = out;
// Keep track of some variables for next execution
pid->lastin = error;//in;
pid->lasttime = tick_get();;

`

And successfully controlled the motors using speed control function. As well as setting up SoftwareSerial using PB10/PB11 while controlling it using the Joystick.

Currently, I am trying to use SoftwareSerial input as an additional controller for motors. However, my SoftwareSerial is keeps getting broken up (code with PID controller) while your code without the PID controller's SoftwareSerial is working just fine. image

I have looked through the difference between the code with PID controller and without the PID controller and played around with some delay and other components of the SoftwareSerial but I am having no luck so far. I also thought it could be due to the noise so I put some ceramic capacitors on the signal line with ferrite core but still no luck. Additionally I have removed rx function from the code but that did not help either.

If you have any insight in this, please let me know!!

Once I finish tuning the PID controller, I will update it here!!

hoonkai commented 5 years ago

@btsimonh Hi Are you using the Hall sensors as odometers? Aren't the Hall sensors meant to be used for RPM commutating and not for measuring RPM though? Hall sensors shouldn't be accurate in measuring RPM.

p-h-a-i-l commented 5 years ago

@hoonkai please elaborate. Why are Hall sensor not accurate for measuring RPM? As you said, they are used for commutating, so they now exactly in which position the wheels are in. No reason to not use them for both.

hoonkai commented 5 years ago

@p-h-a-i-l Well, I was just thinking that Hall effect sensors are for low RPM commutating and not really for generating tach signals. I've had some less than desirable experiences in using Halls for RPM detection in the past (maybe my RPM was too high). I think the main issue is that you have a rotating magnetic field, but it is adjacent to a strong and opposing stationary field. Perhaps it'd be interesting to see how high RPM the speed/position measurement code can be pushed?

p-h-a-i-l commented 5 years ago

In what kind of systems did you see the problems with high rpm? The motor control is only commutating sensored here, no sensorless control at all.

tequilahut commented 5 years ago

does anyone have any experience with this board? is this as flat as the others? it's obviously a gd32 chip. i think the pinout to flatten the chip is the one that says:PROHIBIT. the third pad from the left is also GND which actually speaks for it. IMG_5330 IMG_9909

ghost commented 4 years ago

I wish to serially send data from jetson nano to stm32 ,is there any code available for jetson serial to send speed ,steer and crc values. 2)How should i send them,by integers or by bytes?

alex-makarov commented 4 years ago

Yes, just use the protocol. Works fine with Jetson for me.

ghost commented 4 years ago

hey @alex-makarov thanks for the reply. I'm trying to send the values using

struct.pack('ii',257,0) //first one is speed and the second one is steer. and it is received as bytes in opposite side as 2 1 0 0 0 0 0 0

if on struct.pack('ii',255,10) ==>255 0 0 0 10 0 0 0

actually it is not working. May I know the correct way to send the data?or else can you share me your code

alex-makarov commented 4 years ago

@ashwin-ar here's what I'm using on Nano -- https://github.com/alex-makarov/hoverboard-driver