The following Embedded C program demonstrates one way of
controlling the virtual arm.
The example:
- Initializes the two PWM channels
- Converts joint angles into PWM duty values
- Calculates inverse kinematics
- Moves the end effector to target positions
- Generates linear trajectories
- Draws a rectangular trajectory
This is an example application. Other firmware can use the same
hardware interface to implement different control strategies.
#include "halo.h"
#include <stdio.h>
#include <math.h>
#define PI 3.141592653589793
#define RAD_TO_DEG(rad) ((rad) * 180.0 / PI)
#define L1 10.0
#define L2 10.0
unsigned int angle_to_duty_us(unsigned int angle)
{
if (angle > 180)
angle = 180;
return 40000 + (angle * 40000) / 180;
}
void inverse_kinematics(float x, float y, float* t1, float* t2)
{
float d =
(x*x + y*y - L1*L1 - L2*L2) /
(2*L1*L2);
if (d > 1.0)
d = 1.0;
if (d < -1.0)
d = -1.0;
float theta2 = acos(d);
float theta1 =
atan2(y, x) -
atan2(
L2 * sin(theta2),
L1 + L2 * cos(theta2)
);
*t1 = RAD_TO_DEG(theta1);
*t2 = RAD_TO_DEG(theta2);
}
void move_to(float x, float y)
{
float t1, t2;
inverse_kinematics(x, y, &t1, &t2);
if (t1 < 0)
t1 = 0;
if (t1 > 180)
t1 = 180;
if (t2 < 0)
t2 = 0;
if (t2 > 180)
t2 = 180;
WRITE_REGISTER(
0x40004004,
angle_to_duty_us((unsigned int)t1)
);
WRITE_REGISTER(
0x40004044,
angle_to_duty_us((unsigned int)t2)
);
delay_us(200);
}
void initialize_pwm(void)
{
WRITE_REGISTER(0x40004000, 800000);
WRITE_REGISTER(0x40004040, 800000);
WRITE_REGISTER(0x40004008, 0x01);
WRITE_REGISTER(0x40004048, 0x01);
}
void move_line(
float xs,
float ys,
float xe,
float ye,
int steps
)
{
for (int i = 0; i <= steps; i++)
{
float x =
xs + i * (xe - xs) / steps;
float y =
ys + i * (ye - ys) / steps;
move_to(x, y);
}
}
void draw_rectangle(void)
{
int steps = 25;
float x0 = -10.0, y0 = 15.0;
float x1 = -5.0, y1 = 15.0;
float x2 = -5.0, y2 = 10.0;
float x3 = -10.0, y3 = 10.0;
move_line(x0, y0, x1, y1, steps);
move_line(x1, y1, x2, y2, steps);
move_line(x2, y2, x3, y3, steps);
move_line(x3, y3, x0, y0, steps);
}
void fw_main(void)
{
initialize_pwm();
while (1)
{
draw_rectangle();
}
}
Example behavior
The example continuously generates a rectangular trajectory
by moving the end effector through four target points.
The simulation visualizes the resulting joint and arm movement.
The application can be modified to implement other trajectories
or control strategies while using the same virtual PWM interface.