Задани на лабораторные работы. ПРК / Professional Microsoft Robotics Developer Studio
.pdf
www.it-ebooks.info
Chapter 17: Writing New Hardware Services
makes a competing line of chips under the AVR brand name that is also popular. The ATmega8, for example, is used in several small robots.
Hardware development boards and in-circuit emulators are available for these chips, but generally they are expensive. Luckily, these are not required for most small development projects, which don’t involve developing new hardware.
Assemblers and C compilers for these chips are readily available. Although some of them are free, many are commercial products. Several C compilers are based on the GNU GCC cross-compiler tool chain.
A variety of other languages are also supported by different vendors, including Java, Forth, Pascal, and others. However, C is by far the most popular language.
Note that C# is not available for these chips because Visual Studio generates code for the .NET environment. It does not generate native code for microcontrollers (a fact that some people clearly do not understand judging by the postings on the MRDS Discussion Forum).
There are also GUI-based programming packages similar in concept to VPL, i.e., you create your program by dragging various “blocks” to the workspace and drawing a flow chart. The capabilities of these software packages vary enormously, and they are not the focus of this chapter.
Chips with Built-in BASIC Interpreters
The advantages of BASIC are that it is easy for beginners to learn and the development software is typically free. The compiled code can easily be downloaded via a serial port. (If your PC does not have a serial port, you can use a USB-to-serial device.) The disadvantage is that it might be slower than compiled code written in C and the onboard “operating system” does not provide multi-tasking capabilities, so everything has to be done in a large loop, which means that timing can be critical.
Examples of this kind of chip include the following:
The PICAXE chip (www.picaxe.co.uk) sold by Revolution Education is a good example of an application of a PIC. A PICAXE chip is a PIC chip with a boot loader and BASIC interpreter supplied in firmware on the chip. The PICAXE is used in the Picblok Integrator robot discussed in this chapter.
Parallax (www.parallax.com), the manufacturers of the Boe-Bot and Scribbler robots, uses a BASIC Stamp as the onboard controller. This is a small circuit board that holds a 5V regulator, EEPROM, and a PIC that contains a PBASIC interpreter in firmware. Many of the models of the BASIC Stamp use the SX chip that is made by Parallax, while the early BS1 and BS2 models used a PIC chip from Microchip.
An interesting approach has been taken by Savage Innovations with their OOPic hardware (www.oopic.com). They adopted an object-oriented approach in a multi-tasking environment from the outset, and they support multiple language syntaxes (BASIC, C, or Java).
733
www.it-ebooks.info
Part IV: Robotics Hardware
MRDS Support from Other Vendors
One problem with some companies that make robots is that they have been around for many years and have an existing investment in software. Therefore, some of them are not particularly interested in supporting MRDS, or they don’t have people on their staff with MRDS skills so their support is limited. However, there are companies out there that directly support MRDS, including the following:
Devantech Ltd (www.robot-electronics.com) offers MRDS services written by Chris Kilner. They are well written and are available as open source at www.codeplex.com/DevantechMSRS. Chris has since moved on to bigger and better things with Nao humanoid robots at Aldebaran (www.aldebaran-robotics.com/eng/index.php).
The Vex Robotics Design System (www.vexlabs.com) from Innovation First, Inc., also has an open-source project at www.codeplex.com/VexMsrs.
Stinger robots use a board called the Serializer that has an onboard processor. However, you control the Serializer via a serial port (hence its name) and do not program the board directly. Services for MRDS are available from the RoboticsConnection website (www.roboticsconnection.com).
This book provides updated services for Parallax, Lynxmotion, and Surveyor robots to correct bugs or deficiencies, as well as a new Drive service for the Stinger that conforms to the generic Differential Drive contract. There are also services for the Hemisson robot in this chapter because the manufacturer, K-Team, does not provide them.
Creating a Generic Brick Service
One thing missing from MRDS is a generic “brick” contract, a much-debated topic on the Discussion Forum. The basic problem is that robots are so variable in their hardware components — e.g., sensors, actuators, even the onboard CPU — that it is difficult to generalize.
Consequently, there is no such thing as a “universal remote controller” for a robot. (Even infrared universal remote controls (URCs) for audio-visual equipment have a hard time due to deliberate incompatibilities introduced by the vested interests of competing manufacturers, but that is another story.)
Another issue is the proliferation of services. There needs to be a happy medium between a single monolithic service and “service bloat” caused by too many small services. This is more of a problem when you move services to mobile devices with limited memory and CPU power.
For this chapter, the authors decided to build a Generic Brick service first. This service should be suitable for most small hobby and educational robots. Many elements are common across these types of robots.
The procedure for creating the Generic Brick service contract is quite easy:
1. Create a new service called GenericBrick and open it in Visual Studio. You can use DssNewService or just make a new DSS service project in Visual Studio. This is covered in Chapters 2 and 3.
734
www.it-ebooks.info
Chapter 17: Writing New Hardware Services
2. Edit AssemblyInfo.cs and change the Service Declaration to DataContract instead of
ServiceBehavior, as shown here:
//For a generic contract, the service is a DataContract NOT ServiceBehavior [assembly: ServiceDeclaration(DssServiceDeclaration.DataContract)]
3.
4.
5.
6.
7.
Delete the service implementation file, GenericBrick.cs. A generic contract contains only data definitions — no code.
Update the service state (GenericBrickState) with all the required properties. You might prefer to divide this out from the GenericBrickTypes.cs file into a file called GenericBrickState.cs. Define additional classes, if necessary, and mark them with the [DataContract] attribute.
Add the Request and Response data types for the operations.
Define the main operations port using the operations from step 5.
Compile!
You should leave the contract identifier alone. If you want to, you can delete the manifest that is automatically created because a generic contract cannot be run as a service, so the manifest is useless.
The final version of the Generic Brick service is supplied in ProMRDS\Chapter17\GenericBrick. It was used to create the Integrator and Hemisson services that are also included in this chapter.
Terminology
Before moving on to discuss the Generic Brick contract, it is important to explain some of the terminology used in this chapter:
Brick: A brick is the brains of a robot and typically contains a microcontroller chip. It may not physically look like a brick at all, and it certainly won’t be the size, shape, or weight of a real brick! However, all communication between MRDS services and the robot passes through the brick. The following are examples of bricks:
The brick for the LEGO NXT is something that you can easily point to, and it even looks like a brick. It has I/O ports for motors and sensors, but you cannot open it up and reconfigure the hardware inside. (In fact, it contains two CPUs, but this chapter doesn’t discuss the NXT because it is already well supported.)
In some cases, such as the Boe-Bot, the brick consists of a small circuit board (the BASIC Stamp 2) and a larger board that includes a breadboard area for building interface circuits. There is also a plug-in Bluetooth module for communication. The entire assembly is considered to be the brick, including all of the I/O devices.
The brick in the Stinger robot is a circuit board called a Serializer. It contains
a microcontroller and the necessary hardware to interface with sensors and actuators.
The Hemisson and Scribbler robots are “sealed” units, so the brick is internal to the robots.
Device: Bricks can have a wide range of different hardware attached to them. These pieces of hardware are all referred to as devices. For example, a speaker that can play a tone is a device. So is a chip that measures temperature and returns an analog value.
735
www.it-ebooks.info
Part IV: Robotics Hardware
Pin: A pin quite often refers to a physical pin on the microcontroller. However, there might be logical pins in the implementation that don’t exist physically or which set internal configuration parameters in the microcontroller. In the discussion in this chapter, a pin is generally a digital input or output. This is the lowest level of I/O that can be performed.
Sometimes the terms “pin” and “device” are used interchangeably.
Port: Microcontrollers usually have one or more I/O ports. (These have nothing to do with MRDS ports.) The naming scheme varies from one manufacturer to another, and the number of ports varies from one chip to another. For example, there might be three ports called PortA, PortB, and PortC. The size of each port can also vary from one chip to the next; basically, it is limited by the packaging of the chip. Some ports are 8 bits, and others are 16 or even 32 bits. Certain pins may have dedicated functions so that the effective number of pins in a port is less than 8.
Hardware Identifier: To avoid all of this confusion over port/pin naming, the brick implementation must assign a unique hardware identifier to each input and output. The simplest approach is probably to number all the pins sequentially. For example, the BASIC Stamp 2 has 16 pins numbered 0 to 15. The PICAXE, however, has input and output pins, both of which use the same numbering scheme, so you must make up some different numbers.
Design
There is no doubt that this design is a compromise. A lot of design decisions had to be made to keep the Brick service small and yet flexible. These are discussed below to explain the rationale behind the decisions. You might not agree with them all, in which case you can modify the service and create your own version — that is the advantage of an “open” system. Just remember to change the contract identifier to make your contract unique.
The following design assumptions were made:
One of the authors’ ultimate objectives is to write an enhanced version of the Dashboard that can make intelligent decisions about laying out the user interface based on the types of devices connected to the brick. For example, all the digital inputs could be grouped together as an array of checkboxes in numeric or alphabetical order. This requires a clearly defined approach to naming I/O devices and specifying their functions.
It is assumed that the robot uses a two-wheel differential drive. This is by far the most common configuration for cheap educational robots.
It is implicitly assumed that communications will be via a serial port. Whether it uses a wireless connection (Bluetooth, ZigBee, etc.) is immaterial. However, there is no requirement in the design to use a serial port, and it could easily be extended to use wireless Ethernet, for example.
The authors would like to acknowledge that some of the ideas for this design came from Ben Axelrod and others who have contributed to the MRDS Discussion Forums. The following sections address some of the design considerations.
736
www.it-ebooks.info
Chapter 17: Writing New Hardware Services
No Reconfiguration of the Brick (in this First Version)
Hobby robots are either built from a kit or constructed by the hobbyist using whatever components are at hand. Assembly instructions for kits have to be fairly prescriptive, so the resulting robot has a fixed set of sensors and actuators at known hardware “addresses” (pin numbers). For a home-brewed robot, there are no rules, and the exact combination of hardware components might never be duplicated by anyone else. Although it still makes sense to implement the basic MRDS services so that you can take advantage of the cool services available under MRDS, it is highly unlikely that anyone other than the original robot builder will ever need to modify these basic services.
The obvious exception to this rule is the LEGO NXT, which has ports into which you plug sensors. However, the NXT is very well supported by the MRDS team, so there is no need to develop a Brick service for it. Therefore, reconfiguration is not possible from a partner service, and only very limited reconfiguration can be done via the service’s config file. All the necessary configuration is done by the Brick service itself during initialization.
All Devices Must Have a Name
Although services will invariably use hardware identifiers to manipulate devices, a user interface needs to present information using meaningful names. To avoid situations where different people refer to the same device using different names, or not having a name at all, the implementation must specify names for all devices.
You should try to be consistent in naming devices. For example, you should not have “Left IR Sensor” and “IR Sensor - Rear”. Note that these names include a location or position, a device type, and, implicitly, a direction (Sensor = Input).
No guidelines are given here for naming because that is likely to start a heated debate.
Hardware Identifiers
Hardware identifiers are left to the implementation, and partner services should not assume anything except that they are unique (within a single brick implementation, but not across different bricks) and that they are immutable.
There should be no overlap in identifiers between the different types of devices — you cannot have a digital input with id = 3 and an analog output that also has id = 3.
Timestamps
All I/O values should have timestamps indicating the last time that they were updated. (A value that has never been updated will have the base date.) There are no particular requirements on the values of the date/time except that it must be possible to tell whether a value has been updated since the last time it was retrieved, and the chronological order of updates must be preserved. Several devices can have the same timestamp if the data values all arrived in a single packet.
Automatic Connection
Given that you use most robots by turning them on and then starting a service, it makes sense for the Brick service to automatically try to connect. This means that the configuration information must be either compiled in or, preferably, in the config file.
737
www.it-ebooks.info
Part IV: Robotics Hardware
Explicit Connect and Disconnect operations are not included in the contract, but if you want to add these you can extend the contract.
Inputs and Outputs
All devices attached to the brick are classified as one of the following types:
Digital Input (Boolean)
Digital Output (Boolean)
Analog Input (double)
Analog Output (double)
There might be some argument for using an integer value (0 or 1) for digital devices, but the decision was made to use a Boolean because this seemed to be more common in the MRDS sample implementations.
Analog devices use doubles, so they can have an enormous range. The minimum and maximum values for each device are specified in the device information.
Unit Conversion and Calibration
Analog sensors and output devices must translate from binary values to real-world units at some stage. It is highly desirable that any conversions are performed inside the Brick service. These are what are referred to as normalized measurements in the MRDS generic Analog Sensor service.
For this reason, the “raw” values of analog sensors are not included in the state as separate properties. This enables the data to be used by young people without the complication of extra math. If you want to expose the raw values of sensors — for example, because you want users to do the mathematical calculations — you can either define pseudo-sensors with different hardware identifiers to hold the raw values or perform no processing of the incoming data.
Wherever applicable, appropriate metric units should be used. For example, when working with small robots, distance measurements should be in centimeters. The exception to this rule is angles that should be in degrees, rather than radians. The objective is to make all values easily understandable.
Calibration of sensors is completely ignored in this specification. If the sensors themselves are not well behaved, then you might want to just return raw values. Conversely, if you can linearize or “condition” the values, then the brick should do so.
Be careful about hiding too much information. For example, an infrared sensor might simply be used as a bump sensor, i.e., a digital input. However, you can expose it as two devices: one that provides an analog value and one that provides a digital value. If the threshold is set correctly in the brick code, the digital input can be used and the analog input can be ignored. If the sensor is unreliable, however, a partner service can still read the analog value and set its own threshold.
One situation in which the raw values might be irrelevant is line following. Manufacturers often adjust the hardware design so that the output from the line-following sensors is very reproducible and there is no need to look at the analog values because the sensors clearly see a line or no line. In fact, the
738
www.it-ebooks.info
Chapter 17: Writing New Hardware Services
simulated photo sensors in Chapter 12 use webcams to detect light or dark areas on the floor. There is no reason to expose the webcam images as sensor data — only the binary value is required.
Locations of Sensors and Actuators
The Microsoft samples commonly use a Pose to represent the location of a sensor. Although this is essential for positioning objects in the simulator, it is not intuitively obvious what a Pose means. In particular, the orientation is expressed as a quaternion. Very few people can look at a quaternion and tell you which way it is pointing.
Moreover, for simple robots there are only a handful of positions on the robot where sensors are likely to be located. Therefore, an enumeration is used for the location of sensors and actuators based on the 16 principal compass points, with north corresponding to straight ahead in front of the robot. Whether this approach is successful or not is still an open question — perhaps it will revert to a Pose in a future version of the Generic Brick contract.
Access to Switches and LEDs is Included as a Convenience
The service should internally remap the digital input devices (switches) and output devices (LEDs) to the correct pins (hardware identifiers) and present the information in the form of a binary bitmask. The remapping is an implementation detail that external services should not need to worry about.
Note that setting a single LED this way requires the partner service to remember the current state of all of the LEDs, or to request it beforehand. Even LEDs are not generic. The Roomba has LEDs that can be red, green, or yellow! They will not fit into a binary bitmask unless two bits are used for each LED.
Motors and Wheel Encoders
The drive system of the robot is assumed to be a two-wheeled differential drive. It makes sense if you are trying to make the code as compact as possible to have operations on the Brick service to control the motors. Ultimately, all communication is channeled via the brick, so it should know the state of the motors anyway.
There is no requirement in the generic Differential Drive contract to actually have motor services. You do have to define some motor objects in the state, but nothing else depends on there being actual motor services. In fact, you could extend the Generic Brick contract to implement the generic Differential Drive contract and eliminate a separate service altogether, but this is not done here.
You can reduce overhead by not implementing a motor service at all. This is how the StingerPWMDrive in Chapter 16 works. It sends commands directly to the Serializer, which is effectively the brick for the Stinger robot.
Given that the target devices are at the low end of the hardware spectrum, wheel encoders are ignored in this definition. You can, of course, add wheel encoders if you want to, but there are no operations defined on the brick to explicitly read or set them. (You can just treat them as analog sensors.) For example, the DriveDistance and RotateDegrees operations are expected to use timers, not encoders (and consequently they are not very accurate).
739
www.it-ebooks.info
Part IV: Robotics Hardware
I2C Bus is Not Supported
Many microcontrollers have the capability to talk to other devices over an I2C bus. This is explicitly ignored to simplify the design. Otherwise, it raises the issue of how to specify the I2C addresses and what types of devices might be attached.
The implementation is free to assign a hardware identifier to an I2C device, enabling it to be accessed via the normal I/O operations. However, devices that return multiple bytes of data, especially cameras, cannot be accommodated by this mechanism. There is nothing to stop you from extending the brick contract to add more state information and operations if you need to support I2C devices.
Brick State
The brick state includes the following generic items:
Firmware information
Configuration information
Communications statistics
Sensors
Actuators
LEDs
Switches
DrivePower
Definition of the State
The code for the state is in GenericBrickState.cs and is set out in the following example. Note that there is a [DataContract] attribute on the state, which causes it to be copied into the Proxy DLL. In addition, all of the public properties have the [DataMember] attribute. XML comments and a Description attribute are used to provide documentation:
///<summary>
///The GenericBrick State
///</summary> [DataContract]
public partial class GenericBrickState
{
private string _name; private bool _connected;
///<summary>
///Brick Name
///</summary>
///<remarks>This is set by the service and not read from the
Brick</remarks>
[DataMember, Description(“Friendly name for the Brick”)] public string Name
740
www.it-ebooks.info
Chapter 17: Writing New Hardware Services
{
get { return _name; } set { _name = value; }
}
///<summary>
///Firmware Details (optional)
///</summary>
///<remarks>This information is read from the Brick</remarks> [DataMember, Description(“Firmware information”)] [Browsable(false)]
public Firmware Firmware;
///<summary>
///Communications Configuration
///</summary>
///<remarks>This information should be read from the config file</remarks> [DataMember, Description(“Communications Configuration”)]
public CommsConfig Configuration;
///<summary>
///Connection Flag
///</summary>
[DataMember, Description(“Indicates if a connection to the Brick is
established”)]
[Browsable(false)] public bool Connected
{
get { return _connected; } set { _connected = value; }
}
///<summary>
///Communications Statistics (optional)
///</summary>
[DataMember, Description(“Holds the statistics for the communications
link (optional)”)]
[Browsable(false)]
public CommsStats CommsStats;
///<summary>
///Summary of the Capabilities of the Brick
///</summary>
///<remarks>This should NOT be determined from the config file</remarks> [DataMember, Description(“Brick Capabilities Bitmask”)]
public BrickCapabilities Capabilities;
///<summary>
///Brick Sensors (Inputs)
///</summary>
[DataMember, Description(“All Inputs (Digital and Analog)”)] [Browsable(false)]
public List<Device> Sensors;
(continued)
741
www.it-ebooks.info
Part IV: Robotics Hardware
(continued)
///<summary>
///Brick Actuators (Outputs)
///</summary>
[DataMember, Description(“All Outputs (Digital and Analog)”)] [Browsable(false)]
public List<Device> Actuators;
///<summary>
///Status of LEDs (optional -- can use Actuators instead)
///</summary>
///<remarks>NOTE: LEDs are also listed as Actuators</remarks> [DataMember, Description(“LED settings as a binary Bitmask”)] [Browsable(false)]
public int LEDs;
///<summary>
///Status of Switches (optional -- can use Sensors instead)
///</summary>
///<remarks>NOTE: Switches are also listed as Sensors</remarks> [DataMember, Description(“Switch Status as a binary Bitmask”)] [Browsable(false)]
public int Switches;
///<summary>
///Drive Power (optional)
///</summary>
[DataMember, Description(“Specifies the current Drive Power settings”)] [Browsable(false)]
public DrivePower DrivePower;
}
Do not add static members to your state (or any other class in a generic contract). DssProxy will exit with error code 20 when you try to compile. In addition, all members that you want exposed (and serialized to the config file) must have a [DataMember] attribute, as well as being marked public.
Overview of State Members
As stated in the design, every device has a “friendly” Name, and the purpose of the Connected field is obvious. Several of the members are instances of other classes defined in GenericBrickState.cs. Rather than list all of the code here, these additional classes are discussed briefly:
The information in Firmware is optional. Some robots have version information in their firmware, whereas others do not. If nothing else, requesting the firmware version is a good test to see whether the robot is alive, and it gives the service something to display to the user.
The Configuration field contains information that is basically about communications. It includes the serial port and timing parameters. To set up a robot, you need to run its service once so that it writes out an “empty” config file (saved state). Then you can edit the config and insert the appropriate serial port. It does no harm to run a service and let it fall over. In fact, you can still examine the state using a web browser even when a connection to the robot cannot be established.
742
