#include <mil.h>
#if M_MIL_USE_WINDOWS && !M_MIL_USE_CE
#define USE_D3D_DISPLAY 1
#else
#define USE_D3D_DISPLAY 0
#endif
#if USE_D3D_DISPLAY
#include "MdispD3D.h"
#endif
#if M_MIL_USE_LINUX
#define SAVE_PATH MIL_TEXT("")
#elif M_MIL_USE_CE
#define SAVE_PATH MIL_TEXT("\\Userdisk\\")
#else
#define SAVE_PATH MIL_TEXT("")
#endif
void PrintHeader()
{
MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n")
MIL_TEXT("CameraOnRobotArmCalibration\n\n")
MIL_TEXT("[SYNOPSIS]\n")
MIL_TEXT("This example shows how to calibrate a 3d robotics setup where a camera is\n")
MIL_TEXT("attached to a robotic arm. The calibration module is used to:\n")
MIL_TEXT(" - Calibrate the camera.\n")
MIL_TEXT(" - Find the pose of the camera coordinate system with respect to the\n")
MIL_TEXT(" robot tool coordinate system.\n")
MIL_TEXT(" - Find the pose of the robot base coordinate system with respect to the\n")
MIL_TEXT(" absolute coordinate system.\n\n")
MIL_TEXT("[MODULES USED]\n")
MIL_TEXT("Modules used: application, system, display, buffer, calibration.\n\n")
MIL_TEXT("Press <Enter> to start.\n\n"));
MosGetch();
}
struct SCalibrationData
{
const MIL_TEXT_CHAR* m_ImageFile;
MIL_DOUBLE m_GridCornerHintX;
MIL_DOUBLE m_GridCornerHintY;
MIL_DOUBLE m_ToolPositionX;
MIL_DOUBLE m_ToolPositionY;
MIL_DOUBLE m_ToolPositionZ;
MIL_DOUBLE m_ToolRotationX;
MIL_DOUBLE m_ToolRotationY;
MIL_DOUBLE m_ToolRotationZ;
};
#define EXAMPLE_IMAGE_PATH M_IMAGE_PATH MIL_TEXT("CameraOnRobotArmCalibration/")
static const MIL_TEXT_CHAR* const OUTPUT_CALIBRATION_FILE = SAVE_PATH MIL_TEXT("MilRobotCalibration.mca");
static const MIL_INT NB_CALIBRATION_IMAGES = 7;
static const SCalibrationData CALIBRATION_DATA[NB_CALIBRATION_IMAGES] =
{
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid0.mim"), M_NONE, M_NONE, -29.999479, 700.000122, 510.000092, 174.405594, 28.591669, 91.206627},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid1.mim"), M_NONE, M_NONE, -51.989830, 599.020020, 505.920288, 173.120300, 20.788210, 95.883133},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid2.mim"), M_NONE, M_NONE, 118.010101, 629.020020, 515.919983, -169.119003, 24.478680, 79.661667},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid3.mim"), M_NONE, M_NONE, 118.009903, 719.020020, 505.920105, -167.463898, 31.302469, 85.128510},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid4.mim"), M_NONE, M_NONE, -11.990170, 519.020081, 415.920013, 179.393494, 16.471180, 91.697990},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid5.mim"), M_NONE, M_NONE, -29.999969, 399.999786, 509.999786, 175.392303, 0.067751, 97.142853},
{EXAMPLE_IMAGE_PATH MIL_TEXT("CalGrid6.mim"), M_NONE, M_NONE, -130.000000, 399.999786, 510.000000, 164.944305, 7.392438, 115.798599}
};
static const SCalibrationData TEST_DATA =
{EXAMPLE_IMAGE_PATH MIL_TEXT("TestGrid.mim"), M_NONE, M_NONE, 18.009741, 629.019775, 505.919891, 178.899307, 22.548679, 88.419952};
static const MIL_INT ROW_NUMBER = 20;
static const MIL_INT COLUMN_NUMBER = 20;
static const MIL_DOUBLE ROW_SPACING = 10.05;
static const MIL_DOUBLE COLUMN_SPACING = 10.00;
static const MIL_INT GRID_TYPE = M_CHESSBOARD_GRID;
static const MIL_DOUBLE PIXEL_COLOR = M_COLOR_GREEN;
static const MIL_DOUBLE WORLD_COLOR = M_COLOR_RED;
static const MIL_INT D3D_DISPLAY_SIZE_X = 600;
static const MIL_INT D3D_DISPLAY_SIZE_Y = 600;
static const MIL_TEXT_CHAR* const SEPARATOR = MIL_TEXT("--------------------\n\n");
void MoveRobotPose(MIL_ID MilCalibration, const SCalibrationData& rData);
void AddCalibrationGrid(MIL_ID MilDisplay,
MIL_ID MilCalibration,
MIL_ID MilDisplayImage,
MIL_ID MilOverlayImage,
MIL_INT ImageIndex);
void ShowCalibrationResults(MIL_ID MilDisplay, MIL_ID MilCalibration, MIL_ID MilDisplayImage, MIL_ID MilOverlayImage);
void TestCalibration(MIL_ID MilDisplay,
MIL_ID MilCalibration,
MIL_ID MilDisplayImage,
MIL_ID MilOverlayImage);
int MosMain(void)
{
MIL_ID MilApplication = MappAlloc(M_NULL, M_DEFAULT, M_NULL);
MIL_ID MilSystem = MsysAlloc(M_DEFAULT, M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, M_NULL);
MIL_ID MilDisplay = MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, M_NULL);
PrintHeader();
MIL_ID MilCalibration = McalAlloc(MilSystem, M_3D_ROBOTICS, M_DEFAULT, M_NULL);
MIL_INT SizeX = MbufDiskInquire(TEST_DATA.m_ImageFile, M_SIZE_X, M_NULL);
MIL_INT SizeY = MbufDiskInquire(TEST_DATA.m_ImageFile, M_SIZE_Y, M_NULL);
MIL_ID MilDisplayImage = MbufAlloc2d(MilSystem, SizeX, SizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, M_NULL);
MbufClear(MilDisplayImage, 0.0);
MdispSelect(MilDisplay, MilDisplayImage);
MIL_ID MilOverlayImage;
MdispControl(MilDisplay, M_OVERLAY, M_ENABLE);
MdispInquire(MilDisplay, M_OVERLAY_ID, &MilOverlayImage);
for (MIL_INT ImageIndex = 0; ImageIndex < NB_CALIBRATION_IMAGES; ++ImageIndex)
{
MosPrintf(MIL_TEXT("The robot arm is at pose #%d.\n\n"), ImageIndex);
MoveRobotPose(MilCalibration, CALIBRATION_DATA[ImageIndex]);
AddCalibrationGrid(MilDisplay, MilCalibration, MilDisplayImage, MilOverlayImage, ImageIndex);
MosPrintf(SEPARATOR);
}
MosPrintf(MIL_TEXT("The 3d robotics calibration is performed using all the accumulated data.\n"));
McalGrid(MilCalibration, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_DEFAULT, M_DEFAULT);
MIL_INT CalibrationStatus = McalInquire(MilCalibration, M_CALIBRATION_STATUS, M_NULL);
if (CalibrationStatus == M_CALIBRATED)
{
MosPrintf(MIL_TEXT("The calibration is successful.\n\n"));
ShowCalibrationResults(MilDisplay, MilCalibration, MilDisplayImage, MilOverlayImage);
MosPrintf(SEPARATOR);
TestCalibration(MilDisplay, MilCalibration, MilDisplayImage, MilOverlayImage);
}
else
{
MosPrintf(MIL_TEXT("The calibration failed.\n\n")
MIL_TEXT("Press <Enter> to exit.\n\n"));
MosGetch();
}
MbufFree(MilDisplayImage);
McalFree(MilCalibration);
MdispFree(MilDisplay);
MsysFree(MilSystem);
MappFree(MilApplication);
return 0;
}
void MoveRobotPose(MIL_ID MilCalibration, const SCalibrationData& rData)
{
McalSetCoordinateSystem(MilCalibration,
M_TOOL_COORDINATE_SYSTEM,
M_ROBOT_BASE_COORDINATE_SYSTEM,
M_TRANSLATION+M_ASSIGN,
M_NULL,
rData.m_ToolPositionX,
rData.m_ToolPositionY,
rData.m_ToolPositionZ,
M_DEFAULT);
McalSetCoordinateSystem(MilCalibration,
M_TOOL_COORDINATE_SYSTEM,
M_TOOL_COORDINATE_SYSTEM,
M_ROTATION_ZYX+M_COMPOSE_WITH_CURRENT,
M_NULL,
rData.m_ToolRotationZ,
rData.m_ToolRotationY,
rData.m_ToolRotationX,
M_DEFAULT);
}
void AddCalibrationGrid(MIL_ID MilDisplay,
MIL_ID MilCalibration,
MIL_ID MilDisplayImage,
MIL_ID MilOverlayImage,
MIL_INT ImageIndex)
{
MosPrintf(MIL_TEXT("An image of the calibration grid is taken at that position, and used for\n")
MIL_TEXT("calibration.\n\n")
MIL_TEXT("Calling McalGrid(): "));
const SCalibrationData& rData = CALIBRATION_DATA[ImageIndex];
MbufLoad(rData.m_ImageFile, MilDisplayImage);
McalControl(MilCalibration, M_GRID_CORNER_HINT_X, rData.m_GridCornerHintX);
McalControl(MilCalibration, M_GRID_CORNER_HINT_Y, rData.m_GridCornerHintY);
McalGrid(MilCalibration, MilDisplayImage,
0.0, 0.0, 0.0,
ROW_NUMBER, COLUMN_NUMBER,
ROW_SPACING, COLUMN_SPACING,
M_ACCUMULATE, GRID_TYPE);
MIL_INT CalibrationStatus = McalInquire(MilCalibration, M_CALIBRATION_STATUS, M_NULL);
if (CalibrationStatus == M_CALIBRATING)
{
MgraColor(M_DEFAULT, PIXEL_COLOR);
McalDraw(M_DEFAULT, MilCalibration, MilOverlayImage, M_DRAW_IMAGE_POINTS, ImageIndex, M_DEFAULT);
MosPrintf(MIL_TEXT("extracted features are displayed in green.\n")
MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
MdispControl(MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
}
else
{
MosPrintf(MIL_TEXT("the grid was not found.\n")
MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
}
}
void ShowCalibrationResults(MIL_ID MilDisplay, MIL_ID MilCalibration, MIL_ID MilDisplayImage, MIL_ID MilOverlayImage)
{
MIL_DOUBLE AveragePixelError, MaximumPixelError, AverageWorldError, MaximumWorldError;
McalInquire( MilCalibration, M_GLOBAL_AVERAGE_PIXEL_ERROR, &AveragePixelError );
McalInquire( MilCalibration, M_GLOBAL_MAXIMUM_PIXEL_ERROR, &MaximumPixelError );
McalInquire( MilCalibration, M_GLOBAL_AVERAGE_WORLD_ERROR, &AverageWorldError );
McalInquire( MilCalibration, M_GLOBAL_MAXIMUM_WORLD_ERROR, &MaximumWorldError );
MosPrintf(MIL_TEXT("Global pixel error\n average: %.3g pixels\n maximum: %.3g pixels\n"),
AveragePixelError, MaximumPixelError);
MosPrintf(MIL_TEXT("Global world error\n average: %.3g mm\n maximum: %.3g mm\n\n"),
AverageWorldError, MaximumWorldError);
McalSave(OUTPUT_CALIBRATION_FILE, MilCalibration, M_DEFAULT);
MosPrintf(MIL_TEXT("The calibration context was saved as '"));
MosPrintf(OUTPUT_CALIBRATION_FILE);
MosPrintf(MIL_TEXT("'.\nPress <Enter> to verify the calibration accuracy for each pose.\n\n"));
MosGetch();
MosPrintf(SEPARATOR);
#if USE_D3D_DISPLAY
MIL_DISP_D3D_HANDLE DispHandle;
DispHandle = McalD3DAlloc(NULL, 0, D3D_DISPLAY_SIZE_X, D3D_DISPLAY_SIZE_Y, 0);
#endif
for (MIL_INT ImageIndex = 0; ImageIndex < NB_CALIBRATION_IMAGES; ++ImageIndex)
{
MosPrintf(MIL_TEXT("Pose #%d\n"), ImageIndex);
MosPrintf(MIL_TEXT("-------\n\n"));
MbufLoad(CALIBRATION_DATA[ImageIndex].m_ImageFile, MilDisplayImage);
MoveRobotPose(MilCalibration, CALIBRATION_DATA[ImageIndex]);
McalAssociate(MilCalibration, MilDisplayImage, M_DEFAULT);
McalInquireSingle( MilCalibration, ImageIndex, M_AVERAGE_PIXEL_ERROR, &AveragePixelError );
McalInquireSingle( MilCalibration, ImageIndex, M_MAXIMUM_PIXEL_ERROR, &MaximumPixelError );
McalInquireSingle( MilCalibration, ImageIndex, M_AVERAGE_WORLD_ERROR, &AverageWorldError );
McalInquireSingle( MilCalibration, ImageIndex, M_MAXIMUM_WORLD_ERROR, &MaximumWorldError );
MosPrintf(MIL_TEXT("Pixel error\n average: %.3g pixels\n maximum: %.3g pixels\n"),
AveragePixelError, MaximumPixelError);
MosPrintf(MIL_TEXT("World error\n average: %.3g mm\n maximum: %.3g mm\n\n"),
AverageWorldError, MaximumWorldError);
MgraColor(M_DEFAULT, PIXEL_COLOR);
McalDraw(M_DEFAULT, MilCalibration, MilOverlayImage, M_DRAW_IMAGE_POINTS, ImageIndex, M_DEFAULT);
MgraColor(M_DEFAULT, WORLD_COLOR);
McalDraw(M_DEFAULT, MilCalibration, MilOverlayImage, M_DRAW_WORLD_POINTS, ImageIndex, M_DEFAULT);
MosPrintf(MIL_TEXT("Green: extracted features (pixels).\n")
MIL_TEXT("Red: world points converted to pixels using the calibration context.\n\n"));
#if USE_D3D_DISPLAY
if (DispHandle != NULL)
{
McalD3DSetImages(DispHandle, &MilDisplayImage, 1);
MosPrintf(MIL_TEXT("The DirectX display shows the coordinate systems for calibration pose #%d.\n\n"), ImageIndex);
MdispD3DPrintHelp(DispHandle);
if (ImageIndex == 0)
MdispD3DShow(DispHandle);
}
#endif
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
MdispControl(MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
}
#if USE_D3D_DISPLAY
if (DispHandle != NULL)
{
MdispD3DHide(DispHandle);
MdispD3DFree(DispHandle);
}
#endif
}
void TestCalibration(MIL_ID MilDisplay,
MIL_ID MilCalibration,
MIL_ID MilDisplayImage,
MIL_ID MilOverlayImage)
{
MoveRobotPose(MilCalibration, TEST_DATA);
MbufLoad(TEST_DATA.m_ImageFile, MilDisplayImage);
MgraColor(M_DEFAULT, WORLD_COLOR);
McalDraw(M_DEFAULT, MilCalibration, MilOverlayImage, M_DRAW_WORLD_POINTS, M_DEFAULT, M_DEFAULT);
MosPrintf(MIL_TEXT("The robot arm is moved to a new position. McalSetCoordinateSystem() is used\n")
MIL_TEXT("to provide the new tool pose to the calibration module, thus the system\n")
MIL_TEXT("remains fully calibrated.\n\n")
MIL_TEXT("The calibration grid is grabbed at the new camera position. This grabbed\n")
MIL_TEXT("image was not used during calibration. The corners of the grid are not\n")
MIL_TEXT("extracted from this image; no image processing is performed.\n\n")
MIL_TEXT("Instead, the world points of the calibration grid are converted to pixels,\n")
MIL_TEXT("using the calibration module with the new tool pose. Since these points\n")
MIL_TEXT("(displayed in red) coincide with the corners of the grabbed grid, the\n")
MIL_TEXT("calibration is accurate.\n\n")
MIL_TEXT("Press <Enter> to exit.\n\n"));
MosGetch();
MdispControl(MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
}