Wednesday, May 25, 2016

Bremsproject Season 2 - Part 1: New Electronics

Like many of my colleagues this past semester, I decided it would be fun to build a heavyweight battlebot ("The Dentist") for season 2 of the Battlebots TV show. "The Dentist" involved spinning up a 200KJ energy storage drum, and being as mechanically incompetent as I am, I decided that clearly my contribution to the sport would be some kind of absurd power system involving hundreds of volts and large motors.

I went and bought a few Prius inverters and dusted off the old Bremsthesis bits, and began this year's adventure into turning on someone else's hardware.

New boards, better boards

Wow, these motors have resolvers on them! So much more convenient than our contraption built out of 3D-printed mounts and analog hall sensors. Even more conveniently, there is a rather expensive IC that takes resolver input and outputs quadrature encoder pulses. Rather fortunately, this IC can be sampled from Analog Devices, or purchased from unofficial eBay vendors for about $15.

Implementing the IC using the datasheet notes is fairly straightforward; the only tricky part is getting the output amplifiers right (the sine/cosine generation on the chip itself is the output of a DAC, and is unsuitable for driving a resolver directly):

Layout is a little less straightforward. The following daughtercard works, but has about 1 LSB worth of noise:

Board  || Schematic

I could probably do better, but who needs 12 bits of position accuracy anyway?

The actual interface board for the Prius module remains largely unchanged in design from last year, the only changes being it uses the Morpho headers and is all surface mount, because hell if I'm using sockets in a combat robot.

Board || Schematic

New this year! Prius inverter pinouts

Pin #
Gate drive power in
Reverse-protected, ~9-16V
Small inverter, current sense V
Redundant with GIVA
Small inverter, current sense V
50mV/A, zero-centered, isolated
Small inverter, current sense W
Redundant with GIWA
Small inverter, current sense W
50mV/A, zero-centered, isolated
Small inverter, phase input W
Inverting, 12V logic, isolated; no float
Small inverter, phase input V
See above
Small inverter, phase input U
See above
Small inverter temperature
Unknown behavior, probably ratiometric
Small inverter fault indicator
Probably open-collector
Small inverter ENABLE
12V logic, setting low floats all phases
Large inverter temperature
Unknown behavior, probably ratiometric
Large inverter fault indicator
Probably open-collector
Large inverter ENABLE
12V logic, setting low floats all phases
Gate drive power GND
Large inverter, current sense W
25mV/A, zero-centered, isolated
Large inverter, current sense W
Redundant with MIWA
Large inverter, current sense V
25mV/A, zero-centered, isolated
Large inverter, current sense V
Redundant with MIVA
No connection

No connection

No connection

Large inverter, phase input U
Inverting, 12V logic, isolated; no float
Large inverter, phase input V
See above
Large inverter, phase input W
See above
Bus voltage
Ratio not yet determined
Inverter overvoltage
Probably open-collector
Chassis ground
Leave unconnected

Power stage notes: Sides are probably CM400 and CM200-class IGBT's. Stock switching freqency is 5KHz; performance becomes rather poor past about 15KHz as internal delays and deadtime introduce severe distortion in synthesized waveforms. Current sensors saturate at 400 and 200A; the power module has fast overcurrent detection set somewhere around there (the entire inverter will automatically float if the phase currents are too high). Diodes are quite small in comparison to the IGBT's, and are probably not rated to full inverter current.

Sleeker firmware

Thanks to mostly me, the original Brems-code had bloated to rather ungainly proportions, featuring contexts, event loops, buffers, debuggers, and more classes than should ever be in a two-person microcontroller project. It was time to downsize; conveniently enough, Ben was working on an encoder-based FOC controller for his robot leg project, so I decided to borrow bits of his code and mix it with my own. The new code is here.

Tidbits of particular note:

a = new FastPWM(PWMA);
b = new FastPWM(PWMB);
c = new FastPWM(PWMC);

NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); //Enable TIM1 IRQ

TIM1->DIER |= TIM_DIER_UIE; //enable update interrupt
TIM1->CR1 = 0x40; //CMS = 10, interrupt only when counting up
TIM1->CR1 |= TIM_CR1_ARPE; //autoreload on,
TIM1->RCR |= 0x01; //update event once per up/down count of tim1

TIM1->PSC = 0x00; //no prescaler, timer counts up in sync with the peripheral clock
TIM1->ARR = 0x4650; //5 Khz
TIM1->CCER |= ~(TIM_CCER_CC1NP); //Interupt when low side is on.

This snippet of code sets up Timer 1 to run at 5Khz in center-aligned mode; that is, the center points of the switching waveforms when all three channels are off are aligned. This allows us to sample current when none of the phases are switching, hopefully reducing sensor noise. TIM1->ARR controls the switching frequency; halving its value doubles the switching frequency.

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // clock for ADC1
RCC->APB2ENR |= RCC_APB2ENR_ADC2EN; // clock for ADC2

ADC->CCR = 0x00000006; //Regular simultaneous mode, 3 channels

ADC1->CR2 |= ADC_CR2_ADON; //ADC1 on
ADC1->SQR3 = 0x0000004; //PA_4 as ADC1, sequence 0

ADC2->SQR3 = 0x00000008; //PB_0 as ADC2, sequence 1

GPIOA->MODER |= (1 << 8);
GPIOA->MODER |= (1 << 9);
GPIOA->MODER |= (1 << 2);
GPIOA->MODER |= (1 << 3);
GPIOA->MODER |= (1 << 0);
GPIOA->MODER |= (1 << 1);
GPIOB->MODER |= (1 << 0);
GPIOB->MODER |= (1 << 1);
GPIOC->MODER |= (1 << 2);
GPIOC->MODER |= (1 << 3);

This bit configures the ADC's. We are cheating a little here; the ADC's are set up in sequence mode, but the sequences are length 1. Because only two values are necessary for basic operation we don't have to worry about dealing with the sequencing (somewhat useful, as mbed somehow goes and clobbers ADC_EOC flag functionality).

void zero_current(){
    for (int i = 0; i < 1000; i++) {
        ia_supp_offset += (float) (ADC1->DR);
        ib_supp_offset += (float) (ADC2->DR);
        ADC1->CR2 |= 0x40000000;
    ia_supp_offset /= 1000.0f;
    ib_supp_offset /= 1000.0f;
    ia_supp_offset = ia_supp_offset / 4096.0f * AVDD - I_OFFSET;
    ib_supp_offset = ib_supp_offset / 4096.0f * AVDD - I_OFFSET;

This function tries to compute an additional offset (caused by sensor drift, etc.) every time the controller resets; this is in addition to any measured calibration errors caused by resistor inaccuracies or AVDD error.

p = pos.GetElecPosition() - POS_OFFSET;
if (p < 0) p += 2 * PI;

float sin_p = sinf(p);
float cos_p = cosf(p);

ia = ((float) adval1 / 4096.0f * AVDD - I_OFFSET - ia_supp_offset) / I_SCALE;
ib = ((float) adval2 / 4096.0f * AVDD - I_OFFSET - ib_supp_offset) / I_SCALE;
ic = -ia - ib;

float u = CURRENT_U;
float v = CURRENT_V;

alpha = u;
beta = 1 / sqrtf(3.0f) * u + 2 / sqrtf(3.0f) * v;

d = alpha * cos_p - beta * sin_p;
q = -alpha * sin_p - beta * cos_p;

float d_err = d_ref - d;
float q_err = q_ref - q;

d_integral += d_err * KI;
q_integral += q_err * KI;

if (q_integral > INTEGRAL_MAX) q_integral = INTEGRAL_MAX;
if (d_integral > INTEGRAL_MAX) d_integral = INTEGRAL_MAX;
if (q_integral < -INTEGRAL_MAX) q_integral = -INTEGRAL_MAX;
if (d_integral < -INTEGRAL_MAX) d_integral = -INTEGRAL_MAX;
vd = KP * d_err + d_integral;
vq = KP * q_err + q_integral;
if (vd < -1.0f) vd = -1.0f; if (vd > 1.0f) vd = 1.0f;
if (vq < -1.0f) vq = -1.0f; if (vq > 1.0f) vq = 1.0f;

This is the juicy stuff that actually closes the d and q current loops. POS_OFFSET is a sensor offset measured by aligning the motor to the d-axis (phase A high, phases B and C low).

The rest of the code should be fairly self-explanatory; the mbed project should run out of the box on a Nucleo-F446RE.

Sunday, May 1, 2016

View Camera Part 1

Almost as important as the picture: the picture of the thing that took the picture
I take a lot of pictures of small things. Boards, product shots, machines and the like often warrant close-ups, and taking photos at close quarters necessarily implies a shallow depth of field. I'm also a resolution freak, and like having pixel-level sharpness (after all, what good are all those extra pixels if they have no information?). This necessarily implies having a large sensor, or an exotic ultra-fast lens and lots of tiny pixels.

The problem with having a large sensor is that at close range, depth of field falls off as focal length squared, so getting everything in focus on a KAF-22000 is a lot harder than getting everything in focus on a cell phone sensor. Now, I like buttery bokeh as much as the next person, but for applications like project documentation or web store photos having a 2mm slice of your subject in focus is not really acceptable. Stopping down doesn't fix the issue either; on most modern sensors you get to stop down somewhere between f/5.6 and f/11 before diffraction screws you.

The solution (to some extent at least) is to move the plane of focus with camera movements. For a surprising number of project documentation photos, this works great; boards shots in particular look natural, with the boards entirely in focus and the Z direction of focus approximately perpendicular to the board.

This post is motivated by a couple things. There is surprisingly little documentation on tabletop type photography with medium (1:5-1:20) reproduction ratios using movements and medium format sized image areas; most things out there are focused on shift-based wide angle photography for architectural or fine art landscape work. Optimizing a camera for wide-angle work is remarkably different from building one for close-up work; the movements involved are much, much smaller (a couple degrees instead of tens of degrees) and most cameras out there are pancake-type cameras with rangefinders and focus on portability. Precision of focus is also much less important since depth of field at f/5.6 on a 35mm lens focused at tens of meters is hundreds of times larger than an f/5.6 90mm lens focused at tens of centimeters. Secondly, not a lot of material out there is focused on building a "cheap" camera (relatively speaking, of course; my system still cost $2000+ and I got real lucky on the digital back). Lastly, a lot of information out on forums is posted by idiots or brand loyalists and just plain wrong.

Camera Body

You need half-degree positioning accuracy on all movements to resolve 9-micron pixels, or quarter-degree for 4.5-micron (A7R, D800) pixels. This means gears; cameras such as the Linhof Technica series, the Fuji GX680's or any of the numerous cheap monorails will not work. And no, your Speed Graphic will not work.

Cameras with all geared movements that aren't ass expensive include:

  • Sinar P/P2/X: Probably the best choice; the Sinar system is ubiquitous and the cameras are beautifully built. The X is probably a better choice than the original P simply because it is newer; the P2 is marginally better but is typically quite expensive.
  • Sinar P3: This is a P2 with smaller standards. Costs about twice as much as a P2 on the secondary market, or about four times as much as the X. If you have machine shop access there is little reason to get a P3, since you won't be able to afford any of the P3-specific accessories (CMV shutters, etc) anyway.
  • Cambo Legend Master: I used to have one of these; the gears aren't great and the camera is gigantic with no option to shrink because of the dovetail + L-bracket design. That being said they are dirt cheap, and form a baseline of sorts for what is acceptable.
  • Cambo Ultima: I've never used one, but appears to be the same price and build quality as a P3.
  • Rollei X-act2: This guy is one of two cameras (the other being the nonexistent Silvestri S5 Micron) designed from the ground up to be a digital system. It's tiny, but the accessories are expensive (albeit all easily made on a mill) and the limited range of movements are somewhat restrictive for tabletop work.
  • Linhof M679cs: If you're reading this post you can't afford it.


Lenses for digital view cameras are sort of a mystery, shrouded in BS lore and almost-as-BS marketing materials. In general, they can be divided into three classes:

  • Symmetric 6-element lenses with a small image circle. This includes the Schneider Digitars, apochromatic enlarging lenses, Fujinon EBC GX lenses, and a handful of obscure 2x3 film lenses. They are usually cheap ($200-500) and resolve 9-micron pixels, but not much more. Among these the notable ones are:
    • APO Componon HM 4.5/90: The highest resolution of Schneider's symmetric lens line. It out-resolves its symmetric brethren slightly, and has such features as multicoating and a fancy blue ring on the lens.
    • Makro Symmar HM 5.6/120: Expensive, but the best macro choice. Even resolves 5-micron pixels as close range. Sometimes available in a barrel-type housing (aperture only, no shutter) on machine vision cameras for slightly cheaper.
    • Sinaron Digital 4/80: This is a 80mm Digitar in a Sinar DB mount. Awkward to use since the DB lensboards lack an externally accessible aperture, but dirt cheap (sub-$150) and ubiquitous since it was the cheapest lens in the old Sinarcam system.
    • Componons, in general, are the same optical design as the matching Digitars, but somewhat cheaper. The APO Componons are in fact the exact same lens; while the apochromatic design contributes very little to their resolving power, the modern coatings will improve contrast somewhat. That being said, they are much more expensive than their non-APO counterparts.
    • GX680 system lenses are rather good; however, their electronic leaf shutters will require nontrivial hacking (which as of this post's writing no one has done) to turn on.
  • Symmetric 8-element lenses based on a wide-angle design.:The lore behind these is unclear, but I've heard it said that the first generation Rodenstock Digarons (Apo-Sironar Digital) were rebranded Grandagons. These lenses have flatter fields of view than the 6-element lenses, but are rather rare and expensive. This line most notably contains:
    • Apo-Sironar Digital 90/5.6: As the longest focal length of the 8-element lenses, this lens can almost resolve 5-micron pixels. Marketed as the HR Digaron-W 90/5.6, it claims to be rated for 80 MP sensors, but MTF at 100lp/mm at the corners is less than stellar.
  • Retrofocus 12+ element lenses: Namely, the HR Digaron-S line of lenses. Not actually as expensive as you might think (the 100/4 is $1000 on a good day, including Copal 0 shutter). Also known as the Sinaron Digital HR. All good for 5-micron or smaller pixels. If you're using an A7R as a digital back and want any semblance of resolution, or if you have an IQ180 or a microstepping back, these lenses are more or less mandatory. It is worth noting that you will have limited movements with these lenses on 36x48mm sensors; they were designed for maximum performance on 33x44mm sensors (hence the off focal lengths such as the 60mm).


Live view please! You'll have a miserable time focusing without it. Sadly coaxing live view out of a CCD with no electronic shutter whatsoever is exceedingly difficult. Out of the older CCD backs, Sinar has the best live view implementation, while Phase One had no live video until the P+ series. If you can afford them, the CFV-50C or the IQ3-100 are ideal, but if you are reading this post you probably can't. An alternative option is to use an A7R/II, which has a formidable, if somewhat small, sensor - this makes focusing somewhat awkward; if you want a normal focal length some cameras may not be able to focus to infinity without aggressively recessed lensboards and bag bellows.

(...more to come in the next post)